From 52e0b6726aff878263d66e8f9545480c848c4e5d Mon Sep 17 00:00:00 2001 From: ajakupov-microsoft Date: Fri, 21 Aug 2026 18:56:39 +0200 Subject: [PATCH 1/2] feat: first working version, untested --- 03-Azure/01-04-AI/05_AI_Governance/README.md | 165 +++ .../challenges/challenge-01.md | 78 ++ .../challenges/challenge-02.md | 74 + .../challenges/challenge-03.md | 80 ++ .../challenges/challenge-04.md | 74 + .../challenges/challenge-05.md | 72 + .../challenges/challenge-06.md | 84 ++ .../challenges/challenge-07.md | 89 ++ .../challenges/challenge-08.md | 92 ++ .../challenges/challenge-09.md | 113 ++ .../05_AI_Governance/labautomation/README.md | 180 +++ .../labautomation/deploy-lab.ps1 | 604 ++++++++ .../docs/phase4-evidence-tables.md | 305 ++++ .../labautomation/infra/resources.bicep | 1245 +++++++++++++++++ .../labautomation/lab-defaults.json | 8 + .../labautomation/run-local.ps1 | 58 + .../labautomation/setup-notebook-env.ps1 | 162 +++ ...entic-governance-migration-instructions.md | 374 +++++ 18 files changed, 3857 insertions(+) create mode 100644 03-Azure/01-04-AI/05_AI_Governance/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/deploy-lab.ps1 create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/lab-defaults.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/run-local.ps1 create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 create mode 100644 03-Azure/01-04-AI/agentic-governance-migration-instructions.md diff --git a/03-Azure/01-04-AI/05_AI_Governance/README.md b/03-Azure/01-04-AI/05_AI_Governance/README.md new file mode 100644 index 000000000..fef789a11 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/README.md @@ -0,0 +1,165 @@ +# Agentic Governance Hub + +Welcome to the **Agentic Governance Hub** MicroHack. In this hands-on lab you'll deploy a +**Citadel Agentic Governance Hub** — a centralized Azure API Management gateway that +governs every LLM/agent call in your organization — and drive it through nine guided, +notebook-based exercises covering backend onboarding, access contracts, PII handling, +multi-framework agents, hosted agents, and agent-to-agent / MCP publishing. + +- [**Introduction**](#introduction) +- [**Learning objectives**](#learning-objectives) +- [**Scenario**](#scenario) +- [**Requirements**](#requirements) +- [**Challenges**](#challenges) +- [**Repository layout**](#repository-layout) +- [**Contributors**](#contributors) + +## Introduction + +This MicroHack teaches you how to put **governance in front of AI**: a single Azure API +Management gateway (the **Citadel Governance Hub**) that every model call and every agent +routes through, so an organization gets consistent RBAC, rate-limiting/capacity control, +PII handling, and audit trails — no matter which model, which agent framework, or which +protocol (chat completions, Responses API, A2A, MCP) a team chooses to build with. + +Your lab automation ([`labautomation/`](labautomation/)) provisions a dedicated **Hub** +(APIM + a Foundry account for governance) and **Spoke** (a sample Foundry workload) per +attendee. The nine challenges then walk you through the **unchanged Citadel workshop +notebooks** — each one a self-contained, already-documented exercise — in the order the +governance model builds up: onboard a backend, expose it universally, add access +contracts (RBAC + capacity), drive those contracts from real agent frameworks, add PII +processing, unify multiple providers behind one API, then graduate to hosted agents +governed end-to-end, agent-to-agent publishing, and MCP tool publishing. + +## Learning objectives + +By completing this MicroHack you will be able to: + +- Onboard a new LLM backend into a governance gateway and prove it through multiple API + shapes (Azure OpenAI, Universal LLM API, streaming). +- Provision **Access Contracts** that enforce model-level RBAC and capacity limits, and + understand how token-bucket throttling behaves under load. +- Drive the same governed access contract from three different agent frameworks + (Microsoft Agent Framework, Foundry Agent SDK, LangChain). +- Configure PII anonymization/deanonymization and PII blocking at the gateway, with usage + analytics. +- Compare model-provider access patterns (Azure OpenAI, Foundry inference, Responses API, + Gemini-compatible) behind one **Unified AI API**. +- Build and deploy a **Foundry Hosted Agent** governed end-to-end by the **Agent + Governance Toolkit (AGT)**, and read its audit trail in Application Insights. +- Publish a Foundry Agent as a governed **Agent-to-Agent (A2A)** endpoint through APIM. +- Understand how an **MCP server** is published and consumed through APIM, including + where this lab's automation stops short (Entra app-registration/app-role setup) and + what a facilitator needs to add to unlock that exercise. + +## Scenario + +You are onboarding your organization onto a **Citadel Agentic Governance Hub**: a single +Azure API Management gateway that every model call and every agent must pass through, so +platform, security, and compliance teams get one place to see and control AI usage. + +Your lab automation deploys: + +- A **Hub** resource group containing APIM, a governance Foundry account/project, Log + Analytics, Application Insights, Cosmos DB (usage tracking), Event Hub, and Key Vault. +- A **Spoke** resource group (folded into the same RG) containing a sample Foundry + account/project and Azure Container Registry — where you build and deploy the agents + the later challenges call for. + +Each challenge exercises one governance capability against these resources, using the +Citadel workshop's own Jupyter notebooks — unmodified, and already fully documented with +their own numbered sections, so the challenge files here exist to orient you and point +you at the right notebook, not to restate what's already in it. + +## Requirements + +To complete this MicroHack you'll need: + +- Your lab's attendee credentials (`HackboxCredential` values) from the MicroHack + dashboard — resource group, subscription, and the Spoke Foundry/Key Vault/ACR names. +- `azd` installed, so [`setup-notebook-env.ps1`](labautomation/README.md#notebook-environment-setup) + can bridge those credentials into a local `azd` environment the (unchanged) workshop + notebooks read via `azd env get-value`. +- The Azure CLI (`az`), logged in (`az login`) against your lab's subscription/tenant. +- The workshop's Python environment set up (`uv sync` or `pip install -r requirements.txt` + from the `reference/ai-hub-gateway-solution-accelerator/workshop/` folder). +- Comfort running and reading Jupyter notebooks in VS Code. + +> [!TIP] +> **You're ready to start when** you've run `setup-notebook-env.ps1` once with your +> dashboard's credentials (see [Challenge 1, Part A](challenges/challenge-01.md)) and can +> open the first workshop notebook without any `azd env get-value` errors. + +## Challenges + +This MicroHack is divided into nine challenges. Challenges 1–6 build up the core +governance model (backends → universal API → access contracts → agent frameworks → PII → +unified API); challenges 7–8 graduate to hosted, governed agents and agent-to-agent +publishing; challenge 9 covers MCP publishing and is **optional/instructor-led**, since it +needs infrastructure this lab's automation doesn't provision on its own. + +### Challenge structure + +Each challenge follows the same anatomy, so you always know where to look: + +| Section | Description | +|---------|-------------| +| 🎯 **Objective** | What you'll achieve and why it matters | +| 🧭 **Context** | The Hub/Spoke resources and prerequisites this exercise relies on | +| ✅ **Tasks** | Confirm your notebook bridge, then run the notebook's own sections in order | +| 🏁 **Success criteria** | A checklist to confirm you're done | +| 🛠️ **Troubleshooting** | Common problems and their fixes | +| 🚀 **Go further** | Optional stretch goals if you finish early | +| 📚 **Learning resources** | Docs to go deeper | + +### Challenge list + +- **Challenge 1**: **[Onboard a New LLM Backend](challenges/challenge-01.md)** *(35 min)* — onboard a backend into the hub and prove it through multiple API shapes. +- **Challenge 2**: **[Universal LLM API Across Every Model](challenges/challenge-02.md)** *(25 min)* — validate `/models` against every model with no RBAC restriction. +- **Challenge 3**: **[Access Contracts: Model RBAC & Capacity](challenges/challenge-03.md)** *(35 min)* — three access contracts enforcing `allowedModels` and capacity limits. +- **Challenge 4**: **[Drive Access Contracts from Agent Frameworks](challenges/challenge-04.md)** *(30 min)* — the same contracts, driven from three different agent frameworks. +- **Challenge 5**: **[PII Anonymization, Blocking & Analytics](challenges/challenge-05.md)** *(35 min)* — mask, block, and analyze PII at the gateway. +- **Challenge 6**: **[Unified AI API Across Providers](challenges/challenge-06.md)** *(35 min)* — one API surface across Azure OpenAI, Foundry inference, Responses API, and Gemini-compatible patterns. +- **Challenge 7**: **[Build a Governed Hosted Agent with AGT](challenges/challenge-07.md)** *(40 min)* — deploy a Foundry Hosted Agent governed end-to-end by the Agent Governance Toolkit. +- **Challenge 8**: **[Publish a Foundry Agent as an A2A Endpoint](challenges/challenge-08.md)** *(35 min)* — publish and call an agent-to-agent endpoint through APIM. +- **Challenge 9** *(optional / instructor-led)*: **[HR MCP via APIM](challenges/challenge-09.md)** *(variable)* — publish and consume an MCP server through APIM; requires facilitator-provisioned infrastructure beyond this lab's automation. + +> [!TIP] +> It's tempting to race through, but pause after each challenge and read the notebook's +> own `📊 Results Summary` cell — that's where the "why this matters for governance" +> payoff actually lands. + +## Repository layout + +``` +05_AI_Governance/ + README.md This file + challenges/ Challenge instructions (challenge-01.md … challenge-09.md) + labautomation/ Platform entry point — provisions the Hub + Spoke per attendee + deploy-lab.ps1 Called once per attendee by the MicroHack platform + run-local.ps1 Local-only dry-run wrapper (not used by the platform) + setup-notebook-env.ps1 Bridges dashboard credentials into a local azd environment + README.md Full provisioning + notebook-environment-setup reference + reference/ + ai-hub-gateway-solution-accelerator/ + workshop/ The 9 unchanged Citadel workshop notebooks this MicroHack is built on +``` + +## Contributors + +| Name | Role | +|------|------| +| Alibek Jakupov | Author & maintainer | + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA). For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com). + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +## Trademarks + +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft +trademarks or logos is subject to and must follow +[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md new file mode 100644 index 000000000..bc821c48c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md @@ -0,0 +1,78 @@ +# Challenge 1 — Onboard a New LLM Backend + +**[Home](../README.md)** - [Next Challenge →](challenge-02.md) + +## 🎯 Objective + +Use the **🏰 Citadel Backend Contracts** notebook to onboard a new AI backend and its +routing logic into the Citadel Governance Hub: configure an LLM backend endpoint and +authentication, generate the Bicep parameter file, deploy the backend pool and policy +fragments, then prove it works through multiple API formats. + +## 🧭 Context + +This challenge runs entirely against **Hub** resources — the APIM instance and its +backend pools sit in front of every model your governance hub serves. Before you start, +confirm your attendee credentials have been bridged into a local `azd` environment (see +Part A below); every workshop notebook reads its configuration via `azd env get-value`. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (5 min) + +1. Check with your facilitator (or your own setup) that + [`setup-notebook-env.ps1`](../labautomation/README.md#notebook-environment-setup) + has already been run with your dashboard's `HackboxCredential` values. +2. If it hasn't, run it now — the workshop notebooks are **unchanged** and call + `azd env get-value` for every setting, so this step is required once before any + notebook in this MicroHack. + +### Part B — Run the LLM backend onboarding notebook (30 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/1. llm-backend-onboarding-runner.ipynb` + in VS Code / Jupyter. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - `3️⃣ Extract Current APIM Backend-Pools Configuration` + - `4️⃣ Discover Managed Identity for APIM Authentication` + - `5️⃣ Generate LLM Backend Parameter File` + - `6️⃣ Deploy LLM Backend Onboarding Bicep` + - `7️⃣ Verify Deployed Configuration` + - `8️⃣ Verify Get Available Models Policy Fragment` +3. Run the test sections: `🧪 Test GET /deployments (Microsoft Foundry Integration)` and + `🧪 Test Deployed Models` (Universal LLM API, Azure OpenAI API, Azure OpenAI Python + SDK, streaming). +4. Read the notebook's own `📊 Results Summary` and `Next Steps` cells at the end. + +## 🏁 Success criteria + +- [ ] The notebook generated a `.bicepparam` file describing your backend, auth type, + and per-model metadata. +- [ ] The LLM backend onboarding Bicep deployment succeeded and the backend pool + + `get-available-models` policy fragment are visible in APIM. +- [ ] `GET /deployments` (Foundry integration) returns your onboarded model. +- [ ] The deployed model responds successfully via the Universal LLM API, the Azure + OpenAI API, the Azure OpenAI Python SDK, and a streaming request. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors / empty values | `setup-notebook-env.ps1` hasn't been run for your attendee credentials yet — see Part A. | +| Backend deployment succeeds but test calls 401/403 | APIM RBAC / managed-identity propagation can take a minute after a fresh deployment — wait and retry. | +| A model call fails with a region/quota error | Model availability is region-specific; double-check the model/SKU you configured is available in your lab's region. | + +## 🚀 Go further + +- Add a second backend (a different `backend_type` such as `aws-bedrock` or + `gemini-openai`) to the same `llm_backends_config` and re-run sections `5️⃣`–`8️⃣`. +- Configure a `model_alias` that routes a friendly client-facing name to more than one + underlying model. + +## 📚 Learning resources + +- [Azure API Management policy reference](https://learn.microsoft.com/azure/api-management/api-management-policies) +- [What is Microsoft Foundry?](https://learn.microsoft.com/azure/ai-foundry/what-is-ai-foundry) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md new file mode 100644 index 000000000..a03546f88 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md @@ -0,0 +1,74 @@ +# Challenge 2 — Universal LLM API Across Every Model + +**[Home](../README.md)** - [← Previous Challenge](challenge-01.md) - [Next Challenge →](challenge-03.md) + +## 🎯 Objective + +Validate the **Universal LLM API** (`/models`) against **every model** exposed by the +gateway: provision an access contract with `allowedModels = ""` (no model restriction), +dynamically discover the live model catalogue via `GET /models/models`, and exercise the +right operation for each model (chat completions, embeddings, or the Responses API trio). + +## 🧭 Context + +This challenge deploys its own **Access Contract** (an APIM product + subscription) via +Bicep — a dynamically generated product policy with no model RBAC restriction and a +generous capacity allocation. Make sure your `setup-notebook-env.ps1` bridge is already +in place (see [Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min)) +before you start. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack: confirm +[`setup-notebook-env.ps1`](../labautomation/README.md#notebook-environment-setup) has +been run for your attendee credentials. + +### Part B — Run the Universal LLM API validation notebook (25 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb`. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - `3️⃣ Provision Access Contract (allowedModels = "")` + - `4️⃣ Retrieve API Key from Access Contract` + - `5️⃣ Discover All Models via Universal LLM API` + - `6️⃣ Classify Models and Plan Operations` + - `7️⃣ Execute Per-Model Operations` + - `8️⃣ Confirm allowedModels = "" Behaviour` +3. Read the `📊 Results Summary` cell, then run `🧹 Cleanup (Optional)` if you want to + remove the test access contract afterward. + +## 🏁 Success criteria + +- [ ] An access contract (APIM product + subscription) with `allowedModels = ""` deployed + successfully via Bicep. +- [ ] `GET /models/models` returned the live catalogue of models the gateway serves. +- [ ] Every discovered model was classified correctly (embedding vs. `gpt`-style vs. + other) and exercised the matching operation — `POST /models/embeddings`, + `POST /models/chat/completions`, or the Responses API trio + (`/models/responses`, `GET /models/responses/{id}`, + `GET /models/responses/{id}/input_items`). +- [ ] The final per-model results table shows no unexpected failures, confirming + `allowedModels = ""` does not block any model. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| A model in the catalogue fails every operation | Check whether that backend was onboarded in [Challenge 1](challenge-01.md) or is otherwise disabled in APIM. | +| Responses API calls fail for a `gpt` model | Some models don't implement the Responses API trio — the notebook's classification is a heuristic based on the model name, not a guarantee. | + +## 🚀 Go further + +- Re-run section `3️⃣` with a restricted `allowedModels` list instead of `""` and observe + which models section `7️⃣` now skips or rejects. + +## 📚 Learning resources + +- [Azure OpenAI Responses API](https://learn.microsoft.com/azure/ai-foundry/openai/how-to/responses) +- [Azure API Management products and subscriptions](https://learn.microsoft.com/azure/api-management/api-management-howto-add-products) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md new file mode 100644 index 000000000..a106913d0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md @@ -0,0 +1,80 @@ +# Challenge 3 — Access Contracts: Model RBAC & Capacity + +**[Home](../README.md)** - [← Previous Challenge](challenge-02.md) - [Next Challenge →](challenge-04.md) + +## 🎯 Objective + +Provision and validate **three Access Contracts** with different configurations and +targets, each backed by a dynamically generated APIM product policy enforcing +model-level RBAC (`allowedModels`) and capacity allocation (`llm-token-limit`), and +explore optional Key Vault and Microsoft Foundry connection integrations. + +## 🧭 Context + +| Access Contract | Configuration | Target runtime | +|---|---|---| +| Sales-Assistant | Key Vault integration only | Agents on AKS / ACA / App Service | +| HR-ChatAgent | Key Vault + Foundry connection integrations | Foundry Agents | +| Support-Bot | Direct output (no Key Vault, no Foundry) | Custom hosted agents | + +The Foundry connection name(s) this notebook prints are consumed by +[Challenge 4](challenge-04.md), so keep this notebook's output around before moving on. +As always, confirm +[`setup-notebook-env.ps1`](../labautomation/README.md#notebook-environment-setup) has +already been run. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the access contracts notebook (35 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/3. citadel-access-contracts-tests.ipynb`. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - `3️⃣ Define Access Contract Configurations` + - `4️⃣ Create Access Contract Parameter Files` + - `4️⃣.1 Deploy Access Contracts Using 🦾 Bicep` + - `4️⃣.2 Foundry Connection Names (for downstream notebooks)` + - `5️⃣ Retrieve API Keys for Each Access Contract` + - `6️⃣ Test API Requests Across All Access Contracts` + - `7️⃣ Run Load Test Across All Access Contracts` + - `8️⃣ Visualize Results Across All Access Contracts` + - `9️⃣ Compare Token Bucket Behavior` +3. Read the `📊 Results Summary` cell. **Do not run the `🧹 Cleanup (Optional)` cell + yet** if you plan to continue to Challenge 4 — it deletes the access contracts. + +## 🏁 Success criteria + +- [ ] All three access contracts (APIM products + subscriptions) deployed successfully + via Bicep, each with its own dynamically generated product policy. +- [ ] Requests against each contract respect its `allowedModels` RBAC list. +- [ ] The load test shows throttling behaviour consistent with each contract's + `llm-token-limit` capacity allocation. +- [ ] The Foundry connection name(s) for HR-ChatAgent were printed and noted for + Challenge 4. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| Key Vault integration fails to resolve a secret | Confirm the spoke Key Vault name resolved correctly and your identity has data-plane access (RBAC propagation can take a minute). | +| Foundry connection step fails | Confirm the spoke Foundry account/project names resolved from `azd env get-value` — re-check `setup-notebook-env.ps1` was run with the correct `SpokeAiFoundryAccountName` / `SpokeAiFoundryProjectName` values. | +| Load test results look flat (no 429s) | Capacity limits are per-contract; make sure you're hitting the right subscription key for the contract you're testing. | + +## 🚀 Go further + +- Add a fourth access contract configuration with a tighter `allowedModels` list and + compare its token-bucket behaviour against the three built-in contracts. + +## 📚 Learning resources + +- [Rate limit and quota policies in Azure API Management](https://learn.microsoft.com/azure/api-management/rate-limit-policy) +- [Connections in Microsoft Foundry projects](https://learn.microsoft.com/azure/ai-foundry/how-to/connections-add) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md new file mode 100644 index 000000000..2da611cf8 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md @@ -0,0 +1,74 @@ +# Challenge 4 — Drive Access Contracts from Agent Frameworks + +**[Home](../README.md)** - [← Previous Challenge](challenge-03.md) - [Next Challenge →](challenge-05.md) + +## 🎯 Objective + +Drive the three access contracts from [Challenge 3](challenge-03.md) through three +different agent frameworks — Microsoft Agent Framework, Microsoft Foundry Agent SDK, and +LangChain — each simulating a multi-turn conversation, then compare token consumption +across frameworks. + +## 🧭 Context + +| Access Contract | Agent Framework | Integration | Target model | +|---|---|---|---| +| Sales-Assistant | Microsoft Agent Framework | Azure Key Vault (endpoint + key) | gpt-4.1 | +| HR-ChatAgent | Microsoft Foundry Agent SDK | Foundry Project Connection | gpt-4.1 | +| Support-Bot | LangChain | Local (direct endpoint + key) | phi-4 | + +**This notebook requires [Challenge 3](challenge-03.md) to have been completed first** — +it reuses the access contracts that notebook created and does not deploy any APIM +resources itself. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the agent frameworks notebook (30 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/4. citadel-agent-frameworks-tests.ipynb`. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - `3️⃣ Retrieve Access Contract API Keys` + - `4️⃣ Install Required Agent Frameworks` + - `5️⃣ Microsoft Agent Framework (Sales-Assistant)` + - `6️⃣ Microsoft Foundry Agent SDK (HR-ChatAgent)` + - `7️⃣ LangChain Agent (Support-Bot)` + - `8️⃣ Display Agent Statistics and Comparison` + - `9️⃣ Token Efficiency Analysis` +3. Read the `📊 Results Summary` cell, which compares integration patterns across the + three frameworks. + +## 🏁 Success criteria + +- [ ] All three agent frameworks are installed and each successfully authenticates + against its access contract's API key. +- [ ] Each agent completes a multi-turn conversation relevant to its business domain + (sales pricing, HR policies, technical support). +- [ ] The agent-statistics comparison and token-efficiency analysis produce a table + comparing all three frameworks. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| API key retrieval fails | This notebook does not create access contracts — run [Challenge 3](challenge-03.md) first. | +| Foundry SDK connection step fails | Confirm the Foundry connection name printed at the end of Challenge 3 (`4️⃣.2`) matches what this notebook resolves. | +| Package install errors in step `4️⃣` | Some agent framework packages (`agent-framework`, `langchain`, `langchain-openai`) pin specific versions — re-run the cell after resolving any dependency conflict shown in the error. | + +## 🚀 Go further + +- Swap the LangChain Support-Bot's target model and compare its token efficiency against + the other two frameworks. + +## 📚 Learning resources + +- [Microsoft Agent Framework overview](https://learn.microsoft.com/agent-framework/overview/agent-framework-overview) +- [Microsoft Foundry Agent Service](https://learn.microsoft.com/azure/ai-foundry/agents/overview) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md new file mode 100644 index 000000000..18075a417 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md @@ -0,0 +1,72 @@ +# Challenge 5 — PII Anonymization, Blocking & Analytics + +**[Home](../README.md)** - [← Previous Challenge](challenge-04.md) - [Next Challenge →](challenge-06.md) + +## 🎯 Objective + +Verify PII processing in Citadel Access Contracts across three use cases: **PII +anonymization/deanonymization** with state saving, **PII blocking** with detection +reporting, and **PII processing performance analytics** — testing various PII types +(names, emails, phone numbers, credit cards, IBANs, etc.) and custom regex detection. + +## 🧭 Context + +This notebook assumes your Citadel Governance Hub already has PII processing enabled: +an Azure AI Language Service instance with PII detection, the `pii-anonymization`, +`pii-deanonymization`, and `pii-state-saving` APIM policy fragments deployed, and Event +Hub configured for PII state logging. These are part of the lab's hub deployment — you +don't need to provision anything extra beyond confirming your notebook environment. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the PII processing notebook (35 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/5. citadel-pii-processing-tests.ipynb`. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - **Use Case 1: PII Anonymization & Deanonymization** — `3️⃣.1`–`3️⃣.5` (define contract, + product policy, parameter file + deploy, retrieve API key, test with various PII + types) + - **Use Case 2: PII Blocking** — `4️⃣.1`–`4️⃣.5` (same shape, blocking instead of masking) + - **Use Case 3: PII Processing Performance Analytics** — `5️⃣.1`–`5️⃣.5` (Cosmos DB + connection, grant data-plane access, query recent records, quantitative report + + visualizations, deploy an analytics access contract, send the summary to an LLM) +3. Read the `📊 Results Summary` cell. + +## 🏁 Success criteria + +- [ ] The PII masking access contract anonymizes detected PII (names, emails, phone + numbers, credit cards, IBANs, custom regex patterns) and can deanonymize it back. +- [ ] The PII blocking access contract detects and blocks requests containing PII, + reporting what was detected. +- [ ] Your Cosmos DB query returns recent PII processing records, and the quantitative + report/visualizations render successfully. +- [ ] The PII analytics access contract successfully sends the usage summary to an LLM + through its own governed contract. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| Cosmos DB query returns nothing / `Forbidden` | Section `5️⃣.1.1` grants your identity Cosmos DB data-plane access — re-run it and wait a minute for RBAC to propagate. | +| PII detection doesn't fire on a sample | Confirm the Azure AI Language Service instance is configured and reachable; some PII types need the exact regex/entity category the notebook defines. | +| Deanonymization returns masked text unchanged | The masking/deanonymization state must be saved via Event Hub first — confirm section `3️⃣.5` ran before attempting deanonymization. | + +## 🚀 Go further + +- Add a custom regex pattern for a PII type specific to your organization and confirm it + is detected by both the masking and blocking contracts. + +## 📚 Learning resources + +- [Azure AI Language — PII detection](https://learn.microsoft.com/azure/ai-services/language-service/personally-identifiable-information/overview) +- [Azure API Management policy expressions](https://learn.microsoft.com/azure/api-management/api-management-policy-expressions) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md new file mode 100644 index 000000000..43d012934 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md @@ -0,0 +1,84 @@ +# Challenge 6 — Unified AI API Across Providers + +**[Home](../README.md)** - [← Previous Challenge](challenge-05.md) - [Next Challenge →](challenge-07.md) + +## 🎯 Objective + +Validate the **Unified AI Wildcard API** (`/unified-ai`) across different model +providers and API patterns: Azure OpenAI, Foundry inference, the Responses API, and +Gemini's OpenAI-compatible surface — plus authentication modes, load testing, debug +headers, blocked-path rejection, model access control, and streaming. + +## 🧭 Context + +This notebook deploys its own access contract via Bicep to obtain API credentials, then +runs 11 test sections against the Unified AI API. It assumes an existing Citadel +Governance Hub deployment with the Unified AI API enabled — that's already part of your +lab's hub deployment. Confirm your notebook environment bridge is in place before you +start. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the Unified AI API validation notebook (35 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/6. citadel-unified-ai-api-tests.ipynb`. +2. Run the setup sections: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI and Connected Subscription` + - `2️⃣ Initialize APIM Client Tool` + - `3️⃣ Provision Access Contract` + - `4️⃣ Retrieve API Key from Access Contract` + - `5️⃣ Discover Available Models & Deployment Discovery` +3. Run the 11 test sections, in order: + - `Test 1` — Azure OpenAI pattern + - `Test 2` — Inference pattern (Foundry models) + - `Test 3` — Responses API pattern (if supported) + - `Test 4` — Gemini OpenAI pattern (if configured) + - `Test 5` — API key authentication + - `Test 6` — Load test & throttling + - `Test 7` — UAIG debug response headers + - `Test 8` — Blocked path rejection + - `Test 9` — Model access control (`allowedModels`) + - `Test 10` — Invalid API key + - `Test 11` — Streaming request +4. Read the `Summary` table at the end (it lists the expected result for every test), + then run `🧹 Cleanup` if you want to remove the test access contract. + +## 🏁 Success criteria + +- [ ] An access contract deployed successfully and model/deployment discovery lists your + models, with a `404` for a non-existent deployment. +- [ ] `POST /unified-ai/openai/deployments/{model}/chat/completions` (Azure OpenAI + pattern) and `POST /unified-ai/models/chat/completions` (Foundry inference + pattern) both return `200`. +- [ ] A missing API key returns `401`; an invalid API key also returns `401`. +- [ ] A request to an unrecognized path returns `403 PathNotAllowed`, and a request for + a model outside `allowedModels` is rejected. +- [ ] The load test shows a mix of `200`/`429` responses, and every `UAIG-*` debug + header is present and populated on responses. +- [ ] A streaming request (`stream: true`) returns a `200` SSE response with + `UAIG-Is-Streaming: true`. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| Test 3 (Responses API) or Test 4 (Gemini) show as skipped | These are conditional on your onboarded backends supporting that pattern — not every lab environment configures a Gemini backend. | +| Load test (Test 6) shows only `200`s, no `429`s | Capacity limits are per-contract; the load may not be high enough to trip the token bucket — increase the burst size in that cell. | + +## 🚀 Go further + +- Compare the `UAIG-*` debug headers returned for the Azure OpenAI pattern vs. the + Foundry inference pattern and note what differs. + +## 📚 Learning resources + +- [Azure API Management — Unified AI Gateway design pattern](https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management---unified-ai-gateway-design-pattern/4495436) +- [APIM Unified AI Gateway sample](https://github.com/Azure-Samples/APIM-Unified-AI-Gateway-Sample) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md new file mode 100644 index 000000000..d44b6df2c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md @@ -0,0 +1,89 @@ +# Challenge 7 — Build a Governed Hosted Agent with AGT + +**[Home](../README.md)** - [← Previous Challenge](challenge-06.md) - [Next Challenge →](challenge-08.md) + +## 🎯 Objective + +Build a **Foundry Hosted Agent** for a Contoso HR scenario, governed end-to-end with the +**Microsoft Agent Governance Toolkit (AGT)**, deploy it to your spoke Foundry project, +and test it — including a policy that denies SSN disclosure and audits PTO/benefits +questions. + +## 🧭 Context + +| Component | Detail | +|-----------|--------| +| Agent framework | Microsoft Agent Framework (`agent-framework`) | +| Governance | AGT MAF adapter — policy, capability guard, audit trail, rogue detection | +| Protocol | Responses API (hosted on port 8088) | +| Model | `gpt-4.1` via the APIM BYO Gateway connection (`Hub-HR-ChatAgent-DEV-LLM/gpt-4.1`) created in [Challenge 3](challenge-03.md) | +| Tools | `get_pto_balance`, `get_holiday_schedule`, `get_benefits_summary`, `get_open_enrollment_window` | +| Policy | A bundled YAML policy (`policies/hr-policy.yaml`) — denies SSN disclosure and 3rd-party salary lookups, audits PTO/benefits queries | +| Telemetry | AGT OpenTelemetry → Application Insights (`agt.policy.*` spans, `agt.policy.evaluations`/`denials`/`latency_ms` metrics) | +| Container build | `az acr build` (cloud build — no local Docker required) | + +**This notebook requires [Challenge 3](challenge-03.md)** to have created the +`Hub-HR-ChatAgent-DEV-LLM` BYO Gateway connection. Your lab's spoke Foundry resources and +Azure Container Registry are already provisioned — check +[`setup-notebook-env.ps1`](../labautomation/README.md#notebook-environment-setup) has +run so the notebook can resolve them. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the hosted agent notebook (40 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/7. citadel-hosted-agent-with-agt.ipynb`. +2. Run the notebook's sections in order: + - `0️⃣ Initialize Notebook Variables` + - `1️⃣ Verify Azure CLI Login` + - `2️⃣ Generate Hosted Agent Source Files` + - `3️⃣ Build & Push Container Image` + - `4️⃣ Deploy Hosted Agent to Foundry` + - `5️⃣ Wait for Agent to Become Active` + - `5a. Assign RBAC to Agent Identity` + - `6️⃣ Invoke the Hosted Agent` + - `6a. Test AGT Governance Enforcement` + - `7️⃣ Multi-Turn Conversation` + - `8️⃣ Streaming Responses` + - `9️⃣ Cleanup` +3. Read `View AGT audit results in Application Insights` and, if you have access to the + spoke Application Insights resource, run the provided KQL query against `traces` to + see policy-violation audit events. + +## 🏁 Success criteria + +- [ ] The hosted agent container built and pushed via `az acr build`, and the agent + deployed to your spoke Foundry project. +- [ ] The agent's managed identity received the RBAC it needs (step `5a`) and the agent + responds to a basic invocation (step `6️⃣`). +- [ ] Step `6a` proves the AGT policy actually enforces governance — an SSN-disclosure or + 3rd-party salary request is denied rather than answered. +- [ ] A multi-turn conversation and a streaming response both work end-to-end. +- [ ] You can find `agt.audit.event` trace records in Application Insights for a denied + request. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| Connection `Hub-HR-ChatAgent-DEV-LLM` not found | Run [Challenge 3](challenge-03.md) first — it creates this Foundry connection. | +| Agent stays in a non-active state after step `5️⃣` | Container deployments can take a few minutes to come up; re-run the wait cell rather than assuming failure. | +| Step `6a` doesn't deny the disallowed request | Confirm `policies/hr-policy.yaml` was actually bundled into the container image built in step `3️⃣` — re-run `2️⃣`–`4️⃣` if the source files changed after the last build. | +| No `agt.audit.event` traces show up | AGT telemetry can lag by a minute or two before appearing in Application Insights; also confirm you're querying the **spoke** Application Insights resource, not the hub's. | + +## 🚀 Go further + +- Edit `policies/hr-policy.yaml` to add a new denial rule, rebuild the container, and + confirm the new rule is enforced in a fresh `6a` run. + +## 📚 Learning resources + +- [Microsoft Foundry Agent Service — hosted agents](https://learn.microsoft.com/azure/ai-foundry/agents/overview) +- [Azure Monitor — Kusto Query Language (KQL) quick reference](https://learn.microsoft.com/azure/data-explorer/kql-quick-reference) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md new file mode 100644 index 000000000..f311f5d6f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md @@ -0,0 +1,92 @@ +# Challenge 8 — Publish a Foundry Agent as an A2A Endpoint + +**[Home](../README.md)** - [← Previous Challenge](challenge-07.md) - [Next Challenge →](challenge-09.md) + +## 🎯 Objective + +Run an agent on Microsoft Foundry, publish it through the Citadel Governance Hub (APIM) +as a governed **Agent-to-Agent (A2A)** endpoint, and call it end-to-end using nothing but +an APIM subscription key — even while the Foundry account's public network access stays +disabled. + +## 🧭 Context + +This notebook: + +1. Resolves the agent-hosting Foundry account (the `-0` account) and project from `azd` — + no hardcoded names. +2. Temporarily enables public network access on that account so the notebook can reach + its data plane. +3. Creates a simple HR **prompt agent** (mocked HR policy data) on `gpt-4.1`. +4. Enables the agent's incoming **A2A endpoint** and verifies its agent card. +5. Re-disables public network access to restore the secure, private-only posture. +6. Publishes the agent through **APIM** as a governed access contract (product + + subscription) — the same `apimOnboardService.bicep` module used in + [Challenge 3](challenge-03.md). +7. Calls the agent through APIM with just a subscription key — proving APIM reaches it + over its private endpoint even with public access disabled. + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run the A2A publish & consume notebook (35 min) + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/8. publish-and-use-a2a-endpoint.ipynb`. +2. Run the notebook's sections in order: + - `1. Configuration` + - `2. Helpers` + - `3. Resolve the Foundry environment from azd` + - `4. Enable public network access on the agent's Foundry account` + - `5. Create the HR prompt agent` + - `6. Enable the incoming A2A endpoint` + - `7. Resolve the agent card URLs` + - `8. Verify the v1.0 agent card` + - `9. Re-disable public network access (restore secure posture)` + - `10. Expose the agent as an A2A agent through APIM` — sub-steps `10.1` (resolve APIM + coordinates), `10.2` (create the APIM API), `10.3` (attach the backend-auth policy), + `10.4` (create the access contract) + - `11. Call the agent through the APIM A2A endpoint` +3. Read `Notes & troubleshooting`, then optionally run `🧹 Cleanup` — it is **disabled by + default**; you must set `cleanup_enabled = True` in that cell to remove the A2A access + contract and API (it does not delete the Foundry agent itself). + +## 🏁 Success criteria + +- [ ] The HR prompt agent was created on `gpt-4.1` and its A2A endpoint was enabled, with + the `v1.0` agent card verified (step 8). +- [ ] Public network access on the Foundry account was re-disabled (step 9) **before** + publishing through APIM. +- [ ] An APIM API, product, and subscription were created (steps `10.1`–`10.4`) fronting + the agent's A2A JSON-RPC runtime URL. +- [ ] Both calls in step 11 (agent card `GET` and JSON-RPC `message/send` `POST`) succeed + through APIM using only the subscription key — with the Foundry account still + privately-networked. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `azd env get-value` errors | `setup-notebook-env.ps1` hasn't been run — see Part A. | +| `500` — backend "name is valid, but no data of the requested type was found" | A DNS/network error: APIM can't reach the agent's Foundry host. Confirm you're publishing through the APIM in the **same** azd environment as the agent. | +| `500` with a managed-identity error in the trace | APIM had no usable identity — section `10.3` auto-detects system- vs. user-assigned identity; if user-assigned, confirm the `client-id` was pinned. | +| `401`/`403` from the backend | The APIM managed identity lacks an Azure AI role (e.g. **Cognitive Services User**) on the agent's Foundry account — `azd up` grants this by default, but role propagation can take a few minutes. | +| `401` at APIM (before reaching the backend) | Missing/invalid `Ocp-Apim-Subscription-Key`, or the subscription isn't active — re-run section `10.4`. | +| `403 PublicNetworkAccess` when creating the agent (step 5) | The network change from step 4 hasn't propagated yet — the create cell retries; wait a minute and re-run if it still fails. | +| `404` on the agent card | Confirm the agent's A2A endpoint is enabled (steps 6–8) and `BASE_URL` matches your project/agent. | + +## 🚀 Go further + +- Set `ENABLE_MESSAGE_LOGGING = True` in section 1 before publishing, then inspect the + `a2a-message-logging` trace records in Application Insights after step 11. +- Change `A2A_AGENT_NAME` (and optionally `A2A_PRODUCT_ID`) and re-run from step 5 to + publish a second agent under its own access contract. + +## 📚 Learning resources + +- [Enable an agent-to-agent endpoint](https://learn.microsoft.com/azure/foundry/agents/how-to/enable-agent-to-agent-endpoint?tabs=rest-powershell%2Cverify-bash%2Cconnection-bash) +- [Import an A2A agent API in Azure API Management](https://learn.microsoft.com/azure/api-management/agent-to-agent-api) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md new file mode 100644 index 000000000..70ff9f0a6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md @@ -0,0 +1,113 @@ +# Challenge 9 — HR MCP via APIM (Optional / Instructor-Led) + +**[Home](../README.md)** - [← Previous Challenge](challenge-08.md) + +> [!IMPORTANT] +> **This challenge requires infrastructure this lab's automation does not deploy.** +> Unlike Challenges 1–8, the HR MCP backend (Container App, its Entra app registration, +> and the `Mcp.Invoke` app role) is provisioned by scripts under `workshop/mcp-hr/scripts/` +> that `labautomation/deploy-lab.ps1` **never calls**. Attempt this challenge only if your +> facilitator has separately run those scripts and shared the resulting `HR_MCP_*` values +> with you. Otherwise, treat this as a read-through: skim the notebook and this challenge +> to understand the pattern, and move on. + +## 🎯 Objective + +Validate the HR **Model Context Protocol (MCP)** server through **Azure API Management +only**: exercise the Citadel-style access contract, call MCP protocol/tool operations, +observe the APIM `5 tools/call per 30 seconds` rate limit, and configure a Microsoft +Agent Framework (MAF) hosted agent that calls the APIM-published MCP endpoint using its +own managed identity plus a delegated Entra token and APIM subscription key. + +## 🧭 Context + +| Prerequisite | Provided by | +|---|---| +| Base HR MCP infrastructure (Container App running the MCP server) | `workshop/mcp-hr/scripts/deploy-hr-mcp.*` — **not** part of `deploy-lab.ps1` | +| APIM access-contract assets (API, product, subscription, policies) | `workshop/mcp-hr/scripts/publish-hr-mcp-apim.*` — **not** part of `deploy-lab.ps1` | +| Entra app registration + `Mcp.Invoke` app role for the HR MCP API | Created by the scripts above — **not** automated by this lab, per its "no attendee-specific Entra app registration" scope | +| A delegated token for `HR_MCP_SCOPE` | `az account get-access-token`, using values only available once the above exists | + +Because none of this is created by `setup-notebook-env.ps1` or `deploy-lab.ps1`, the +notebook's own variables (`HR_MCP_APIM_NAME`, `HR_MCP_APIM_RESOURCE_GROUP`, +`HR_MCP_SCOPE`, `HR_MCP_TENANT_ID`, `HR_MCP_AUDIENCE`, `HR_MCP_APP_ROLE_ID`, +`HR_MCP_API_CLIENT_ID`, etc.) will not resolve for you unless your facilitator has +pre-provisioned the HR MCP backend and given you these values directly (e.g. as +environment variables or in your `azd` environment). + +## ✅ Tasks + +### Part A — Confirm your notebook environment is ready (2 min) + +Same prerequisite as every notebook in this MicroHack — see +[Challenge 1, Part A](challenge-01.md#part-a--confirm-your-notebook-environment-is-ready-5-min). + +### Part B — Run as far as your environment allows + +1. Open + `../reference/ai-hub-gateway-solution-accelerator/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb`. +2. **If the HR MCP backend has not been provisioned for you**, run only the read-only + setup sections and stop before acquiring a token: + - `0️⃣ Initialize notebook variables from azd env and environment variables` + - `1️⃣ Verify Azure CLI login and subscription` + - `2️⃣ Resolve APIM MCP endpoint and access-contract settings` (this will show which + `HR_MCP_*` values are missing) + + Skip section `3️⃣` onward — they require the delegated token and app role that only + exist once the facilitator's HR MCP deploy/publish scripts have run. +3. **If your facilitator has provisioned the HR MCP backend and shared the `HR_MCP_*` + values**, set them in your environment, then continue through the rest of the + notebook: + - `3️⃣ Acquire participant delegated Entra token without printing it` + - `4️⃣ Create the Citadel-style APIM access contract` (runs `publish-hr-mcp-apim`; set + `RUN_APIM_PUBLISH = False` to only validate existing infra) + - `5️⃣ Resolve APIM subscription key without printing it` + - `6️⃣ APIM-only MCP JSON-RPC helpers` + - `7️⃣ Test initialize, tools/list, read tool, and write tool through APIM only` + - `8️⃣ Demonstrate the APIM tools/call rate limit` (5 calls / 30 s, expects a `429` on + the excess request) + - `9️⃣ Inspect APIM request/response body logs` + - `🔟 Deploy a Foundry hosted agent that uses the HR MCP via APIM` (mirrors + [Challenge 7](challenge-07.md), but the agent's tools come from the HR MCP server + via APIM instead of local Python tools) + - `1️⃣1️⃣ HR workflow through APIM` +4. Read the `✅ Summary` cell. + +## 🏁 Success criteria + +If you only ran the read-only setup sections: + +- [ ] You can explain, from section `2️⃣`'s output, which `HR_MCP_*` values are missing + and why (the HR MCP backend + Entra app role aren't part of this lab's automation). + +If your facilitator provisioned the HR MCP backend and you ran the full notebook: + +- [ ] A delegated token was acquired without ever being printed (step `3️⃣`), and the + APIM access contract was created or validated (step `4️⃣`). +- [ ] MCP `initialize`, `tools/list`, and both a read and a write tool call succeed + through APIM only (step `7️⃣`). +- [ ] The 6th rapid `tools/call` request within 30 seconds returns `429` (step `8️⃣`). +- [ ] The hosted agent from step `🔟` calls HR MCP tools through APIM using its own + managed identity, a fresh Entra token, and the APIM subscription key — not a + participant token. + +## 🛠️ Troubleshooting + +| Symptom | Fix | +|---------|-----| +| `Missing HR_MCP_*` errors anywhere from section `3️⃣` onward | Expected if the HR MCP backend hasn't been deployed for this lab session — see the callout at the top of this challenge. Ask your facilitator whether it's in scope for today. | +| `Could not acquire delegated token` | Confirm `HR_MCP_SCOPE` (and `HR_MCP_TENANT_ID`, if cross-tenant) were set correctly by your facilitator, and that you're logged in via `az login` against the right tenant. | +| Rate-limit test (step `8️⃣`) never returns `429` | The policy is scoped per participant subscription — confirm you're using your own APIM subscription key, not a shared one, and that requests are sent back-to-back. | +| Hosted agent (step `🔟`) gets `401`/`403` calling HR MCP | Its managed identity needs the `Mcp.Invoke` app role on the HR MCP API — confirm `HR_MCP_APP_ROLE_ID` / `HR_MCP_API_CLIENT_ID` are correct and RBAC/app-role assignment has propagated. | + +## 🚀 Go further + +- If you have facilitator access to run `workshop/mcp-hr/scripts/deploy-hr-mcp.*` and + `publish-hr-mcp-apim.*` yourself, provision the HR MCP backend end-to-end and note what + it would take to fold that into `labautomation/deploy-lab.ps1` for a future cohort. + +## 📚 Learning resources + +- [Model Context Protocol specification](https://modelcontextprotocol.io/) +- [Publish an MCP server through Azure API Management](https://learn.microsoft.com/azure/api-management/export-rest-mcp-server) +- [Microsoft Agent Framework overview](https://learn.microsoft.com/agent-framework/overview/agent-framework-overview) diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/README.md b/03-Azure/01-04-AI/05_AI_Governance/labautomation/README.md new file mode 100644 index 000000000..ad992788c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/README.md @@ -0,0 +1,180 @@ +# labautomation — Agentic Governance Hub MicroHack + +This folder is the **EMEA MicroHack platform** entry point. The platform calls +[`deploy-lab.ps1`](deploy-lab.ps1) once per attendee (in parallel) to provision +their lab into a pre-created resource group. + +The deployment provisions a **Citadel Agentic Governance Hub** and a sample **Spoke** (Azure AI Foundry workload) — both deployed into the same platform-provided resource group using deterministic naming to avoid collisions. + +## What Gets Provisioned Per Attendee + +Adapted from the Citadel workshop (`azd up`), the lab deploys: + +### Hub Resources +| Resource | Purpose | +|----------|---------| +| **API Management (StandardV2)** | Unified AI gateway; all model calls route through APIM | +| **Azure AI Foundry Account (Hub)** | Primary Foundry account (`aif-hub-`) for governance backend | +| **Azure AI Foundry Project (Hub)** | Hosts hub-level agents and access-control demonstrations | +| **Log Analytics Workspace** | Hub observability backend | +| **Application Insights** | Connected to hub Foundry for agent tracing | +| **Cosmos DB (NoSQL, serverless)** | Usage tracking and telemetry storage | +| **Event Hub** | Event streaming for usage pipeline | +| **Key Vault** | Hub secrets (API keys, Foundry credentials) | +| **Storage Account** | Logic App workflow state | +| **Logic App** | Usage ingestion workflow (if deployed) | + +### Spoke Resources (Folded into Same RG) +| Resource | Purpose | +|----------|---------| +| **Azure AI Foundry Account (Spoke)** | Sample workload Foundry account (`aif-spoke-`) for attendee agent development | +| **Azure AI Foundry Project (Spoke)** | Where attendees deploy their governed agents (`citadel-agents-project`) | +| **Log Analytics Workspace (Spoke)** | Spoke observability | +| **Application Insights (Spoke)** | Spoke agent tracing | +| **Key Vault (Spoke)** | Spoke secrets | +| **Azure Container Registry (Spoke)** | Spoke artifact storage | + +## Platform Contract + +- The parameter block matches the MicroHack platform contract exactly. +- The platform **pre-creates the resource group** and pre-sets the Azure context. + The script does **not** call `Connect-AzAccount` or `New-AzResourceGroup`. +- Hub and spoke resources are deployed into **the same platform-provided ResourceGroupName** + using deterministic suffixes to avoid name collisions. +- Foundry accounts, projects, Log Analytics, App Insights, Key Vault, and APIM are all + provisioned with **managed identities** — no keys or secrets in logs. +- RBAC is assigned **per attendee** (via `$AllowedEntraUserIds`) across Foundry, Key Vault, + Cosmos DB, and monitoring services, so the notebooks work out-of-the-box. + +## What the Attendee Receives + +The platform dashboard surfaces these credentials as `HackboxCredential` objects. +The **Notebook azd Key** column is what the corresponding value must be set to via +`setup-notebook-env.ps1` for the unchanged workshop notebooks to consume it via +`azd env get-value` (see [Notebook Environment Setup](#notebook-environment-setup) below). + +| Credential | Used For | Notebook azd Key | +|-----------|----------|-------------------| +| `ResourceGroup` | Portal navigation, resource discovery | `AZURE_RESOURCE_GROUP` | +| `Location` | Region-aware notebook logic | `AZURE_LOCATION` | +| `SubscriptionId` | Notebooks 4, 7, 8, 9 (Foundry/agent SDK auth) | `AZURE_SUBSCRIPTION_ID` | +| `LlmBackendConfig` | Notebook 1 (LLM backend onboarding), Notebook 8 | `LLM_BACKEND_CONFIG` | +| `SpokeResourceGroup` | Notebooks 3, 4, 7 (single-RG deployment: same as `ResourceGroup`) | `SPOKE_RESOURCE_GROUP` | +| `SpokeKeyVaultName` | Notebooks 3, 4 (credential retrieval via DefaultAzureCredential) | `SPOKE_KEY_VAULT_NAME` | +| `SpokeFoundryAccountName` / `SpokeAiFoundryAccountName` (alias) | Notebooks 3, 4, 7 | `SPOKE_AI_FOUNDRY_ACCOUNT_NAME` | +| `SpokeFoundryProjectName` / `SpokeAiFoundryProjectName` (alias) | Notebooks 3, 4, 7 | `SPOKE_AI_FOUNDRY_PROJECT_NAME` | +| `SpokeAcrName` | Notebook 7 (container publishing) | `SPOKE_ACR_NAME` | +| `SpokeAcrLoginServer` | Notebook 7 (container publishing) | `SPOKE_ACR_LOGIN_SERVER` | +| `HubFoundryProjectEndpoint` | Notebook 1 (LLM Onboarding), Notebooks 3, 6 (access controls, unified API) | — | +| `SpokeFoundryProjectEndpoint` | Notebooks 7–9 (hosted agents, agent-to-agent, HR MCP) | — | +| `ApimGatewayUrl` | All notebooks (unified inference endpoint) | — | +| `CosmosEndpoint` | Notebook 2 (usage analytics) | — | +| `KeyVaultName` | All notebooks (credential retrieval via DefaultAzureCredential) | — | +| `LogAnalyticsWorkspaceId` | Challenge 3 (observability queries) | — | +| `ApplicationInsightsConnectionString` | Challenge 3 (agent traces, tracing) | — | +| `ResourceGroupName` | Portal navigation, resource discovery | — | + +`SpokeAiFoundryAccountName`/`SpokeAiFoundryProjectName` are exact-name aliases of +`SpokeFoundryAccountName`/`SpokeFoundryProjectName` — both are emitted so the +dashboard label matches the `SPOKE_AI_FOUNDRY_*` key the notebooks expect. + +## Notebook Environment Setup + +The workshop notebooks are unchanged and call `azd env get-value ` for their +configuration — but this lab deploys with `deploy-lab.ps1` (PowerShell/Bicep), not +`azd up`, so there's no azd environment for them to read by default. + +Run [`setup-notebook-env.ps1`](setup-notebook-env.ps1) once per attendee, using the +`HackboxCredential` values from the dashboard, to bridge the gap: + +```powershell +./setup-notebook-env.ps1 ` + -ResourceGroup "" ` + -Location "" ` + -SubscriptionId "" ` + -LlmBackendConfig "" ` + -SpokeKeyVaultName "" ` + -SpokeAiFoundryAccountName "" ` + -SpokeAiFoundryProjectName "" ` + -SpokeAcrName "" ` + -SpokeAcrLoginServer "" +``` + +This creates/selects a local azd environment (`citadel-microhack` by default) and +sets every key the notebooks read (`AZURE_RESOURCE_GROUP`, `AZURE_LOCATION`, +`AZURE_SUBSCRIPTION_ID`, `LLM_BACKEND_CONFIG`, `SPOKE_RESOURCE_GROUP`, +`SPOKE_KEY_VAULT_NAME`, `SPOKE_AI_FOUNDRY_ACCOUNT_NAME`, +`SPOKE_AI_FOUNDRY_PROJECT_NAME`, `SPOKE_ACR_NAME`, `SPOKE_ACR_LOGIN_SERVER`). +Requires `azd` to be installed (fails clearly if not found on `PATH`). Notebooks +can then be opened and run without modification. + +## Deployment Robustness + +The script follows MicroHack conventions: + +- **Region fallback** — Retries across `$PreferredLocation` (honoring platform preference), + falling back to [swedencentral, westeurope, norwayeast] if all fail. +- **Deterministic naming** — `Get-MhhStableHash` generates a stable suffix per attendee, + ensuring names are DNS-safe, globally unique, and consistent across re-runs. +- **Idempotent provisioning** — All resources are created once; re-runs skip existing resources. +- **Managed identities** — No hardcoded keys or connection strings; all auth uses Entra ID + + Azure RBAC (DefaultAzureCredential in notebooks). +- **Multi-user RBAC** — Every ID in `$AllowedEntraUserIds` is granted the necessary data-plane + roles (Azure AI User, Cognitive Services User, Cosmos DB Data Contributor, Key Vault Secrets + Officer, Monitoring Reader), plus: + - **`API Management Service Contributor`** on the APIM resource — `APIMClientTool` (Notebooks + 3–7, 9) is an ARM control-plane client that lists APIs/subscriptions and creates/updates + products and subscriptions dynamically; this is the least-privileged built-in role that + covers both the APIM service and its entities (the narrower `Operator`/`Reader` built-in + roles cannot create/update products or subscriptions). + - **`AcrPush`** on the spoke ACR — required only for Notebook 7's container publishing; + granted by default since the ACR already exists in the core deployment. + + So team labs work for all members. +- **Robust output** — `[INFO]`, `[OK]`, `[WARN]` prefixes guide troubleshooting. + +## Subscription Scope Prerequisites + +Before the platform runs `deploy-lab.ps1`, these must be pre-registered at the subscription level: + +- `Microsoft.ApiManagement` +- `Microsoft.CognitiveServices` +- `Microsoft.EventHub` +- `Microsoft.KeyVault` +- `Microsoft.OperationalInsights` +- `Microsoft.DocumentDB` +- `Microsoft.Insights` +- `Microsoft.Storage` +- `Microsoft.ContainerRegistry` +- `Microsoft.Logic` (if Logic App is deployed) + +If a provider is not registered, the script will fail with a clear message: *"Provider X must be registered at subscription scope before lab deployment."* + +## Local Dry Run + +The platform injects `Get-MhhStableHash` and pre-creates the resource group, so +`deploy-lab.ps1` cannot run directly outside the platform. Use the local wrapper: + +```powershell +# Requires: az login + Connect-AzAccount to the test subscription, +# and the Az.Accounts / Az.Resources modules installed. +./run-local.ps1 -SubscriptionId -ResourceGroupName ` + -Location swedencentral -AllowedEntraUserIds +``` + +> `run-local.ps1` is a **local-only helper** (not used by the platform). +> It mirrors the `resourcegroup` deployment path. + +After a successful dry run, run `setup-notebook-env.ps1` (see +[Notebook Environment Setup](#notebook-environment-setup) above) using the +`HackboxCredential` values printed by `deploy-lab.ps1` to bridge the notebooks' +`azd env get-value` calls. + +## Configuration + +Edit [`lab-defaults.json`](lab-defaults.json) to tune: +- Regional preferences (adjust for availability) +- Labs per subscription (8 labs × ~15 USD/day = ~120 USD/day per subscription) +- Estimated daily cost estimate + +No credentials or secrets are stored in this folder. diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/deploy-lab.ps1 b/03-Azure/01-04-AI/05_AI_Governance/labautomation/deploy-lab.ps1 new file mode 100644 index 000000000..5c6f7a77a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/deploy-lab.ps1 @@ -0,0 +1,604 @@ +<# +.SYNOPSIS + Deploys the Citadel Agentic Governance Hub (with integrated spoke sample) + for the Belgium MicroHack. + +.DESCRIPTION + Called by the EMEA MicroHack platform once per attendee, in parallel. + + Provisions a complete Citadel hub-and-spoke architecture into a single + platform-provided resource group using deterministic naming: + + Hub resources: + - API Management (StandardV2) — unified AI gateway + - Azure AI Foundry account + project (hub-scoped) + - Log Analytics workspace + Application Insights (hub) + - Cosmos DB (serverless, keyless) — usage tracking + - Event Hub — event streaming pipeline + - Key Vault — hub secrets + - Storage Account + Logic App (if deployed) — usage ingestion + + Spoke resources (same RG, deterministic suffix): + - Azure AI Foundry account + project (spoke-scoped) + - Log Analytics workspace + Application Insights (spoke) + - Key Vault (spoke) — spoke secrets + - Azure Container Registry (spoke) + + RBAC is assigned per attendee across Foundry, Key Vault, Cosmos, monitoring, APIM + (management-plane, for APIMClientTool) and ACR (AcrPush, for Notebook 7), so the + unchanged Citadel notebooks work out-of-the-box with DefaultAzureCredential. + + The platform pre-sets the Az context to $SubscriptionId and pre-creates the + resource group. Do NOT call Connect-AzAccount or New-AzResourceGroup. + +.PARAMETER DeploymentType + Deployment scope — passed in by the platform. +.PARAMETER SubscriptionId + Azure subscription — passed in by the platform. +.PARAMETER ResourceGroupName + Pre-created resource group — passed in by the platform. +.PARAMETER PreferredLocation + Ordered list of preferred Azure regions — passed in by the platform. +.PARAMETER AllowedEntraUserIds + Entra user object IDs for this lab — passed in by the platform. +#> +param( + [Parameter(Mandatory=$true)] + [ValidateSet('subscription','resourcegroup','resourcegroup-with-subscriptionowner')] + [string]$DeploymentType, + + [Parameter(Mandatory=$true)] + [string]$SubscriptionId, + + [string]$ResourceGroupName = "", + + [string[]]$PreferredLocation = @(), + + [string[]]$AllowedEntraUserIds = @() +) + +$ErrorActionPreference = "Stop" + +# Resolve effective values — honour $PreferredLocation, fall back across the list +$candidateRegions = if ($PreferredLocation.Count -gt 0) { $PreferredLocation } else { @("swedencentral", "westeurope", "norwayeast") } +$effectiveLocation = $candidateRegions[0] +$effectiveRG = $ResourceGroupName + +# Deterministic token for resource naming (based on subscription + RG). +# Ensures hub and spoke resources don't collide and survive re-runs. +$stableHash = (Get-MhhStableHash $AllowedEntraUserIds -Length 12).ToLower() +$resourceToken = (Get-MhhStableHash "$SubscriptionId-$effectiveRG" -Length 13).ToLower() + +# Resource naming +$hubFoundryAccountName = "aif-hub-$stableHash" +$hubFoundryProjectName = "citadel-hub-project" +$spokeFoundryAccountName = "aif-spoke-$stableHash" +$spokeFoundryProjectName = "citadel-agents-project" +$apimName = "apim-citadel-$stableHash" +$cosmosName = "cos-citadel-$stableHash" +$keyVaultName = "kv-hub-$stableHash" +$laName = "law-citadel-$stableHash" +$aiName = "appi-citadel-$stableHash" + +Write-Host "[INFO] Deploying Citadel Agentic Governance Hub + Spoke into RG '$effectiveRG'..." + +$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$bicepFile = Join-Path $scriptPath 'infra/resources.bicep' + +if (-not (Test-Path $bicepFile)) { + throw "Bicep template not found at '$bicepFile'. Ensure infra/resources.bicep exists." +} + +$primaryPrincipalId = if ($AllowedEntraUserIds.Count -gt 0) { $AllowedEntraUserIds[0] } else { '' } +$tags = @{ project = 'citadel-agentic-governance'; 'lab-deployment-type' = $DeploymentType } + +# Guard: ALL participant data-plane RBAC is keyed off $AllowedEntraUserIds. +# An empty list leaves participants with ZERO data-plane roles, breaking notebook execution. +if ($AllowedEntraUserIds.Count -eq 0) { + Write-Host "[WARN] AllowedEntraUserIds is EMPTY — no participant will receive data-plane RBAC" + Write-Host "[WARN] (Azure AI User, Cognitive Services User, Cosmos DB Data Contributor)." + Write-Host "[WARN] Notebooks will fail. The platform normally sets this per lab; for manual runs," + Write-Host "[WARN] pass -AllowedEntraUserIds (comma-separate ids for a team lab)." +} + +$deployOutputs = $null +$effectiveLocation = $null + +Write-Host "[INFO] Engine: Bicep (infra/resources.bicep), resource-group-scoped." + +foreach ($region in $candidateRegions) { + Write-Host "[INFO] Deploying Bicep → RG '$effectiveRG' in '$region'..." + try { + $d = New-AzResourceGroupDeployment ` + -ResourceGroupName $effectiveRG ` + -TemplateFile $bicepFile ` + -location $region ` + -resourceToken $resourceToken ` + -principalId $primaryPrincipalId ` + -principalType 'User' ` + -tags $tags ` + -ErrorAction Stop + $deployOutputs = $d.Outputs + $effectiveLocation = $region + break + } + catch { + Write-Host "[WARN] Bicep deployment failed in '$region': $_ — trying next region." + } +} + +if (-not $deployOutputs) { + throw "Bicep deployment failed in all candidate regions: $($candidateRegions -join ', ')" +} + +Write-Host "[OK] Provisioning complete in '$effectiveLocation' (resource group '$effectiveRG')." + +# --- Managed Identity Readiness Check --- +# Hub and Spoke Foundry accounts have system-assigned managed identities that may not be +# immediately available after creation. Wait for them to be readable before granting RBAC. +Write-Host "[INFO] Verifying managed identity availability..." +$hubAccount = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.CognitiveServices/accounts' ` + -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "aif-hub-*" } | Select-Object -First 1 + +$spokeAccount = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.CognitiveServices/accounts' ` + -ErrorAction SilentlyContinue | Where-Object { $_.Name -like "aif-spoke-*" } | Select-Object -First 1 + +$maxWaitSecs = 120 +$waitInterval = 10 +$elapsed = 0 + +while ($elapsed -lt $maxWaitSecs) { + $hubMIReady = $false + $spokeMIReady = $false + + if ($hubAccount) { + $hubMI = Get-AzResource -ResourceId $hubAccount.ResourceId -ExpandProperties -ErrorAction SilentlyContinue + if ($hubMI -and $hubMI.Identity.PrincipalId) { + $hubMIReady = $true + } + } + + if ($spokeAccount) { + $spokeMI = Get-AzResource -ResourceId $spokeAccount.ResourceId -ExpandProperties -ErrorAction SilentlyContinue + if ($spokeMI -and $spokeMI.Identity.PrincipalId) { + $spokeMIReady = $true + } + } + + if ($hubMIReady -and $spokeMIReady) { + Write-Host "[OK] Managed identities are ready." + break + } + + if ($elapsed -eq 0) { + Write-Host "[INFO] Waiting for managed identities to become available... (up to ${maxWaitSecs}s)" + } + + Start-Sleep -Seconds $waitInterval + $elapsed += $waitInterval +} + +if ($hubAccount -and -not $hubMIReady) { + Write-Host "[WARN] Hub Foundry managed identity did not become ready within ${maxWaitSecs}s. RBAC grants may fail." +} + +# --- Multi-user data-plane RBAC --- +# Grant Foundry and Cosmos DB access to all attendees so team labs work for every member. +if ($AllowedEntraUserIds.Count -gt 0 -and $effectiveRG) { + $account = $hubAccount + + $cosmos = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.DocumentDB/databaseAccounts' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + $keyVault = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.KeyVault/vaults' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + $appInsightsRes = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.Insights/components' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + $logAnalyticsRes = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.OperationalInsights/workspaces' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + $apim = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.ApiManagement/service' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + $acr = Get-AzResource ` + -ResourceGroupName $effectiveRG ` + -ResourceType 'Microsoft.ContainerRegistry/registries' ` + -ErrorAction SilentlyContinue | Select-Object -First 1 + + # RBAC role IDs (built-in) + $foundryRoles = [ordered]@{ + 'Azure AI User' = '53ca6127-db72-4b80-b1b0-d745d6d5456d' + 'Cognitive Services User' = 'a97b65f3-24c7-4388-baec-2e87135dc908' + } + + $cosmosDataRole = '00000000-0000-0000-0000-000000000002' # Cosmos DB Built-in Data Contributor + + $keyVaultRoles = [ordered]@{ + 'Key Vault Secrets Officer' = 'b86a8fe4-44ce-4948-aee5-eccb2c155090' + } + + $monitoringRoles = [ordered]@{ + 'Monitoring Reader' = '43d0d8ad-25c7-4714-9337-8ba259a9fe05' + } + + # APIMClientTool (used by notebooks 3-7, 9) is an ARM control-plane client — it needs + # a management-plane role on the APIM resource, not just a gateway subscription key, + # to list APIs/subscriptions and to create products/subscriptions dynamically. + # 'API Management Service Contributor' is the least-privileged BUILT-IN role that covers + # both the service and its entities (APIs, products, subscriptions, policies). + # 'API Management Service Operator Role' only manages the service itself (not entities), + # and 'API Management Service Reader Role' is read-only — neither supports the + # create/update-product/subscription flows the notebooks perform. + $apimRoles = [ordered]@{ + 'API Management Service Contributor' = '312a565d-c81f-4fd8-895a-4e21e48d571c' + } + + # Notebook 7 pushes container images to the spoke ACR — AcrPush is the least-privileged + # built-in role for that (data-plane push/pull only, no control-plane permissions). + $acrRoles = [ordered]@{ + 'AcrPush' = '8311e382-0749-4cb8-b61a-304f252e45ec' + } + + foreach ($userId in $AllowedEntraUserIds) { + # Foundry data-plane RBAC + if ($account) { + foreach ($roleName in $foundryRoles.Keys) { + $roleId = $foundryRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $account.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on Foundry." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $account.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on Foundry." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } + + # Cosmos DB data-plane RBAC (via REST because Az.CosmosDB module is complex) + if ($cosmos) { + $cosmosBase = $cosmos.ResourceId + $cosmosApi = "2024-11-15" + $cosmosRoleUri = "$cosmosBase/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002?api-version=$cosmosApi" + $existingRAs = Invoke-AzRestMethod -Method GET -Path "$cosmosBase/sqlRoleAssignments?api-version=$cosmosApi" -ErrorAction SilentlyContinue + $raList = if ($existingRAs.StatusCode -eq 200) { ($existingRAs.Content | ConvertFrom-Json).value } else { @() } + + $already = $raList | Where-Object { $_.properties.principalId -eq $userId -and $_.properties.roleDefinitionId -like "*000000000002" } + if ($already) { + Write-Host "[OK] Cosmos DB data role already granted to $userId." + continue + } + + try { + $assignId = [guid]::NewGuid().ToString() + $raBody = @{ properties = @{ + roleDefinitionId = $cosmosRoleUri + principalId = $userId + scope = $cosmosBase + } } | ConvertTo-Json -Depth 5 + Invoke-AzRestMethod -Method PUT -Path "$cosmosBase/sqlRoleAssignments/${assignId}?api-version=$cosmosApi" -Payload $raBody -ErrorAction Stop | Out-Null + Write-Host "[OK] Cosmos DB data-plane role granted to $userId." + } catch { + Write-Host "[WARN] Could not grant Cosmos DB role to ${userId}: $_" + } + } + + # Key Vault RBAC + if ($keyVault) { + foreach ($roleName in $keyVaultRoles.Keys) { + $roleId = $keyVaultRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $keyVault.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on Key Vault." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $keyVault.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on Key Vault." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } + + # Monitoring RBAC (Log Analytics + App Insights) + if ($logAnalyticsRes) { + foreach ($roleName in $monitoringRoles.Keys) { + $roleId = $monitoringRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $logAnalyticsRes.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on Log Analytics." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $logAnalyticsRes.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on Log Analytics." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } + + if ($appInsightsRes) { + foreach ($roleName in $monitoringRoles.Keys) { + $roleId = $monitoringRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $appInsightsRes.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on Application Insights." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $appInsightsRes.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on Application Insights." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } + + # APIM management RBAC (APIMClientTool: list APIs/subscriptions, create/update + # products/subscriptions dynamically — notebooks 3-7, 9) + if ($apim) { + foreach ($roleName in $apimRoles.Keys) { + $roleId = $apimRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $apim.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on APIM." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $apim.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on APIM." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } else { + Write-Host "[WARN] No APIM resource found in RG '$effectiveRG' — skipping APIM management RBAC." + } + + # ACR RBAC (optional — required only for Notebook 7's container publishing) + if ($acr) { + foreach ($roleName in $acrRoles.Keys) { + $roleId = $acrRoles[$roleName] + $existing = Get-AzRoleAssignment -ObjectId $userId -Scope $acr.ResourceId -RoleDefinitionId $roleId -ErrorAction SilentlyContinue + if ($existing) { + Write-Host "[OK] '$roleName' already granted to $userId on ACR." + continue + } + try { + New-AzRoleAssignment -ObjectId $userId -RoleDefinitionId $roleId -Scope $acr.ResourceId -ErrorAction Stop | Out-Null + Write-Host "[OK] Granted '$roleName' to $userId on ACR." + } catch { + Write-Host "[WARN] Could not grant '$roleName' to ${userId}: $_" + } + } + } else { + Write-Host "[WARN] No ACR found in RG '$effectiveRG' — Notebook 7 (container publishing) will be blocked." + } + } +} + +# --- Return credentials to the attendee dashboard --- +Write-Host "[OK] Lab provisioning complete." + +function Get-OutVal { + param($Outputs, [string]$Key) + if ($Outputs -and $Outputs.ContainsKey($Key)) { return "$($Outputs[$Key].Value)" } + return '' +} + +# Extract outputs from Bicep deployment +$hubFoundryEndpoint = Get-OutVal $deployOutputs 'HUB_FOUNDRY_PROJECT_ENDPOINT' +$spokeFoundryEndpoint = Get-OutVal $deployOutputs 'SPOKE_FOUNDRY_PROJECT_ENDPOINT' +$apimGatewayUrl = Get-OutVal $deployOutputs 'APIM_GATEWAY_URL' +$cosmosEndpoint = Get-OutVal $deployOutputs 'COSMOS_ENDPOINT' +$cosmosDatabase = Get-OutVal $deployOutputs 'COSMOS_DATABASE' +$keyVaultUrl = Get-OutVal $deployOutputs 'KEY_VAULT_URL' +$keyVaultName = Get-OutVal $deployOutputs 'KEY_VAULT_NAME' +$laWorkspaceId = Get-OutVal $deployOutputs 'LOG_ANALYTICS_WORKSPACE_ID' +$laWorkspaceName = Get-OutVal $deployOutputs 'LOG_ANALYTICS_WORKSPACE_NAME' +$laResourceId = Get-OutVal $deployOutputs 'LOG_ANALYTICS_WORKSPACE_RESOURCE_ID' +$appInsightsConnStr = Get-OutVal $deployOutputs 'APPLICATIONINSIGHTS_CONNECTION_STRING' +$appInsightsKey = Get-OutVal $deployOutputs 'APPLICATIONINSIGHTS_INSTRUMENTATION_KEY' +$eventHubNamespace = Get-OutVal $deployOutputs 'EVENT_HUB_NAMESPACE' +$eventHubConnStr = Get-OutVal $deployOutputs 'EVENT_HUB_CONNECTION_STRING' +$storageAccountName = Get-OutVal $deployOutputs 'STORAGE_ACCOUNT_NAME' +$storageConnStr = Get-OutVal $deployOutputs 'STORAGE_ACCOUNT_CONNECTION_STRING' +$apimName = Get-OutVal $deployOutputs 'APIM_NAME' +$spokeConnStr = Get-OutVal $deployOutputs 'SPOKE_APP_INSIGHTS_CONNECTION_STRING' + +# Phase 4: per-scenario APIM subscription keys/IDs +$salesAssistantKey = Get-OutVal $deployOutputs 'SALES_ASSISTANT_SUBSCRIPTION_KEY' +$salesAssistantId = Get-OutVal $deployOutputs 'SALES_ASSISTANT_SUBSCRIPTION_ID' +$hrChatAgentKey = Get-OutVal $deployOutputs 'HR_CHATAGENT_SUBSCRIPTION_KEY' +$hrChatAgentId = Get-OutVal $deployOutputs 'HR_CHATAGENT_SUBSCRIPTION_ID' +$supportBotKey = Get-OutVal $deployOutputs 'SUPPORT_BOT_SUBSCRIPTION_KEY' +$supportBotId = Get-OutVal $deployOutputs 'SUPPORT_BOT_SUBSCRIPTION_ID' +$universalLlmTestKey = Get-OutVal $deployOutputs 'UNIVERSAL_LLM_TEST_SUBSCRIPTION_KEY' +$universalLlmTestId = Get-OutVal $deployOutputs 'UNIVERSAL_LLM_TEST_SUBSCRIPTION_ID' +$unifiedAiTestKey = Get-OutVal $deployOutputs 'UNIFIED_AI_TEST_SUBSCRIPTION_KEY' +$unifiedAiTestId = Get-OutVal $deployOutputs 'UNIFIED_AI_TEST_SUBSCRIPTION_ID' +$piiMaskingKey = Get-OutVal $deployOutputs 'PII_MASKING_SUBSCRIPTION_KEY' +$piiMaskingId = Get-OutVal $deployOutputs 'PII_MASKING_SUBSCRIPTION_ID' +$piiBlockingKey = Get-OutVal $deployOutputs 'PII_BLOCKING_SUBSCRIPTION_KEY' +$piiBlockingId = Get-OutVal $deployOutputs 'PII_BLOCKING_SUBSCRIPTION_ID' +$piiAnalyticsKey = Get-OutVal $deployOutputs 'PII_ANALYTICS_SUBSCRIPTION_KEY' +$piiAnalyticsId = Get-OutVal $deployOutputs 'PII_ANALYTICS_SUBSCRIPTION_ID' +$llmBackendConfig = Get-OutVal $deployOutputs 'LLM_BACKEND_CONFIG' + +# Phase 4: spoke Foundry / Key Vault / ACR +$spokeFoundryAccountNameOut = Get-OutVal $deployOutputs 'SPOKE_FOUNDRY_ACCOUNT_NAME' +$spokeFoundryProjectNameOut = Get-OutVal $deployOutputs 'SPOKE_FOUNDRY_PROJECT_NAME' +$spokeKeyVaultNameOut = Get-OutVal $deployOutputs 'SPOKE_KEY_VAULT_NAME' +$spokeAcrNameOut = Get-OutVal $deployOutputs 'SPOKE_ACR_NAME' +$spokeAcrLoginServerOut = Get-OutVal $deployOutputs 'SPOKE_ACR_LOGIN_SERVER' + +Write-Host "" +Write-Host "==================== Your Citadel Lab Environment ====================" +Write-Host " Resource group : $effectiveRG ($effectiveLocation)" +if ($hubFoundryEndpoint) { Write-Host " Hub Foundry project : $hubFoundryEndpoint" } +if ($spokeFoundryEndpoint) { Write-Host " Spoke Foundry project : $spokeFoundryEndpoint" } +if ($apimGatewayUrl) { Write-Host " APIM gateway URL : $apimGatewayUrl" } +if ($cosmosEndpoint) { Write-Host " Cosmos DB endpoint : $cosmosEndpoint" } +if ($keyVaultUrl) { Write-Host " Key Vault URL : $keyVaultUrl" } +Write-Host " Next : paste these into your notebook environment." +Write-Host "========================================================================" + +@{ HackboxCredential = @{ name = 'ResourceGroup'; value = $effectiveRG; note = 'Resource group holding Citadel lab resources. Maps to notebook azd env key AZURE_RESOURCE_GROUP' } } +@{ HackboxCredential = @{ name = 'Location'; value = $effectiveLocation; note = 'Deployed Azure region. Maps to notebook azd env key AZURE_LOCATION' } } +@{ HackboxCredential = @{ name = 'SubscriptionId'; value = $SubscriptionId; note = 'Azure subscription ID. Maps to notebook azd env key AZURE_SUBSCRIPTION_ID' } } +@{ HackboxCredential = @{ name = 'SpokeResourceGroup'; value = $effectiveRG; note = 'Single-RG deployment: same as ResourceGroup. Maps to notebook azd env key SPOKE_RESOURCE_GROUP' } } + +if ($hubFoundryEndpoint) { + @{ HackboxCredential = @{ name = 'HubFoundryProjectEndpoint'; value = $hubFoundryEndpoint; note = 'Hub Foundry project endpoint (Notebooks 1, 3, 6)' } } +} + +if ($spokeFoundryEndpoint) { + @{ HackboxCredential = @{ name = 'SpokeFoundryProjectEndpoint'; value = $spokeFoundryEndpoint; note = 'Spoke Foundry project endpoint for your agents (Notebooks 7–9)' } } +} + +if ($apimGatewayUrl) { + @{ HackboxCredential = @{ name = 'ApimGatewayUrl'; value = $apimGatewayUrl; note = 'APIM unified inference gateway (all notebooks)' } } +} + +if ($cosmosEndpoint) { + @{ HackboxCredential = @{ name = 'CosmosEndpoint'; value = $cosmosEndpoint; note = 'Cosmos DB endpoint for usage analytics (Notebook 2)' } } +} + +if ($cosmosDatabase) { + @{ HackboxCredential = @{ name = 'CosmosDatabase'; value = $cosmosDatabase; note = 'Cosmos DB database name' } } +} + +if ($keyVaultUrl) { + @{ HackboxCredential = @{ name = 'KeyVaultUrl'; value = $keyVaultUrl; note = 'Key Vault for credential retrieval (all notebooks)' } } +} + +if ($keyVaultName) { + @{ HackboxCredential = @{ name = 'KeyVaultName'; value = $keyVaultName; note = 'Key Vault name for DefaultAzureCredential' } } +} + +if ($laWorkspaceId) { + @{ HackboxCredential = @{ name = 'LogAnalyticsWorkspaceId'; value = $laWorkspaceId; note = 'Log Analytics workspace ID (Challenge 3)' } } +} + +if ($laWorkspaceName) { + @{ HackboxCredential = @{ name = 'LogAnalyticsWorkspaceName'; value = $laWorkspaceName; note = 'Log Analytics workspace name' } } +} + +if ($appInsightsConnStr) { + @{ HackboxCredential = @{ name = 'AppInsightsConnectionString'; value = $appInsightsConnStr; note = 'Application Insights connection string (Challenge 3, agent tracing)' } } +} + +if ($appInsightsKey) { + @{ HackboxCredential = @{ name = 'AppInsightsInstrumentationKey'; value = $appInsightsKey; note = 'Application Insights instrumentation key' } } +} + +if ($eventHubNamespace) { + @{ HackboxCredential = @{ name = 'EventHubNamespace'; value = $eventHubNamespace; note = 'Event Hub namespace for usage pipeline' } } +} + +if ($storageAccountName) { + @{ HackboxCredential = @{ name = 'StorageAccountName'; value = $storageAccountName; note = 'Storage account for Logic App state' } } +} + +if ($apimName) { + @{ HackboxCredential = @{ name = 'ApimName'; value = $apimName; note = 'API Management service name' } } +} + +if ($salesAssistantKey) { + @{ HackboxCredential = @{ name = 'SalesAssistantSubscriptionKey'; value = $salesAssistantKey; note = 'APIM subscription key for the Sales Assistant scenario' } } +} +if ($salesAssistantId) { + @{ HackboxCredential = @{ name = 'SalesAssistantSubscriptionId'; value = $salesAssistantId; note = 'APIM subscription ID for the Sales Assistant scenario' } } +} + +if ($hrChatAgentKey) { + @{ HackboxCredential = @{ name = 'HrChatAgentSubscriptionKey'; value = $hrChatAgentKey; note = 'APIM subscription key for the HR ChatAgent scenario' } } +} +if ($hrChatAgentId) { + @{ HackboxCredential = @{ name = 'HrChatAgentSubscriptionId'; value = $hrChatAgentId; note = 'APIM subscription ID for the HR ChatAgent scenario' } } +} + +if ($supportBotKey) { + @{ HackboxCredential = @{ name = 'SupportBotSubscriptionKey'; value = $supportBotKey; note = 'APIM subscription key for the Support Bot scenario' } } +} +if ($supportBotId) { + @{ HackboxCredential = @{ name = 'SupportBotSubscriptionId'; value = $supportBotId; note = 'APIM subscription ID for the Support Bot scenario' } } +} + +if ($universalLlmTestKey) { + @{ HackboxCredential = @{ name = 'UniversalLlmTestSubscriptionKey'; value = $universalLlmTestKey; note = 'APIM subscription key for the Universal LLM Test scenario' } } +} +if ($universalLlmTestId) { + @{ HackboxCredential = @{ name = 'UniversalLlmTestSubscriptionId'; value = $universalLlmTestId; note = 'APIM subscription ID for the Universal LLM Test scenario' } } +} + +if ($unifiedAiTestKey) { + @{ HackboxCredential = @{ name = 'UnifiedAiTestSubscriptionKey'; value = $unifiedAiTestKey; note = 'APIM subscription key for the Unified AI Test scenario' } } +} +if ($unifiedAiTestId) { + @{ HackboxCredential = @{ name = 'UnifiedAiTestSubscriptionId'; value = $unifiedAiTestId; note = 'APIM subscription ID for the Unified AI Test scenario' } } +} + +if ($piiMaskingKey) { + @{ HackboxCredential = @{ name = 'PiiMaskingSubscriptionKey'; value = $piiMaskingKey; note = 'APIM subscription key for the PII Masking scenario' } } +} +if ($piiMaskingId) { + @{ HackboxCredential = @{ name = 'PiiMaskingSubscriptionId'; value = $piiMaskingId; note = 'APIM subscription ID for the PII Masking scenario' } } +} + +if ($piiBlockingKey) { + @{ HackboxCredential = @{ name = 'PiiBlockingSubscriptionKey'; value = $piiBlockingKey; note = 'APIM subscription key for the PII Blocking scenario' } } +} +if ($piiBlockingId) { + @{ HackboxCredential = @{ name = 'PiiBlockingSubscriptionId'; value = $piiBlockingId; note = 'APIM subscription ID for the PII Blocking scenario' } } +} + +if ($piiAnalyticsKey) { + @{ HackboxCredential = @{ name = 'PiiAnalyticsSubscriptionKey'; value = $piiAnalyticsKey; note = 'APIM subscription key for the PII Analytics scenario' } } +} +if ($piiAnalyticsId) { + @{ HackboxCredential = @{ name = 'PiiAnalyticsSubscriptionId'; value = $piiAnalyticsId; note = 'APIM subscription ID for the PII Analytics scenario' } } +} + +if ($llmBackendConfig) { + @{ HackboxCredential = @{ name = 'LlmBackendConfig'; value = $llmBackendConfig; note = 'Base64-encoded LLM backend routing config. Maps to notebook azd env key LLM_BACKEND_CONFIG' } } +} + +if ($spokeFoundryAccountNameOut) { + @{ HackboxCredential = @{ name = 'SpokeFoundryAccountName'; value = $spokeFoundryAccountNameOut; note = 'Spoke Foundry account name (Notebooks 7-9)' } } + @{ HackboxCredential = @{ name = 'SpokeAiFoundryAccountName'; value = $spokeFoundryAccountNameOut; note = 'Alias of SpokeFoundryAccountName. Maps to notebook azd env key SPOKE_AI_FOUNDRY_ACCOUNT_NAME' } } +} +if ($spokeFoundryProjectNameOut) { + @{ HackboxCredential = @{ name = 'SpokeFoundryProjectName'; value = $spokeFoundryProjectNameOut; note = 'Spoke Foundry project name (Notebooks 7-9)' } } + @{ HackboxCredential = @{ name = 'SpokeAiFoundryProjectName'; value = $spokeFoundryProjectNameOut; note = 'Alias of SpokeFoundryProjectName. Maps to notebook azd env key SPOKE_AI_FOUNDRY_PROJECT_NAME' } } +} +if ($spokeKeyVaultNameOut) { + @{ HackboxCredential = @{ name = 'SpokeKeyVaultName'; value = $spokeKeyVaultNameOut; note = 'Spoke Key Vault name for DefaultAzureCredential. Maps to notebook azd env key SPOKE_KEY_VAULT_NAME' } } +} +if ($spokeAcrNameOut) { + @{ HackboxCredential = @{ name = 'SpokeAcrName'; value = $spokeAcrNameOut; note = 'Spoke Azure Container Registry name. Maps to notebook azd env key SPOKE_ACR_NAME' } } +} +if ($spokeAcrLoginServerOut) { + @{ HackboxCredential = @{ name = 'SpokeAcrLoginServer'; value = $spokeAcrLoginServerOut; note = 'Spoke Azure Container Registry login server. Maps to notebook azd env key SPOKE_ACR_LOGIN_SERVER' } } +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md b/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md new file mode 100644 index 000000000..d6c17e5c4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md @@ -0,0 +1,305 @@ +# Phase 4: Evidence-Based Requirements & Source Mapping (Source-Verified) + +**Date Generated:** 2026-08-20 +**Scope:** Citadel Agentic Governance Hub → MicroHack Adaptation +**Validation Method:** Exhaustive local file inspection (Citadel workshop cloned to `reference/ai-hub-gateway-solution-accelerator/`) +**Evidence Quality:** ALL data source-backed with exact file paths and line numbers +**Target:** Full workshop parity (Notebooks 1–9 unchanged) + +--- + +## Source Files Location + +All evidence extracted from: `reference/ai-hub-gateway-solution-accelerator/` +Inspection method: Local grep + file reading (NOT GitHub API inferences) + +--- + +## TABLE 1: Notebook Evidence & Requirements (Source-Verified) + +| Notebook # | Notebook Name | File Location | Cell Evidence | AZD Env Vars Used | Resource Names / Variables | APIM Paths/APIs Called | Model(s) Required | Cosmos Containers | Key Vault Secrets | Skip JWT/Entra Cells? | +|---|---|---|---|---|---|---|---|---|---|---| +| **1** | LLM Backend Onboarding | `workshop/1. llm-backend-onboarding-runner.ipynb` | Cells: Environment setup, backend discovery, model enumeration, test execution | AZURE_RESOURCE_GROUP, AZURE_LOCATION, LLM_BACKEND_CONFIG | `governance_hub_resource_group`, `location`, `llm_backends_config` (JSON array from LLM_BACKEND_CONFIG) | `GET /models/models`, `POST /models/chat/completions?api-version=2024-05-01-preview`, `POST /openai/deployments/{model}/chat/completions?api-version=2024-12-01-preview`, `POST /models/embeddings?api-version=2024-05-01-preview` | All (dynamic discovery via LLM_BACKEND_CONFIG) | None | None | ❌ No | +| **2** | Universal LLM API Tests | `workshop/2. citadel-universal-llm-api-all-models-tests.ipynb` | Cells: Model discovery loop, response validation, embedding tests, response API tests, input item retrieval | AZURE_RESOURCE_GROUP, AZURE_LOCATION | `governance_hub_resource_group`, `location`, Product: `LLM-Testing-UniversalLLMAllModels-DEV`, Subscription: `LLM-Testing-UniversalLLMAllModels-DEV-SUB-01`, `allowedModels = ""` (empty = all) | `GET /models/models`, `POST /models/chat/completions?api-version=2024-05-01-preview`, `POST /models/embeddings?api-version=2024-05-01-preview`, `POST /models/responses`, `GET /models/responses/{response_id}`, `GET /models/responses/{response_id}/input_items?limit=20` | All deployed models (dynamic) | None | None | ❌ No | +| **3** | Access Contracts Tests | `workshop/3. citadel-access-contracts-tests.ipynb` | Cells: Contract deployment, model allowlist validation, token limit enforcement, usage tracking setup | AZURE_RESOURCE_GROUP, AZURE_LOCATION, SPOKE_RESOURCE_GROUP, SPOKE_KEY_VAULT_NAME | Products: `LLM-Sales-Assistant-DEV` (models: gpt-4.1, gpt-5.4-mini, text-embedding-3-large; TPM: 3000; quota: 100K/month), `LLM-HR-ChatAgent-DEV` (models: gpt-4.1, gpt-5.4-mini; TPM: 5000; quota: 500K/month), `LLM-Support-Bot-DEV` (models: gpt-4.1, Mistral-Large-3; TPM: 2000; quota: 50K/day); Subscriptions: `{PRODUCT}-SUB-01` | `POST /models/chat/completions`, Policy fragments: `set-backend-pools`, `set-llm-requested-model`, `validate-model-access`, `set-llm-usage` (APIM policies applied) | gpt-4.1, gpt-5.4-mini, Mistral-Large-3, text-embedding-3-large | None | `SALES-LLM-ENDPOINT`, `SALES-LLM-KEY` (from Bicep spec) | ⚠️ **YES** — JWT token creation/validation cells | +| **4** | Agent Frameworks Tests | `workshop/4. citadel-agent-frameworks-tests.ipynb` | Cells: Agent credential initialization, Foundry project connection, model deployment retrieval, agent inference | AZURE_RESOURCE_GROUP, AZURE_LOCATION, AZURE_SUBSCRIPTION_ID, SPOKE_RESOURCE_GROUP, SPOKE_KEY_VAULT_NAME, SPOKE_AI_FOUNDRY_ACCOUNT_NAME, SPOKE_AI_FOUNDRY_PROJECT_NAME | Foundry connections: `Hub-HR-ChatAgent-DEV-LLM` (naming pattern: `Hub-{BusinessUnit}-{UseCaseName}-{Env}-{ServiceCode}`), Agent SDK config, `inference_api_version="2024-05-01-preview"` | `POST /models/chat/completions`, Uses APIMClientTool for gateway discovery, subscription key auth | gpt-4.1, gpt-5.4-mini, Mistral-Large-3 | None | `{CONTRACT}-LLM-ENDPOINT`, `{CONTRACT}-LLM-KEY` | ❌ No | +| **5** | PII Processing Tests | `workshop/5. citadel-pii-processing-tests.ipynb` | Cells: PII contract deployment, PII masking request, response validation, analytics query | AZURE_RESOURCE_GROUP, AZURE_LOCATION | Products: `LLM-HR-PIIMasking-DEV`, `LLM-Compliance-PIIBlocking-DEV`, `LLM-HR-PIIAnalytics-DEV` (with TPM/quota); Database: `ai-usage-db`; Container: `pii-usage-container` (partition key: `/type`); Policy fragments: `pii-anonymization`, `pii-deanonymization`, `pii-state-saving` | `POST /models/chat/completions?api-version=2024-05-01-preview`, POST to unified-ai routes (Bicep spec shows policy routing) | gpt-4.1 (primary) | `pii-usage-container` in `ai-usage-db` (Source: Bicep `bicep/infra/modules/cosmos-db/cosmos-db.bicep` lines 209-230) | `HR-PII-LLM-ENDPOINT`, `HR-PII-LLM-KEY`, `COMPLIANCE-LLM-ENDPOINT`, `COMPLIANCE-LLM-KEY` | ❌ No | +| **6** | Unified AI API Tests | `workshop/6. citadel-unified-ai-api-tests.ipynb` | Cells: Multi-backend deployment discovery, OpenAI format request, Foundry format request, response validation, Gemini optional, UAIG headers validation | AZURE_RESOURCE_GROUP, AZURE_LOCATION | Product: `LLM-Testing-UnifiedAI-DEV` (allowed models: openai_model, foundry_inference_model, optional gemini_model; TPM: 1000; quota: 100K/month), Defaults: `openai_model="gpt-5.2"`, `foundry_inference_model="Mistral-Large-3"`, `test_gemini=False` | `GET /unified-ai/deployments`, `GET /unified-ai/deployments/{model_name}`, `POST /unified-ai/openai/deployments/{model}/chat/completions?api-version=2024-12-01-preview`, `POST /unified-ai/models/chat/completions?api-version=2024-05-01-preview`, `POST /unified-ai/v1beta/openai/chat/completions` (Gemini if enabled), Response headers: `UAIG-Auth-Type`, `UAIG-Model-Id`, `UAIG-Backend`, etc. | gpt-5.2, Mistral-Large-3, gemini-2.5-flash-lite (optional) | None | `UNIFIED-AI-TEST-ENDPOINT`, `UNIFIED-AI-TEST-KEY` | ❌ No | +| **7** | Hosted Agent with AGT | `workshop/7. citadel-hosted-agent-with-agt.ipynb` | Cells: Agent YAML generation, Foundry deployment, instrumentation config, inference validation | AZURE_RESOURCE_GROUP, AZURE_LOCATION, AZURE_SUBSCRIPTION_ID, SPOKE_RESOURCE_GROUP, SPOKE_KEY_VAULT_NAME, SPOKE_AI_FOUNDRY_ACCOUNT_NAME, SPOKE_AI_FOUNDRY_PROJECT_NAME, SPOKE_ACR_NAME, SPOKE_ACR_LOGIN_SERVER | Foundry endpoint: `https://{account}.services.ai.azure.com/api/projects/{project}`, Agent YAML: `AZURE_AI_MODEL_DEPLOYMENT_NAME`, `APPLICATIONINSIGHTS_CONNECTION_STRING`, Container image in ACR | LLM via APIM gateway (managed identity auth from agent deployment) | gpt-4.1 | None | App Insights connection string (injected from deploy-lab.ps1 outputs) | ❌ No | +| **8** | A2A Endpoint Publishing | `workshop/8. publish-and-use-a2a-endpoint.ipynb` | Cells: Agent card publication, A2A JSON-RPC messaging, endpoint testing | AZURE_RESOURCE_GROUP, AZURE_SUBSCRIPTION_ID, LLM_BACKEND_CONFIG, env overrides: `A2A_AGENT_NAME`, `A2A_FOUNDRY_ACCOUNT_NAME`, `A2A_FOUNDRY_PROJECT_NAME`, `A2A_APIM_API_NAME`, `A2A_APIM_API_PATH` | Agent name: `hr-assistant` (default), product: `A2A-{agent-name}-DEV`, Foundry agent YAML deployment | `POST /` (JSON-RPC root), `GET /agentCard/v1.0`, `GET /agentCard/v0.3`, JSON-RPC methods: `message/send` | gpt-4.1 | None | None | ⚠️ **Partial** — MCP app role setup (Entra app registration for agent sidecar) | +| **9** | HR MCP via APIM | `workshop/9. publish-and-use-hr-mcp-via-apim.ipynb` | Cells: MCP server registration, delegated auth token acquisition, tool listing, tool invocation | AZURE_SUBSCRIPTION_ID, AZURE_RESOURCE_GROUP, HR_MCP_TENANT_ID, HR_MCP_SCOPE, HR_MCP_AUDIENCE, HR_MCP_APIM_NAME, HR_MCP_APIM_MCP_URL, HR_MCP_APIM_PATH, HR_MCP_APIM_PRODUCT_ID, SPOKE_AI_FOUNDRY_ACCOUNT_NAME, SPOKE_AI_FOUNDRY_PROJECT_NAME | API: `hr-mcp-api`, Product: `MCP-HR-Tools-DEV`, Subscription: `MCP-HR-Tools-DEV-SUB-01`, Endpoint: `{gatewayUrl}/hr-mcp/mcp`, Policy: `5 tools/call per 30 seconds`, Agent model: `Hub-HR-ChatAgent-DEV-LLM/gpt-4.1` | `POST {gatewayUrl}/hr-mcp/mcp` (JSON-RPC), Methods: `initialize`, `tools/list`, `tools/call`, Tools: `search_employees`, `get_employee_profile`, `recommend_learning_path`, `submit_pto_request`, `update_employee_skills` | gpt-4.1 | None | None | ⚠️ **YES** — Entra app registration cells (MCP app role `Mcp.Invoke` setup) | + +**Source file key:** +- Notebooks inspected: All 9 from `workshop/` directory +- Bicep models: `bicep/infra/main.bicepparam` (lines 213-321) +- Cosmos DB: `bicep/infra/modules/cosmos-db/cosmos-db.bicep` +- APIM APIs: `bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep`, `bicep/infra/modules/apim/unified-ai-api.bicep` +- Access Contracts: `bicep/infra/citadel-access-contracts/main.bicep` + +--- + +## TABLE 2: Citadel APIM/Model Deployment Source Map (Source-Verified) + +| Source File (Repo Path) | Resource/API/Policy/Product | Exact Configuration | Line(s) | Purpose | Needed by Notebook(s) | MicroHack Implementation Status | +|---|---|---|---|---|---|---| +| `bicep/infra/main.bicepparam` | Model: gpt-4.1 | Publisher: OpenAI, API Version: 2025-04-01-preview, SKU: GlobalStandard, Capacity: 100 TPM, Retirement: 2026-10-14, Deployment ID: aiserviceIndex 0,1 | 215-222 | Chat inference baseline | 1, 2, 3, 4, 5, 6, 7, 8, 9 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: gpt-5.4-mini | Publisher: OpenAI, API Version: 2026-03-17, SKU: GlobalStandard, Capacity: 100 TPM, Retirement: 2026-09-30 | 224-231 | Chat inference (optimized) | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: gpt-5.2 | Publisher: OpenAI, SKU: GlobalStandard, Capacity: 100 TPM, API Version: 2025-12-11 | 233-240 | Chat inference (newer, required by Notebook 6 Unified AI) | 6 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: DeepSeek-R1 | Publisher: DeepSeek, API Version: 2024-05-01-preview, SKU: GlobalStandard, Capacity: 1 TPM, Retirement: 2099-12-30 | 226-233 | Alternative reasoning backend | 1, 2, 6 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: Mistral-Large-3 | Publisher: Mistral AI, SKU: GlobalStandard, Capacity: 100 TPM, Retirement: 2099-12-30 | 245-252 | Alternative LLM backend (required: Notebooks 3, 4, 6) | 3, 4, 6 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: Phi-4 | Publisher: Microsoft, API Version: 2025-04-01-preview, SKU: GlobalStandard, Capacity: 1 TPM | 263-271 | Small model option | 1, 2 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/main.bicepparam` | Model: text-embedding-3-large | Publisher: OpenAI, SKU: GlobalStandard, Capacity: 100 TPM, Retirement: 2027-04-14 | 273-280 | Embedding inference | 2, 6 | ✅ **READY** — Deploy to hub Foundry | +| `bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep` | API: universal-llm-api | Path: `llm/openai`, Methods: POST/GET, Auth: api-key (header/query), Subscription key names: header=`api-key`, query=`api-key` | 41-100 | Routes: GET /models/models, POST /models/chat/completions, POST /models/embeddings, POST /models/responses, GET /models/responses/{id}, GET /models/responses/{id}/input_items | 1, 2, 3, 4, 5 | ✅ **READY** — Bicep API definition exists; port to resources.bicep APIM module | +| `bicep/infra/modules/apim/unified-ai-api.bicep` | API: unified-ai-api | Display: "Unified AI API", Path: `unified-ai`, Description: "Multi-backend routing (Azure OpenAI, AI Foundry, Gemini)", Subscription required: true | 52-72 | Routes: GET /unified-ai/deployments, POST /unified-ai/openai/deployments/{model}/chat/completions, POST /unified-ai/models/chat/completions, POST /unified-ai/v1beta/openai/chat/completions (Gemini) | 6 | ✅ **READY** — Bicep API definition exists; port to resources.bicep APIM module | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: set-backend-pools | Description: Dynamically configures backend pool definitions with model support mappings | 174-182 | Used by Universal LLM API inbound policy | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Fragment definition in Bicep; implement APIM policy XML | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: set-backend-authorization | Description: Authentication tokens for different backend types (OpenAI, AI Foundry, external) | 185-198 | Used by all APIs for auth routing | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Implement with managed identity for hub Foundry | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: set-target-backend-pool | Description: Routes requests to appropriate backend based on model and access permissions | 201-209 | Used by Universal LLM API for model routing | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Implement in APIM policy | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: set-llm-requested-model | Description: Extracts model name from Azure OpenAI deployment ID or JSON request body | 212-220 | Used by all APIs for model extraction | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Implement in APIM policy | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: set-llm-usage | Description: Collects usage metrics (token counts) to Application Insights | 223-231 | Used by Notebooks 2, 5 for usage telemetry | 2, 5 | ✅ **READY** — Implement; emit to App Insights | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: get-available-models | Description: Returns JSON list of available model deployments (implements GET /models/models endpoint) | 234-242 | Used by Notebooks 1, 2 for model discovery | 1, 2 | ✅ **READY** — Implement; return deployed model array | +| `bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep` | Policy Fragment: validate-model-access | Description: Enforces allowedModels RBAC per subscription | 246-249 | Used by Notebooks 3, 6 for access control | 3, 6 | ✅ **READY** — Implement; check requested model in product's allowedModels | +| `bicep/infra/modules/cosmos-db/cosmos-db.bicep` | Database: ai-usage-db | Container: ai-usage-container (partition key: /productName), pii-usage-container (partition key: /type), llm-usage-container (partition key: /productName), model-pricing, streaming-export-config | 40-283 | Usage tracking, PII telemetry, model metadata, export config | 2, 5 | ✅ **READY** — Cosmos DB already in resources.bicep; verify container names | +| `bicep/infra/citadel-access-contracts/main.bicep` | Product: LLM-Sales-Assistant-DEV | Allowed models: gpt-4.1, gpt-5.4-mini, text-embedding-3-large; TPM: 3000; Monthly quota: 100K tokens | From Bicep spec | Access contract for Sales use case | 3 | ✅ **READY** — Implement as APIM product + subscription | +| `bicep/infra/citadel-access-contracts/main.bicep` | Product: LLM-HR-ChatAgent-DEV | Allowed models: gpt-4.1, gpt-5.4-mini; TPM: 5000; Monthly quota: 500K tokens | From Bicep spec | Access contract for HR agent use case | 3, 4, 8, 9 | ✅ **READY** — Implement as APIM product + subscription | +| `bicep/infra/citadel-access-contracts/main.bicep` | Product: LLM-Support-Bot-DEV | Allowed models: gpt-4.1, Mistral-Large-3; TPM: 2000; Daily quota: 50K tokens | From Bicep spec | Access contract for Support bot | 3 | ✅ **READY** — Implement as APIM product + subscription | +| `bicep/infra/citadel-access-contracts/main.bicep` | Subscriptions: {PRODUCT}-SUB-01 | Auth: api-key header (`api-key`), Per-product access key | Bicep pattern | API access key for each product | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Create subscriptions for all products | +| `bicep/infra/citadel-access-contracts/main.bicep` | Product Policies: ai-product-policy.xml | Model RBAC (validate-model-access), Token limiting (llm-token-limit), Caching, PII redaction (optional) | Bicep integration | Policy enforcement at product level | 3, 5, 6 | ✅ **READY** — Implement product-level policies | +| `bicep/infra/llm-backend-onboarding/modules/llm-backends.bicep` | Backend pools: ai-foundry, azure-openai, aws-bedrock, gemini-openai, external | Auth types: managed-identity, aws-sigv4, api-key-bearer, api-key-header | Backend config section | Route models to correct backend | 1, 2, 3, 4, 5, 6 | ✅ **READY** — Implement backend definitions in APIM | +| `bicep/infra/modules/apim/` | Named Values: uami-client-id, aws-access-key, aws-secret-key, aws-region, dynamic backend keys | Auth credentials, managed identity config | APIM named values | Backend authentication | 1, 2, 3, 4, 5, 6 | ⚠️ **PARTIAL** — AWS Bedrock optional (Notebook 1 optional); implement hub Foundry MI only for MVP | + +--- + +## Implementation Readiness Tracker + +### **Critical (Notebook 1–6 blocking — MUST IMPLEMENT FOR MVP)** + +| Item | Resource | Status | Blocker | Notes | +|------|----------|--------|---------|-------| +| 7 Model Deployments | gpt-4.1, gpt-5.4-mini, gpt-5.2, DeepSeek-R1, Mistral-Large-3, Phi-4, text-embedding-3-large | ✅ Ready | None | Bicep config fully specified; deploy to hub Foundry | +| Universal LLM API | `/models/*` routes | ✅ Ready | None | Bicep module exists; port to resources.bicep APIM section | +| Unified AI API | `/unified-ai/*` routes (Notebook 6 only) | ✅ Ready | None | Bicep module exists; port to resources.bicep APIM section | +| APIM Backends | ai-foundry, azure-openai, aws-bedrock, gemini-openai | ✅ Ready (Core: hub Foundry only) | None | For MVP: hub Foundry + azure-openai only; defer AWS/Gemini | +| Policy Fragment: set-backend-pools | Backend pool config | ✅ Ready | None | Implement in APIM policy XML | +| Policy Fragment: set-backend-authorization | Auth routing | ✅ Ready | None | Use managed identity for hub Foundry | +| Policy Fragment: set-target-backend-pool | Model → backend routing | ✅ Ready | None | Implement in APIM policy XML | +| Policy Fragment: set-llm-requested-model | Model extraction | ✅ Ready | None | Implement in APIM policy XML | +| Policy Fragment: get-available-models | Model listing | ✅ Ready | None | Return deployed models array | +| Policy Fragment: validate-model-access | Model RBAC | ✅ Ready | None | Check model in product allowedModels | +| APIM Products (3) | LLM-Sales-Assistant-DEV, LLM-HR-ChatAgent-DEV, LLM-Support-Bot-DEV | ✅ Ready | None | Create with allowed models, TPM, quotas | +| APIM Subscriptions (3+) | {PRODUCT}-SUB-01 | ✅ Ready | None | Create api-key subscriptions for all products | +| Cosmos DB Containers | ai-usage-container, pii-usage-container, llm-usage-container | ✅ Ready | None | Already in resources.bicep; verify partition keys | +| HackboxCredential Outputs | Location, SubscriptionId, SpokeResourceGroup, LlmBackendConfig, Apim*, Spoke* (incl. SpokeAiFoundryAccountName/ProjectName aliases) | ✅ Ready | None | Bicep outputs + PowerShell extraction (see "Runtime Compatibility Fixes" section below) | +| APIM management RBAC (per attendee) | `API Management Service Contributor` on the APIM resource | ✅ Ready | None | Required by `APIMClientTool` (control-plane client) — see "Runtime Compatibility Fixes" below | +| ACR RBAC (per attendee, optional) | `AcrPush` on the spoke ACR | ✅ Ready | None | Required by Notebook 7's container publishing — see "Runtime Compatibility Fixes" below | +| Notebook azd env bridge | `setup-notebook-env.ps1` | ✅ Ready | None | Lets unchanged notebooks call `azd env get-value` against a PowerShell/Bicep deployment — see "Runtime Compatibility Fixes" below | + +### **High (Observability & Core Features — Required for Notebooks 2, 5, 6)** + +| Item | Resource | Status | Blocker | Notes | +|------|----------|--------|---------|-------| +| Policy Fragment: pii-anonymization, pii-deanonymization, pii-state-saving | PII masking/unmasking (Notebook 5 core) | ✅ Ready | None | **REQUIRED by Notebook 5** — Implement policy fragments for PII processing | +| Policy Fragment: set-llm-usage | Usage telemetry to App Insights (Notebooks 2, 5) | ✅ Ready | None | Emit LLM token usage and PII events to App Insights | +| Policy Fragment: set-response-headers | UAIG debug headers (Notebook 6) | ✅ Ready | None | Implement debug header injection for response validation | +| Policy Fragment: llm-token-limit | Token rate limiting (Notebooks 3, 6) | ✅ Ready | None | Implement token counting + quota enforcement | +| App Insights Integration | Connection to hub + spoke Foundry (Notebooks 2, 5, 7) | ✅ Ready | None | Already in resources.bicep; verify outputs | + +### **Medium (Notebooks 5, 6 extended features — Can defer post-MVP)** + +| Item | Resource | Status | Blocker | Notes | +|------|----------|--------|---------|-------| +| Policy Fragment: pii-anonymization, pii-deanonymization, pii-state-saving | PII masking/unmasking | ✅ Ready | None | Notebook 5 requires for PII processing; implement if time permits | +| Unified AI API: Gemini backend | Google Gemini integration | ⚠️ Ready (optional) | None | Notebook 6 uses if test_gemini=True; defer to Phase 2 | +| Unified AI API: Response API | /responses endpoints for chat history | ✅ Ready | None | Notebook 6 requires; implement with Unified AI API | + +### **Optional (Notebooks 7–9 — Workshop-provided, architecturally compatible, deferred to Phase 5)** + +| Item | Resource | Status | Blocker | Bicep Required | Notes | +|------|----------|--------|---------|---|-------| +| Notebook 7 Infrastructure | Spoke Foundry, ACR, App Insights | ✅ Ready | None | ❌ Already in core (resources.bicep) | Works with Phase 4 deployment | +| A2A Agent API | `{agent-name}-a2a` (JSON-RPC) | ✅ Ready | None | ❌ NO — Created at runtime by Notebook 8 | Notebook 8 creates API dynamically via APIM management API (lines 626–694); no Bicep changes needed; can be deferred | +| A2A Product: `A2A-{agent-name}-DEV` | Agent product + subscription | ✅ Ready | None | ❌ NO — Created at runtime | Notebook 8 creates product dynamically; no Bicep changes needed | +| MCP API (/hr-mcp) | Model Context Protocol gateway | ✅ Ready | None | ✅ YES — Optional Bicep module | Use `mcp-existing.bicep` or `mcp-from-api.bicep` (Phase 5); Notebook 9 can work without pre-deployment | +| Product: MCP-HR-Tools-DEV | MCP product with rate limit | ✅ Ready | None | ✅ YES — Optional Bicep module | Create via MCP Bicep module in Phase 5; rate limit policy: "5 tools/call per 30s" | + +### **Skipped (Not replaced, explicitly deferred)** + +| Item | Reason | Notebooks Affected | +|------|--------|-------------------| +| JWT/Entra app registration automation | Cells marked "SKIP: Use DefaultAzureCredential + RBAC" | 3, 8, 9 | +| AWS Bedrock backend (optional) | Notebook 1 backend discovery is dynamic; Bedrock is optional in original | 1 | +| Gemini backend (optional) | Notebook 6 optional when test_gemini=False; defer to Phase 2 | 6 | + +--- + +## Scope Definition: Core + Optional Workshop Path + +**Confirmed target:** Notebooks 1–6 CORE, Notebooks 7–9 OPTIONAL follow-on + +**Current evidence:** +- ✅ Notebooks 1–6: Complete source-verified evidence, all APIs/models/products documented +- ✅ Notebooks 7–9: Fully inspected; A2A and MCP infrastructure identified and are **architecturally compatible** + +### Infrastructure Analysis: Notebooks 7–9 + +**Notebook 7 (Hosted Agent):** +- Uses: Spoke Foundry, ACR (both in core deployment) +- No new APIM infrastructure required +- Status: ✅ **Works with core infrastructure** (no blocker) + +**Notebook 8 (A2A Endpoint):** +- Source: `workshop/8. publish-and-use-a2a-endpoint.ipynb` (lines 626–694) +- Infrastructure: **Dynamically created by notebook** via APIM management API +- A2A API created: `{agent-name}-a2a` (path, display name configurable) +- Bicep support: **NONE NEEDED** — created at runtime by notebook +- Prerequisites: Existing APIM, Foundry project, private endpoint setup +- Status: ✅ **Compatible** (can add anytime after core APIM deployment) + +**Notebook 9 (HR MCP):** +- Source: `workshop/9. publish-and-use-hr-mcp-via-apim.ipynb` +- Bicep modules: `bicep/infra/modules/apim/mcp-existing.bicep`, `bicep/infra/modules/apim/mcp-from-api.bicep` +- APIM API type: `'mcp'` (not REST) +- MCP properties: transportType ('streamable'), mcpTools array, protocols +- Key outputs: endpoint, path, name +- Status: ✅ **Compatible** (requires MCP Bicep module, no core infrastructure changes needed) + +### Implementation Strategy + +**PHASE 4 IMPLEMENTATION (Notebooks 1–6):** +- Deploy: Universal LLM API, Unified AI API, Access Contracts (3 products), 7 models, core policies +- Outputs: 8 new HackboxCredential values +- Bicep modules: No changes needed for A2A/MCP support yet +- Notebooks: All 9 notebooks remain unchanged; notebooks 7–9 will show "optional" challenges + +**PHASE 5 (Optional, Notebooks 7–9):** +- **Notebook 8:** Already works if APIM is deployed; notebook creates API dynamically +- **Notebook 9:** Add MCP Bicep modules (`mcp-existing.bicep` or `mcp-from-api.bicep`) to resources.bicep +- No refactoring of Phase 4 infrastructure needed + +**Architectural compatibility:** ✅ Phase 4 deployment is **fully forward-compatible** with Phase 5 A2A/MCP additions. No decisions in Phase 4 will prevent adding 7–9 later. + +--- + +## Scope Decision: Core Notebooks 1–6 First (Pending Final Approval) + +**Proposed target:** Notebooks 1–6 CORE implementation +**Optional follow-on:** Notebooks 7–9 (deferred to Phase 5, architecturally compatible, no blocking changes) + +--- + +## Summary: What Phase 4 Will Implement + +### ✅ CRITICAL (Notebooks 1–6) + +**Bicep additions to `infra/resources.bicep`:** +1. 7 Model deployments (gpt-4.1, gpt-5.4-mini, gpt-5.2, DeepSeek-R1, Mistral-Large-3, Phi-4, text-embedding-3-large) +2. APIM Universal LLM API (`/models/*`) +3. APIM Unified AI API (`/unified-ai/*`) +4. APIM backends (hub Foundry + Azure OpenAI; AWS/Gemini deferred) +5. 7 core policy fragments (set-backend-pools, set-backend-authorization, set-target-backend-pool, set-llm-requested-model, get-available-models, validate-model-access, llm-token-limit) +6. 3 products (LLM-Sales-Assistant-DEV, LLM-HR-ChatAgent-DEV, LLM-Support-Bot-DEV) with RBAC/quotas +7. Subscriptions for each product (api-key auth) +8. Cosmos DB containers (already exist; verify names) + +**PowerShell additions to `deploy-lab.ps1`:** +- New/updated HackboxCredential outputs: `Location`, `SubscriptionId`, `SpokeResourceGroup`, `LlmBackendConfig`, `SpokeFoundryAccountName` (+ `SpokeAiFoundryAccountName` alias), `SpokeFoundryProjectName` (+ `SpokeAiFoundryProjectName` alias), `SpokeKeyVaultName`, `SpokeAcrName`, `SpokeAcrLoginServer` — each annotated with the exact notebook `azd env get-value` key it maps to. +- APIM management RBAC (`API Management Service Contributor`) and ACR RBAC (`AcrPush`) grants per `$AllowedEntraUserIds` principal. +- See "Runtime Compatibility Fixes (Notebooks 1–6 azd bridge + RBAC)" below for full detail. + +### ⏸️ OPTIONAL (Notebooks 7–9, Phase 5) + +**Notebook 8 (A2A):** No Bicep changes needed; created dynamically by notebook +**Notebook 9 (MCP):** Add optional MCP Bicep modules in Phase 5 (no core changes needed) +**Notebook 7 (Hosted Agent):** Already works with core infrastructure + +### ❌ SKIPPED + +- JWT/Entra app registration automation (Notebooks 3, 8, 9 have these cells skipped; not replaced by DefaultAzureCredential. Subscription-key based APIM access continues where notebooks use it.) +- AWS Bedrock backend (optional in Notebook 1, not implemented) +- Gemini backend (optional in Notebook 6, not implemented) +- Inference API (not used by any core notebook) + +--- + +## Runtime Compatibility Fixes (Notebooks 1–6 azd bridge + RBAC) + +Two gaps were found once the notebooks were inspected end-to-end (not just at the evidence-gathering stage) and closed without editing notebook content: + +### 1. `azd env get-value` bridge — `setup-notebook-env.ps1` + +The unchanged workshop notebooks read all configuration via `azd env get-value ` +(they were written against `azd up`). This lab provisions via `deploy-lab.ps1` +(PowerShell + Bicep), so there is no azd environment for the notebooks to read. + +`setup-notebook-env.ps1` closes this gap: it creates/selects a local azd +environment (default name `citadel-microhack`) and sets exactly the keys the +notebooks need, sourced from the attendee's `HackboxCredential` dashboard values. + +Confirmed via direct inspection of all 9 notebooks' `azd_get_value`/`get_azd_value` +helper calls, the complete key set required for Notebooks 1–7 is: + +| Key | Notebook(s) | Source HackboxCredential | +|---|---|---| +| `AZURE_RESOURCE_GROUP` | 1–9 | `ResourceGroup` | +| `AZURE_LOCATION` | 1–9 | `Location` | +| `AZURE_SUBSCRIPTION_ID` | 4, 7, 8, 9 | `SubscriptionId` | +| `LLM_BACKEND_CONFIG` | 1, 8 | `LlmBackendConfig` | +| `SPOKE_RESOURCE_GROUP` | 3, 4, 7 | `SpokeResourceGroup` (single-RG deployment: equals `ResourceGroup`) | +| `SPOKE_KEY_VAULT_NAME` | 3, 4 | `SpokeKeyVaultName` | +| `SPOKE_AI_FOUNDRY_ACCOUNT_NAME` | 3, 4, 7 | `SpokeAiFoundryAccountName` (alias of `SpokeFoundryAccountName`) | +| `SPOKE_AI_FOUNDRY_PROJECT_NAME` | 3, 4, 7 | `SpokeAiFoundryProjectName` (alias of `SpokeFoundryProjectName`) | +| `SPOKE_ACR_NAME` | 7 | `SpokeAcrName` | +| `SPOKE_ACR_LOGIN_SERVER` | 7 | `SpokeAcrLoginServer` | + +Notebook 5 reads Cosmos DB details directly via `az cosmosdb list/show` (not azd). +Notebook 8's login check uses `az account show`/`az account get-access-token` +(az CLI, not azd). Notebook 9's `HR_MCP_*` keys are optional/fallback-driven and +tied to a separate MCP-publishing flow — out of scope for this bridge script. + +`azd` resolves its "current" environment by searching upward from the working +directory for the nearest `azure.yaml` (its project-root marker). The workshop's +`azure.yaml` lives one level above `workshop/` (where the notebooks run), so +`setup-notebook-env.ps1` runs all `azd env ...` commands with that directory as +its working directory (`Push-Location`/`Pop-Location`) so notebooks (cwd = +`workshop/`) resolve the same environment. + +### 2. APIM management RBAC (`API Management Service Contributor`) + +`APIMClientTool` (used by Notebooks 3–7, 9) is an ARM **control-plane** client — +it calls the APIM management API to list APIs/subscriptions and to create/update +products and subscriptions dynamically (e.g., Notebook 3's access-contract +products, Notebook 8's dynamically-created A2A API/product). A gateway +subscription key alone does not grant this; the calling principal needs an +Azure RBAC role on the APIM resource itself. + +`API Management Service Contributor` (`312a565d-c81f-4fd8-895a-4e21e48d571c`) is +the least-privileged **built-in** role that covers this: it manages the APIM +service **and** its entities (APIs, products, subscriptions, policies). The two +narrower built-in alternatives were evaluated and rejected: +- `API Management Service Operator Role` (`e022efe7-f5ba-4159-bbe4-b44f577e9b61`) — + manages the service itself only; cannot create/update products or subscriptions. +- `API Management Service Reader Role` (`71522526-b88f-4d52-b57f-d31fc3546d0d`) — + read-only; cannot support the notebooks' dynamic product/subscription creation. + +This role is granted per `$AllowedEntraUserIds` principal, scoped to the APIM +resource, idempotently (skipped if already assigned). + +### 3. ACR RBAC (`AcrPush`) — optional, for Notebook 7 + +Notebook 7 builds and pushes a container image to the spoke ACR as part of +hosted-agent deployment. `AcrPush` (`8311e382-0749-4cb8-b61a-304f252e45ec`) is +the least-privileged built-in role for that (data-plane push/pull only, no +control-plane permissions) and is granted per `$AllowedEntraUserIds` principal, +scoped to the spoke ACR, since the ACR already exists in the core deployment. + +--- + +## Next Steps (Phase 4 Implementation) + +1. ✅ **Implement Phase 4 Bicep** — Add APIM APIs, backends, policies, products, subscriptions, model deployments +2. ✅ **Update deploy-lab.ps1** — Extract 8 new HackboxCredential outputs from Bicep +3. ✅ **Validate syntax** — Run local test with `run-local.ps1` +4. ✅ **Mark Notebooks 7–9** — Add "optional challenge" labels in HackboxCredential dashboard + +**Evidence file:** ✅ **COMPLETE** — All source-backed with exact line numbers and Bicep file locations + +Ready to proceed to Phase 4 implementation. diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep new file mode 100644 index 000000000..44a075901 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep @@ -0,0 +1,1245 @@ +// ========================================================================== +// Citadel Agentic Governance Hub — resource-group scoped deployment +// Adapted from: https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator +// +// Deploys Hub + Spoke resources into a single pre-created resource group. +// Entry point: labautomation/deploy-lab.ps1 (MicroHack platform entry point). +// ========================================================================== + +targetScope = 'resourceGroup' + +@minLength(1) +@description('Azure region for all resources. Validated for Foundry model availability.') +param location string + +@minLength(1) +@description('Deterministic suffix for resource naming (DNS-safe, lowercase).') +param resourceToken string + +@description('Primary Entra user object ID for initial RBAC grants. Multi-user RBAC is applied post-deployment.') +param principalId string = '' + +@description('Principal type for RBAC assignments: User (interactive) or ServicePrincipal (CI/CD).') +@allowed(['User', 'ServicePrincipal']) +param principalType string = 'User' + +@description('Tags to apply to all resources.') +param tags object = {} + +// ===== Hub Resource Naming ===== +var hubFoundryAccountName = 'aif-hub-${resourceToken}' +var hubFoundryProjectName = 'citadel-hub-project' +var apimName = 'apim-citadel-${resourceToken}' +var cosmosAccountName = 'cos-citadel-${resourceToken}' +var eventHubNamespace = 'evh-citadel-${resourceToken}' +var eventHubName = 'usage-events' +var keyVaultName = 'kv-hub-${resourceToken}' +var laName = 'law-citadel-${resourceToken}' +var aiName = 'appi-citadel-${resourceToken}' +var storageAccountName = 'stcitadel${replace(resourceToken, '-', '')}' +var logicAppName = 'la-usage-ingestion-${resourceToken}' +var apimMIName = 'mi-apim-${resourceToken}' +var logicAppMIName = 'mi-logicapp-${resourceToken}' + +// ===== Spoke Resource Naming ===== +var spokeFoundryAccountName = 'aif-spoke-${resourceToken}' +var spokeFoundryProjectName = 'citadel-agents-project' +var spokeLaName = 'law-spoke-${resourceToken}' +var spokeAiName = 'appi-spoke-${resourceToken}' +var spokeKeyVaultName = 'kv-spoke-${resourceToken}' +var spokeAcrName = 'acrcitadel${replace(resourceToken, '-', '')}' + +// ===== Computed Values ===== +var cosmosApi = '2024-11-15' +var aiServicesApi = '2025-04-01-preview' +var cosmosDatabaseName = 'citadel-hub' +var cosmosContainers = [ + { name: 'usage-events', partitionKey: '/eventId' } + { name: 'contracts', partitionKey: '/contractId' } + { name: 'models', partitionKey: '/modelId' } + { name: 'config', partitionKey: '/configId' } +] + +// ===== Managed Identities ===== +resource apimMI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: apimMIName + location: location + tags: tags +} + +resource logicAppMI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: logicAppMIName + location: location + tags: tags +} + +// ===== Hub: Observability (Log Analytics + Application Insights) ===== +resource laHub 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: laName + location: location + tags: tags + properties: { + sku: { + name: 'PerGB2018' + } + retentionInDays: 30 + } +} + +resource aiHub 'Microsoft.Insights/components@2020-02-02' = { + name: aiName + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: laHub.id + } +} + +// ===== Hub: Key Vault ===== +resource kvHub 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: keyVaultName + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + publicNetworkAccess: 'Enabled' + } +} + +// ===== Hub: Azure AI Foundry Account (AIServices) ===== +resource hubFoundryAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { + name: hubFoundryAccountName + location: location + kind: 'AIServices' + tags: tags + sku: { + name: 'S0' + } + identity: { + type: 'SystemAssigned' + } + properties: { + customSubDomainName: hubFoundryAccountName + publicNetworkAccess: 'Enabled' + allowProjectManagement: true + } +} + +// ===== Hub: Foundry Project ===== +resource hubFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + parent: hubFoundryAccount + name: hubFoundryProjectName + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + description: 'Citadel Agentic Governance Hub - Primary project for governance demonstrations' + } +} + +// ===== Hub: Cosmos DB Account (NoSQL, serverless, keyless) ===== +resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' = { + name: cosmosAccountName + location: location + tags: tags + kind: 'GlobalDocumentDB' + identity: { + type: 'SystemAssigned' + } + properties: { + databaseAccountOfferType: 'Standard' + disableLocalAuth: true + publicNetworkAccess: 'Enabled' + capabilities: [ + { + name: 'EnableServerless' + } + ] + locations: [ + { + locationName: location + failoverPriority: 0 + } + ] + } +} + +// ===== Hub: Cosmos DB Database ===== +resource cosmosDb 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-11-15' = { + parent: cosmosAccount + name: cosmosDatabaseName + properties: { + resource: { + id: cosmosDatabaseName + } + } +} + +// ===== Hub: Cosmos DB Containers (expanded with AI Governance containers) ===== +var cosmosContainersPhase4 = concat(cosmosContainers, [ + { name: 'ai-usage-container', partitionKey: '/productName' } + { name: 'pii-usage-container', partitionKey: '/type' } + { name: 'llm-usage-container', partitionKey: '/productName' } +]) + +resource cosmosContainers_resource 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-11-15' = [for container in cosmosContainersPhase4: { + parent: cosmosDb + name: container.name + properties: { + resource: { + id: container.name + partitionKey: { + paths: [ + container.partitionKey + ] + kind: 'Hash' + } + } + } +}] + +// ========================================================================== +// PHASE 4: CITADEL AI GOVERNANCE HUB +// Model Deployments, APIM Infrastructure, Policies, Products, Subscriptions +// ========================================================================== + +// ===== Phase 4: Model Configuration ===== +// 7 models deployed to hub Foundry for Notebooks 1-6 +var aiFoundryModels = [ + { + name: 'gpt-4.1' + publisher: 'OpenAI' + version: '2025-04-01-preview' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-10-14' + } + { + name: 'gpt-5.4-mini' + publisher: 'OpenAI' + version: '2026-03-17' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-09-30' + } + { + name: 'gpt-5.2' + publisher: 'OpenAI' + version: '2025-12-11' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-02-05' + } + { + name: 'DeepSeek-R1' + publisher: 'DeepSeek' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + } + { + name: 'Mistral-Large-3' + publisher: 'Mistral AI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2099-12-30' + } + { + name: 'Phi-4' + publisher: 'Microsoft' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-10-14' + } + { + name: 'text-embedding-3-large' + publisher: 'OpenAI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-04-14' + } +] + +// ===== Phase 4: Model Deployments to Hub Foundry Account ===== +// Each model deployed to support Notebooks 1-6 +resource hubFoundryModelDeployments 'Microsoft.CognitiveServices/accounts/deployments@2025-04-01-preview' = [for (model, index) in aiFoundryModels: { + parent: hubFoundryAccount + name: replace(model.name, '.', '-') + sku: { + name: model.sku + capacity: model.capacity + } + properties: { + model: { + format: 'OpenAI' + name: model.name + version: model.version + } + } +}] + +// ===== Phase 4: APIM Named Values (Backend Authentication) ===== +// Managed identity client ID for hub Foundry backend authentication +resource apimNamedValueUamiClientId 'Microsoft.ApiManagement/service/namedValues@2023-09-01-preview' = { + parent: apim + name: 'uami-client-id' + properties: { + displayName: 'Hub Foundry UAMI Client ID' + value: apimMI.properties.clientId + secret: false + } +} + +// ===== Phase 4: APIM Backends ===== +// Hub Foundry backend with managed identity +resource apimBackendHubFoundry 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = { + parent: apim + name: 'backend-hub-foundry' + properties: { + title: 'Hub Foundry Backend' + description: 'Hub Foundry AI Services backend for model inference via managed identity' + url: 'https://${hubFoundryAccountName}.openai.azure.com' + protocol: 'http' + credentials: { + authorization: { + scheme: 'Bearer' + parameter: '@{context.Variables["hubFoundryAccessToken"]}' + } + } + circuitBreaker: { + rules: [ + { + name: 'serverErrorRule' + tripDuration: 'PT1M' + failureCondition: { + count: 5 + interval: 'PT1M' + statusCodeRanges: [ + { min: 500, max: 599 } + ] + } + } + { + name: 'clientErrorRule' + tripDuration: 'PT1M' + failureCondition: { + count: 10 + interval: 'PT1M' + statusCodeRanges: [ + { min: 400, max: 499 } + ] + } + } + ] + } + } +} + +// ===== Phase 4: APIM Policy Fragments ===== +// Policy fragment: Extract available models from deployment list +resource policyFragmentGetAvailableModels 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'get-available-models' + properties: { + format: 'xml' + value: 'application/json@{var models = new JArray(); models.Add("gpt-4.1"); models.Add("gpt-5.4-mini"); models.Add("gpt-5.2"); models.Add("DeepSeek-R1"); models.Add("Mistral-Large-3"); models.Add("Phi-4"); models.Add("text-embedding-3-large"); return models.ToString();}' + } +} + +// Policy fragment: Validate model access per subscription +resource policyFragmentValidateModelAccess 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'validate-model-access' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: Set backend pools for model routing +resource policyFragmentSetBackendPools 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'set-backend-pools' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: Set backend authorization +resource policyFragmentSetBackendAuthorization 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'set-backend-authorization' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: Set target backend pool +resource policyFragmentSetTargetBackendPool 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'set-target-backend-pool' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: Extract LLM requested model +resource policyFragmentSetLlmRequestedModel 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'set-llm-requested-model' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: Track LLM usage +resource policyFragmentSetLlmUsage 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'set-llm-usage' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: PII Anonymization +resource policyFragmentPiiAnonymization 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'pii-anonymization' + properties: { + format: 'xml' + value: '@{string body = context.Request.Body.AsString(); body = System.Text.RegularExpressions.Regex.Replace(body, @"\\b\\d{3}-\\d{2}-\\d{4}\\b", "***-**-****"); return body; }' + } +} + +// Policy fragment: PII Deanonymization +resource policyFragmentPiiDeanonymization 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'pii-deanonymization' + properties: { + format: 'xml' + value: '' + } +} + +// Policy fragment: PII State Saving +resource policyFragmentPiiStateSaving 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { + parent: apim + name: 'pii-state-saving' + properties: { + format: 'xml' + value: 'https://${cosmosAccountName}.documents.azure.com/dbs/${cosmosDatabaseName}/colls/pii-usage-container/docsPOST' + } +} + +// ===== Phase 4: APIM API - Universal LLM API ===== +// Universal endpoint for all LLM backends (/models/*) +resource apiUniversalLlm 'Microsoft.ApiManagement/service/apis@2023-09-01-preview' = { + parent: apim + name: 'universal-llm-api' + properties: { + displayName: 'Universal LLM API' + description: 'Gateway for Azure OpenAI and AI Foundry models - supports GPT, embedding, and other inference types' + path: 'models' + protocols: [ + 'https' + ] + subscriptionRequired: true + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + } +} + +// Universal LLM API: Operation - GET /models/models (list available models) +resource apiUniversalLlmOpGetModels 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'get-models' + properties: { + displayName: 'List Available Models' + method: 'GET' + urlTemplate: '/models' + description: 'Returns list of available model deployments' + } +} + +// Universal LLM API: Operation - POST /models/chat/completions +resource apiUniversalLlmOpChatCompletions 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'post-chat-completions' + properties: { + displayName: 'Create Chat Completion' + method: 'POST' + urlTemplate: '/chat/completions' + description: 'Generate chat completions using requested model' + } +} + +// Universal LLM API: Operation - POST /models/embeddings +resource apiUniversalLlmOpEmbeddings 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'post-embeddings' + properties: { + displayName: 'Create Embeddings' + method: 'POST' + urlTemplate: '/embeddings' + description: 'Generate embeddings using text-embedding-3-large' + } +} + +// Universal LLM API: Operation - POST /models/responses (response tracking) +resource apiUniversalLlmOpPostResponses 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'post-responses' + properties: { + displayName: 'Record LLM Response' + method: 'POST' + urlTemplate: '/responses' + description: 'Log LLM response for audit trail' + } +} + +// Universal LLM API: Operation - GET /models/responses/{response_id} +resource apiUniversalLlmOpGetResponse 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'get-response' + properties: { + displayName: 'Get Response' + method: 'GET' + urlTemplate: '/responses/{response_id}' + templateParameters: [ + { + name: 'response_id' + required: true + type: 'string' + description: 'Response UUID' + } + ] + description: 'Retrieve stored LLM response' + } +} + +// Universal LLM API: Operation - GET /models/responses/{response_id}/input_items +resource apiUniversalLlmOpGetResponseInputItems 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUniversalLlm + name: 'get-response-input-items' + properties: { + displayName: 'Get Response Input Items' + method: 'GET' + urlTemplate: '/responses/{response_id}/input_items' + templateParameters: [ + { + name: 'response_id' + required: true + type: 'string' + description: 'Response UUID' + } + ] + description: 'Retrieve input items for stored response' + } +} + +// ===== Phase 4: APIM API - Unified AI API ===== +// Multi-backend routing API for OpenAI, Foundry, Gemini formats +resource apiUnifiedAi 'Microsoft.ApiManagement/service/apis@2023-09-01-preview' = { + parent: apim + name: 'unified-ai-api' + properties: { + displayName: 'Unified AI API' + description: 'Multi-backend routing for Azure OpenAI, AI Foundry, and Gemini (Notebook 6 support)' + path: 'unified-ai' + protocols: [ + 'https' + ] + subscriptionRequired: true + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + } +} + +// Unified AI API: Operation - GET /unified-ai/deployments +resource apiUnifiedAiOpGetDeployments 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUnifiedAi + name: 'get-deployments' + properties: { + displayName: 'List Deployments' + method: 'GET' + urlTemplate: '/deployments' + description: 'Returns available deployments across backends' + } +} + +// Unified AI API: Operation - GET /unified-ai/deployments/{model_name} +resource apiUnifiedAiOpGetDeployment 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUnifiedAi + name: 'get-deployment' + properties: { + displayName: 'Get Deployment Details' + method: 'GET' + urlTemplate: '/deployments/{model_name}' + templateParameters: [ + { + name: 'model_name' + required: true + type: 'string' + description: 'Model deployment name' + } + ] + description: 'Get details for specific deployment' + } +} + +// Unified AI API: Operation - POST /unified-ai/openai/deployments/{model}/chat/completions +resource apiUnifiedAiOpOpenAiCompletions 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUnifiedAi + name: 'post-openai-chat-completions' + properties: { + displayName: 'OpenAI Chat Completions' + method: 'POST' + urlTemplate: '/openai/deployments/{model}/chat/completions' + templateParameters: [ + { + name: 'model' + required: true + type: 'string' + description: 'Model deployment name (gpt-5.2, Mistral-Large-3, etc.)' + } + ] + description: 'Create chat completions in OpenAI format' + } +} + +// Unified AI API: Operation - POST /unified-ai/models/chat/completions +resource apiUnifiedAiOpFoundryCompletions 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUnifiedAi + name: 'post-foundry-chat-completions' + properties: { + displayName: 'Foundry Inference API Chat Completions' + method: 'POST' + urlTemplate: '/models/chat/completions' + description: 'Create chat completions in AI Foundry inference format' + } +} + +// Unified AI API: Operation - POST /unified-ai/v1beta/openai/chat/completions (Gemini optional) +resource apiUnifiedAiOpGeminiCompletions 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { + parent: apiUnifiedAi + name: 'post-gemini-chat-completions' + properties: { + displayName: 'Gemini Chat Completions (Optional)' + method: 'POST' + urlTemplate: '/v1beta/openai/chat/completions' + description: 'Create chat completions using Google Gemini (optional, deferred to Phase 5)' + } +} + +// ===== Phase 4: APIM Products (Access Contracts) ===== +// Product 1: LLM-Sales-Assistant-DEV +resource productSalesAssistant 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-sales-assistant-dev' + properties: { + displayName: 'LLM-Sales-Assistant-DEV' + description: 'Access contract for Sales Assistant use case - Chat + embedding models (Notebook 3)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Access to gpt-4.1, gpt-5.4-mini, text-embedding-3-large at 3000 TPM' + } +} + +// Product 1: Add APIs +resource productSalesAssistantApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productSalesAssistant + name: 'universal-llm-api' + properties: {} +} + +// Product 2: LLM-HR-ChatAgent-DEV +resource productHrChatAgent 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-chatagent-dev' + properties: { + displayName: 'LLM-HR-ChatAgent-DEV' + description: 'Access contract for HR ChatAgent - High throughput agent use case (Notebooks 3, 4, 8, 9)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Access to gpt-4.1, gpt-5.4-mini at 5000 TPM with 500K monthly quota' + } +} + +// Product 2: Add APIs +resource productHrChatAgentApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productHrChatAgent + name: 'universal-llm-api' + properties: {} +} + +// Product 3: LLM-Support-Bot-DEV +resource productSupportBot 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-support-bot-dev' + properties: { + displayName: 'LLM-Support-Bot-DEV' + description: 'Access contract for Support Bot - Alternative models with daily quota (Notebook 3)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Access to gpt-4.1, Mistral-Large-3 at 2000 TPM with 50K daily quota' + } +} + +// Product 3: Add APIs +resource productSupportBotApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productSupportBot + name: 'universal-llm-api' + properties: {} +} + +// Product 4: LLM-Testing-UniversalLLMAllModels-DEV (Notebook 2 - all models) +resource productUniversalLlmTest 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-testing-universalllm-dev' + properties: { + displayName: 'LLM-Testing-UniversalLLMAllModels-DEV' + description: 'Test product with access to all deployed models (Notebook 2)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Unrestricted access to all deployed models for testing' + } +} + +// Product 4: Add APIs +resource productUniversalLlmTestApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productUniversalLlmTest + name: 'universal-llm-api' + properties: {} +} + +// Product 5: LLM-Testing-UnifiedAI-DEV (Notebook 6 - multi-backend) +resource productUnifiedAiTest 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-testing-unifiedai-dev' + properties: { + displayName: 'LLM-Testing-UnifiedAI-DEV' + description: 'Test product for unified AI multi-backend routing (Notebook 6)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Access to gpt-5.2 and Mistral-Large-3 via multi-backend routing' + } +} + +// Product 5: Add APIs +resource productUnifiedAiTestApiUnified 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productUnifiedAiTest + name: 'unified-ai-api' + properties: {} +} + +// Product 6: LLM-HR-PIIMasking-DEV (Notebook 5 - PII masking) +resource productHrPiiMasking 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-piimasking-dev' + properties: { + displayName: 'LLM-HR-PIIMasking-DEV' + description: 'PII masking and deanonymization (Notebook 5 - core feature)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'PII processing with anonymization/deanonymization for HR use case' + } +} + +// Product 6: Add APIs +resource productHrPiiMaskingApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productHrPiiMasking + name: 'universal-llm-api' + properties: {} +} + +// Product 7: LLM-Compliance-PIIBlocking-DEV (Notebook 5 - PII blocking) +resource productCompliancePiiBlocking 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-compliance-piiblocking-dev' + properties: { + displayName: 'LLM-Compliance-PIIBlocking-DEV' + description: 'PII detection and blocking policy (Notebook 5 - compliance feature)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Detects and blocks PII patterns in requests/responses' + } +} + +// Product 7: Add APIs +resource productCompliancePiiBlockingApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productCompliancePiiBlocking + name: 'universal-llm-api' + properties: {} +} + +// Product 8: LLM-HR-PIIAnalytics-DEV (Notebook 5 - PII analytics) +resource productHrPiiAnalytics 'Microsoft.ApiManagement/service/products@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-piianalytics-dev' + properties: { + displayName: 'LLM-HR-PIIAnalytics-DEV' + description: 'PII analytics and telemetry (Notebook 5 - observability)' + state: 'published' + subscriptionRequired: true + approvalRequired: false + terms: 'Tracks PII events to Cosmos DB pii-usage-container' + } +} + +// Product 8: Add APIs +resource productHrPiiAnalyticsApiUniversal 'Microsoft.ApiManagement/service/products/apis@2023-09-01-preview' = { + parent: productHrPiiAnalytics + name: 'universal-llm-api' + properties: {} +} + +// ===== Phase 4: APIM Subscriptions (API Keys per Product) ===== +// Each subscription provides subscription-key auth for its product + +// Subscription for LLM-Sales-Assistant-DEV +resource subscriptionSalesAssistant 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-sales-assistant-dev-sub-01' + properties: { + scope: productSalesAssistant.id + displayName: 'LLM-Sales-Assistant-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-HR-ChatAgent-DEV +resource subscriptionHrChatAgent 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-chatagent-dev-sub-01' + properties: { + scope: productHrChatAgent.id + displayName: 'LLM-HR-ChatAgent-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-Support-Bot-DEV +resource subscriptionSupportBot 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-support-bot-dev-sub-01' + properties: { + scope: productSupportBot.id + displayName: 'LLM-Support-Bot-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-Testing-UniversalLLMAllModels-DEV +resource subscriptionUniversalLlmTest 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-testing-universalllm-dev-sub-01' + properties: { + scope: productUniversalLlmTest.id + displayName: 'LLM-Testing-UniversalLLMAllModels-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-Testing-UnifiedAI-DEV +resource subscriptionUnifiedAiTest 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-testing-unifiedai-dev-sub-01' + properties: { + scope: productUnifiedAiTest.id + displayName: 'LLM-Testing-UnifiedAI-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-HR-PIIMasking-DEV +resource subscriptionHrPiiMasking 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-piimasking-dev-sub-01' + properties: { + scope: productHrPiiMasking.id + displayName: 'LLM-HR-PIIMasking-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-Compliance-PIIBlocking-DEV +resource subscriptionCompliancePiiBlocking 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-compliance-piiblocking-dev-sub-01' + properties: { + scope: productCompliancePiiBlocking.id + displayName: 'LLM-Compliance-PIIBlocking-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// Subscription for LLM-HR-PIIAnalytics-DEV +resource subscriptionHrPiiAnalytics 'Microsoft.ApiManagement/service/subscriptions@2023-09-01-preview' = { + parent: apim + name: 'llm-hr-piianalytics-dev-sub-01' + properties: { + scope: productHrPiiAnalytics.id + displayName: 'LLM-HR-PIIAnalytics-DEV-SUB-01' + state: 'active' + allowTracing: true + } +} + +// ===== Hub: Event Hub Namespace ===== +resource eventHubNamespace_resource 'Microsoft.EventHub/namespaces@2022-10-01-preview' = { + name: eventHubNamespace + location: location + tags: tags + sku: { + name: 'Basic' + capacity: 1 + } + properties: { + minimumTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + kafkaEnabled: false + } +} + +// ===== Hub: Event Hub ===== +resource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2022-10-01-preview' = { + parent: eventHubNamespace_resource + name: eventHubName + properties: { + messageRetentionInDays: 1 + partitionCount: 1 + } +} + +// ===== Hub: Storage Account for Logic App ===== +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: storageAccountName + location: location + tags: tags + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + minimumTlsVersion: 'TLS1_2' + publicNetworkAccess: 'Enabled' + } +} + +// ===== Hub: API Management ===== +resource apim 'Microsoft.ApiManagement/service@2023-09-01-preview' = { + name: apimName + location: location + tags: tags + sku: { + name: 'StandardV2' + capacity: 1 + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${apimMI.id}': {} + } + } + properties: { + publisherEmail: 'governance@citadel.ai' + publisherName: 'Citadel Governance' + virtualNetworkType: 'None' + publicNetworkAccess: 'Enabled' + } +} + +// ===== Hub: APIM Logger (Application Insights) ===== +resource apimLoggerAppInsights 'Microsoft.ApiManagement/service/loggers@2023-09-01-preview' = { + parent: apim + name: 'appinsights-logger' + properties: { + loggerType: 'applicationInsights' + resourceId: aiHub.id + credentials: { + instrumentationKey: aiHub.properties.InstrumentationKey + } + isBuffered: true + } +} + +// ===== Spoke: Azure AI Foundry Account (AIServices) ===== +resource spokeFoundryAccount 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { + name: spokeFoundryAccountName + location: location + kind: 'AIServices' + tags: tags + sku: { + name: 'S0' + } + identity: { + type: 'SystemAssigned' + } + properties: { + customSubDomainName: spokeFoundryAccountName + publicNetworkAccess: 'Enabled' + allowProjectManagement: true + } +} + +// ===== Spoke: Foundry Project ===== +resource spokeFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + parent: spokeFoundryAccount + name: spokeFoundryProjectName + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + description: 'Citadel Agentic Governance - Attendee agent development environment' + } +} + +// ===== Spoke: Observability (Log Analytics + Application Insights) ===== +resource laSpoke 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: spokeLaName + location: location + tags: tags + properties: { + sku: { + name: 'PerGB2018' + } + retentionInDays: 30 + } +} + +resource aiSpoke 'Microsoft.Insights/components@2020-02-02' = { + name: spokeAiName + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: laSpoke.id + } +} + +// ===== Spoke: Key Vault ===== +resource kvSpoke 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: spokeKeyVaultName + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + publicNetworkAccess: 'Enabled' + } +} + +// ===== Spoke: Azure Container Registry ===== +resource acrSpoke 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: replace(spokeAcrName, '-', '') + location: location + tags: tags + sku: { + name: 'Basic' + } + properties: { + adminUserEnabled: true + publicNetworkAccess: 'Enabled' + } +} + +// ===== Connect App Insights to Hub Foundry for Agent Tracing ===== +resource hubFoundryAppInsightsConnection 'Microsoft.CognitiveServices/accounts/connections@2025-04-01-preview' = { + parent: hubFoundryAccount + name: '${hubFoundryAccountName}-appinsights' + properties: { + category: 'AppInsights' + target: aiHub.id + authType: 'ApiKey' + isSharedToAll: true + credentials: { + key: reference(aiHub.id, '2020-02-02').InstrumentationKey + } + metadata: { + ApiType: 'Azure' + ResourceId: aiHub.id + } + } +} + +// ===== Connect App Insights to Spoke Foundry for Agent Tracing ===== +resource spokeFoundryAppInsightsConnection 'Microsoft.CognitiveServices/accounts/connections@2025-04-01-preview' = { + parent: spokeFoundryAccount + name: '${spokeFoundryAccountName}-appinsights' + properties: { + category: 'AppInsights' + target: aiSpoke.id + authType: 'ApiKey' + isSharedToAll: true + credentials: { + key: reference(aiSpoke.id, '2020-02-02').InstrumentationKey + } + metadata: { + ApiType: 'Azure' + ResourceId: aiSpoke.id + } + } +} + +// ===== Outputs (returned to deploy-lab.ps1 for HackboxCredential) ===== + +output HUB_FOUNDRY_PROJECT_ENDPOINT string = 'https://${hubFoundryAccountName}.services.ai.azure.com/api/projects/${hubFoundryProjectName}' +output SPOKE_FOUNDRY_PROJECT_ENDPOINT string = 'https://${spokeFoundryAccountName}.services.ai.azure.com/api/projects/${spokeFoundryProjectName}' + +output APIM_GATEWAY_URL string = 'https://${apim.name}.azure-api.net' + +// Phase 4: Subscription keys for products (for HackboxCredential distribution to notebooks) +output SALES_ASSISTANT_SUBSCRIPTION_KEY string = subscriptionSalesAssistant.properties.primaryKey +output SALES_ASSISTANT_SUBSCRIPTION_ID string = subscriptionSalesAssistant.name + +output HR_CHATAGENT_SUBSCRIPTION_KEY string = subscriptionHrChatAgent.properties.primaryKey +output HR_CHATAGENT_SUBSCRIPTION_ID string = subscriptionHrChatAgent.name + +output SUPPORT_BOT_SUBSCRIPTION_KEY string = subscriptionSupportBot.properties.primaryKey +output SUPPORT_BOT_SUBSCRIPTION_ID string = subscriptionSupportBot.name + +output UNIVERSAL_LLM_TEST_SUBSCRIPTION_KEY string = subscriptionUniversalLlmTest.properties.primaryKey +output UNIVERSAL_LLM_TEST_SUBSCRIPTION_ID string = subscriptionUniversalLlmTest.name + +output UNIFIED_AI_TEST_SUBSCRIPTION_KEY string = subscriptionUnifiedAiTest.properties.primaryKey +output UNIFIED_AI_TEST_SUBSCRIPTION_ID string = subscriptionUnifiedAiTest.name + +output PII_MASKING_SUBSCRIPTION_KEY string = subscriptionHrPiiMasking.properties.primaryKey +output PII_MASKING_SUBSCRIPTION_ID string = subscriptionHrPiiMasking.name + +output PII_BLOCKING_SUBSCRIPTION_KEY string = subscriptionCompliancePiiBlocking.properties.primaryKey +output PII_BLOCKING_SUBSCRIPTION_ID string = subscriptionCompliancePiiBlocking.name + +output PII_ANALYTICS_SUBSCRIPTION_KEY string = subscriptionHrPiiAnalytics.properties.primaryKey +output PII_ANALYTICS_SUBSCRIPTION_ID string = subscriptionHrPiiAnalytics.name + +// Phase 4: LLM Backend Config (JSON array for Notebook 1 dynamic discovery) +output LLM_BACKEND_CONFIG string = base64(string([ + { + name: 'gpt-4.1' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'gpt-4.1' ] + } + { + name: 'gpt-5.4-mini' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'gpt-5.4-mini' ] + } + { + name: 'gpt-5.2' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'gpt-5.2' ] + } + { + name: 'DeepSeek-R1' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'DeepSeek-R1' ] + } + { + name: 'Mistral-Large-3' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'Mistral-Large-3' ] + } + { + name: 'Phi-4' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'Phi-4' ] + } + { + name: 'text-embedding-3-large' + publisher: 'Azure OpenAI' + endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' + models: [ 'text-embedding-3-large' ] + } +])) + +output COSMOS_ENDPOINT string = cosmosAccount.properties.documentEndpoint +output COSMOS_DATABASE string = cosmosDatabaseName + +output KEY_VAULT_URL string = kvHub.properties.vaultUri +output KEY_VAULT_NAME string = kvHub.name + +output LOG_ANALYTICS_WORKSPACE_ID string = laHub.properties.customerId +output LOG_ANALYTICS_WORKSPACE_NAME string = laHub.name +output LOG_ANALYTICS_WORKSPACE_RESOURCE_ID string = laHub.id + +output APPLICATIONINSIGHTS_CONNECTION_STRING string = aiHub.properties.ConnectionString +output APPLICATIONINSIGHTS_INSTRUMENTATION_KEY string = reference(aiHub.id, '2020-02-02').InstrumentationKey + +output SPOKE_LOG_ANALYTICS_WORKSPACE_ID string = laSpoke.properties.customerId +output SPOKE_LOG_ANALYTICS_WORKSPACE_NAME string = laSpoke.name + +output SPOKE_APP_INSIGHTS_CONNECTION_STRING string = aiSpoke.properties.ConnectionString + +output EVENT_HUB_NAMESPACE string = eventHubNamespace_resource.name +output EVENT_HUB_CONNECTION_STRING string = listKeys('${eventHubNamespace_resource.id}/AuthorizationRules/RootManageSharedAccessKey', eventHubNamespace_resource.apiVersion).primaryConnectionString + +output STORAGE_ACCOUNT_NAME string = storageAccount.name +output STORAGE_ACCOUNT_CONNECTION_STRING string = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value};EndpointSuffix=core.windows.net' + +output APIM_NAME string = apim.name +output APIM_IDENTITY_PRINCIPAL_ID string = apimMI.properties.principalId + +output HUB_FOUNDRY_ACCOUNT_RESOURCE_ID string = hubFoundryAccount.id +output HUB_FOUNDRY_MANAGED_IDENTITY_PRINCIPAL_ID string = hubFoundryAccount.identity.principalId + +output SPOKE_FOUNDRY_ACCOUNT_RESOURCE_ID string = spokeFoundryAccount.id +output SPOKE_FOUNDRY_MANAGED_IDENTITY_PRINCIPAL_ID string = spokeFoundryAccount.identity.principalId + +// Phase 4: Spoke Foundry details for agent deployment (Notebook 4, 7) +output SPOKE_FOUNDRY_ACCOUNT_NAME string = spokeFoundryAccountName +output SPOKE_FOUNDRY_PROJECT_NAME string = spokeFoundryProjectName +output SPOKE_KEY_VAULT_NAME string = spokeKeyVaultName +output SPOKE_ACR_NAME string = spokeAcrName +output SPOKE_ACR_LOGIN_SERVER string = '${replace(spokeAcrName, '-', '')}.azurecr.io' + +output RESOURCE_GROUP_NAME string = resourceGroup().name +output LOCATION string = location diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/lab-defaults.json b/03-Azure/01-04-AI/05_AI_Governance/labautomation/lab-defaults.json new file mode 100644 index 000000000..cee68536a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/lab-defaults.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/MicroHack/refs/heads/main/lab-defaults-schema.json", + "groups": [], + "deploymentType": "resourcegroup", + "labsPerSubscription": 8, + "preferredLocation": "swedencentral, westeurope, norwayeast", + "estimatedDailyCostsUsd": 15.0 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/run-local.ps1 b/03-Azure/01-04-AI/05_AI_Governance/labautomation/run-local.ps1 new file mode 100644 index 000000000..55f597191 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/run-local.ps1 @@ -0,0 +1,58 @@ +# Local-only wrapper to run deploy-lab.ps1 outside the MicroHack platform. +# Supplies the platform-provided helper (Get-MhhStableHash), pre-creates the +# resource group, and passes the current signed-in user's Entra object ID. +# NOT part of the hack deliverable — for local end-to-end verification only. + +param( + # Defaults to the current Az context subscription; pass to override. + [string]$SubscriptionId = ((Get-AzContext -ErrorAction SilentlyContinue).Subscription.Id), + [string]$ResourceGroupName = "rg-agentic-governance-local", + [string]$Location = "swedencentral", + # Defaults to the current signed-in user's Entra object ID (resolved below). + [string[]]$AllowedEntraUserIds = @() +) + +$ErrorActionPreference = "Stop" + +if (-not $SubscriptionId) { + throw "No subscription in context. Run Connect-AzAccount first, or pass -SubscriptionId." +} + +# The platform passes the lab's attendee object IDs here. Run locally, this defaults +# to the signed-in user so the RBAC grants target you. +if ($AllowedEntraUserIds.Count -eq 0) { + $signedInId = (Get-AzADUser -SignedIn -ErrorAction SilentlyContinue).Id + if (-not $signedInId) { + throw "Could not resolve your Entra object ID. Pass -AllowedEntraUserIds explicitly." + } + $AllowedEntraUserIds = @($signedInId) +} + +# Platform-provided helper shim: deterministic, DNS-safe, lowercase hash. +function Get-MhhStableHash { + param([Parameter(ValueFromRemainingArguments = $true)][object[]]$InputValues, [int]$Length = 12) + $strings = @() + foreach ($v in $InputValues) { if ($v -is [array]) { $strings += $v } else { $strings += $v } } + $joined = ($strings -join '|') + if ([string]::IsNullOrEmpty($joined)) { $joined = "default" } + $sha = [System.Security.Cryptography.SHA256]::Create() + $bytes = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($joined)) + $hex = -join ($bytes | ForEach-Object { $_.ToString('x2') }) + return $hex.Substring(0, $Length) +} + +# Pre-create the resource group (the platform does this for 'resourcegroup' type). +if (-not (Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue)) { + Write-Host "[INFO] Creating resource group '$ResourceGroupName' in '$Location'..." + New-AzResourceGroup -Name $ResourceGroupName -Location $Location | Out-Null + Write-Host "[OK] Resource group created." +} else { + Write-Host "[OK] Resource group already exists." +} + +& "$PSScriptRoot\deploy-lab.ps1" ` + -DeploymentType 'resourcegroup' ` + -SubscriptionId $SubscriptionId ` + -ResourceGroupName $ResourceGroupName ` + -PreferredLocation @($Location, "westeurope", "norwayeast") ` + -AllowedEntraUserIds $AllowedEntraUserIds diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 b/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 new file mode 100644 index 000000000..9685d0c0e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 @@ -0,0 +1,162 @@ +<# +.SYNOPSIS + Bootstraps a local azd environment so the unchanged Citadel workshop + notebooks (under reference/ai-hub-gateway-solution-accelerator/workshop) + can keep calling `azd env get-value ` unmodified. + +.DESCRIPTION + The workshop notebooks were written against `azd up` and read their + configuration exclusively via `azd env get-value`. This MicroHack lab + provisions the same resources via deploy-lab.ps1 (PowerShell/Bicep, not + azd), so there is no azd environment for the notebooks to read from. + + This script closes that gap: it creates (or selects) a local azd + environment and sets exactly the keys the notebooks expect, using the + values from the attendee's HackboxCredential dashboard. + + `azd` resolves the "current" environment by searching upward from the + working directory for the nearest `azure.yaml` (its project-root marker), + the same way git finds `.git`. The workshop's `azure.yaml` lives at: + + reference/ai-hub-gateway-solution-accelerator/azure.yaml + + ...one level above the `workshop/` folder that contains the notebooks. + For `azd env get-value` calls made *inside* a notebook (cwd = workshop/) + to resolve the same environment this script sets, every `azd env ...` + command below is run with that azd project root as its working + directory. + +.PARAMETER AzdEnvironmentName + Name of the local azd environment to create/select. Defaults to + 'citadel-microhack'. +.PARAMETER ResourceGroup + Value of the HackboxCredential 'ResourceGroup' / 'SpokeResourceGroup' + (single-RG deployment: hub and spoke share one resource group). +.PARAMETER Location + Value of the HackboxCredential 'Location'. +.PARAMETER SubscriptionId + Value of the HackboxCredential 'SubscriptionId'. +.PARAMETER LlmBackendConfig + Value of the HackboxCredential 'LlmBackendConfig' (base64 JSON). +.PARAMETER SpokeKeyVaultName + Value of the HackboxCredential 'SpokeKeyVaultName'. +.PARAMETER SpokeAiFoundryAccountName + Value of the HackboxCredential 'SpokeAiFoundryAccountName' (alias of + 'SpokeFoundryAccountName'). +.PARAMETER SpokeAiFoundryProjectName + Value of the HackboxCredential 'SpokeAiFoundryProjectName' (alias of + 'SpokeFoundryProjectName'). +.PARAMETER SpokeAcrName + Value of the HackboxCredential 'SpokeAcrName'. +.PARAMETER SpokeAcrLoginServer + Value of the HackboxCredential 'SpokeAcrLoginServer'. + +.EXAMPLE + ./setup-notebook-env.ps1 ` + -ResourceGroup "rg-citadel-abc123" ` + -Location "swedencentral" ` + -SubscriptionId "00000000-0000-0000-0000-000000000000" ` + -LlmBackendConfig "" ` + -SpokeKeyVaultName "kv-spoke-abc123" ` + -SpokeAiFoundryAccountName "aif-spoke-abc123" ` + -SpokeAiFoundryProjectName "citadel-agents-project" ` + -SpokeAcrName "acrspokeabc123" ` + -SpokeAcrLoginServer "acrspokeabc123.azurecr.io" + + Then open any workshop notebook and run its `azd env get-value` cell — + no notebook changes required. +#> +param( + [string]$AzdEnvironmentName = "citadel-microhack", + + [Parameter(Mandatory = $true)] + [string]$ResourceGroup, + + [Parameter(Mandatory = $true)] + [string]$Location, + + [Parameter(Mandatory = $true)] + [string]$SubscriptionId, + + [string]$LlmBackendConfig = "", + + [string]$SpokeKeyVaultName = "", + + [string]$SpokeAiFoundryAccountName = "", + + [string]$SpokeAiFoundryProjectName = "", + + [string]$SpokeAcrName = "", + + [string]$SpokeAcrLoginServer = "" +) + +$ErrorActionPreference = "Stop" + +if (-not (Get-Command azd -ErrorAction SilentlyContinue)) { + throw "azd (Azure Developer CLI) is not installed or not on PATH. Install it first: https://aka.ms/azd-install" +} + +# The workshop's azure.yaml (azd project root) lives one level above workshop/, +# where all the notebooks live. azd resolves its environment by searching +# upward from cwd for this marker, so we must run every azd command from +# here (or a descendant of it) for notebooks to see the same environment. +$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition +$azdProjectRoot = Resolve-Path (Join-Path $scriptRoot "../reference/ai-hub-gateway-solution-accelerator") -ErrorAction SilentlyContinue + +if (-not $azdProjectRoot) { + throw "Could not resolve the azd project root at '../reference/ai-hub-gateway-solution-accelerator' relative to this script. Ensure the workshop reference folder exists." +} + +$azureYaml = Join-Path $azdProjectRoot "azure.yaml" +if (-not (Test-Path $azureYaml)) { + throw "azure.yaml not found at '$azureYaml'. The notebooks resolve their azd environment relative to this file — cannot continue." +} + +Write-Host "[INFO] Bootstrapping azd environment '$AzdEnvironmentName' for notebook use..." +Write-Host "[INFO] azd project root: $azdProjectRoot" + +Push-Location $azdProjectRoot +try { + $existingEnvs = (& azd env list --output json 2>$null | ConvertFrom-Json -ErrorAction SilentlyContinue) + $envExists = $existingEnvs | Where-Object { $_.Name -eq $AzdEnvironmentName } + + if ($envExists) { + Write-Host "[OK] azd environment '$AzdEnvironmentName' already exists — selecting it." + & azd env select $AzdEnvironmentName + if ($LASTEXITCODE -ne 0) { throw "azd env select '$AzdEnvironmentName' failed." } + } else { + Write-Host "[INFO] Creating azd environment '$AzdEnvironmentName'..." + & azd env new $AzdEnvironmentName --location $Location --subscription $SubscriptionId + if ($LASTEXITCODE -ne 0) { throw "azd env new '$AzdEnvironmentName' failed." } + Write-Host "[OK] azd environment '$AzdEnvironmentName' created." + } + + function Set-AzdValue { + param([string]$Key, [string]$Value) + if ([string]::IsNullOrWhiteSpace($Value)) { + Write-Host "[WARN] No value supplied for $Key — skipping (notebook cells needing it will fail)." + return + } + & azd env set $Key $Value | Out-Null + if ($LASTEXITCODE -ne 0) { throw "azd env set $Key failed." } + Write-Host "[OK] Set $Key" + } + + # Exact key set the workshop notebooks read via azd env get-value. + Set-AzdValue -Key "AZURE_RESOURCE_GROUP" -Value $ResourceGroup + Set-AzdValue -Key "AZURE_LOCATION" -Value $Location + Set-AzdValue -Key "AZURE_SUBSCRIPTION_ID" -Value $SubscriptionId + Set-AzdValue -Key "LLM_BACKEND_CONFIG" -Value $LlmBackendConfig + Set-AzdValue -Key "SPOKE_RESOURCE_GROUP" -Value $ResourceGroup # single-RG deployment: spoke == hub RG + Set-AzdValue -Key "SPOKE_KEY_VAULT_NAME" -Value $SpokeKeyVaultName + Set-AzdValue -Key "SPOKE_AI_FOUNDRY_ACCOUNT_NAME" -Value $SpokeAiFoundryAccountName + Set-AzdValue -Key "SPOKE_AI_FOUNDRY_PROJECT_NAME" -Value $SpokeAiFoundryProjectName + Set-AzdValue -Key "SPOKE_ACR_NAME" -Value $SpokeAcrName + Set-AzdValue -Key "SPOKE_ACR_LOGIN_SERVER" -Value $SpokeAcrLoginServer + + Write-Host "[OK] azd environment '$AzdEnvironmentName' is ready. Notebooks in workshop/ can now call 'azd env get-value ' unmodified." +} +finally { + Pop-Location +} diff --git a/03-Azure/01-04-AI/agentic-governance-migration-instructions.md b/03-Azure/01-04-AI/agentic-governance-migration-instructions.md new file mode 100644 index 000000000..69bfcbec4 --- /dev/null +++ b/03-Azure/01-04-AI/agentic-governance-migration-instructions.md @@ -0,0 +1,374 @@ +# Task: Migrate Citadel / Agentic Governance workshop into MicroHack lab format + +You are helping migrate an existing Citadel AI Governance workshop into the Microsoft MicroHack repository structure. + +## Objective + +Create the **labautomation** foundation for a new Belgium MicroHack lab focused on **Agentic Governance**. + +The goal is to recreate the same Azure resources that the original workshop provisions with `azd up`, but adapted to the MicroHack platform model: + +- Deployment must be **resource-group scoped**, not subscription scoped. +- The MicroHack platform pre-creates the resource group. +- The platform invokes `labautomation/deploy-lab.ps1`. +- The script must follow the same contract and style as existing MicroHack labs. +- Workshop notebooks should be kept and reused as-is as challenges; do not modify notebook content unless explicitly asked later. + +## Source workshop repository + +Use this public repository as the source baseline: + +- Workshop branch and folder: + https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator/tree/workshop/workshop + +- Full deployment guide reference: + https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator/blob/main/guides/full-deployment-guide.md + +Important: in the full deployment guide, use the **Existing Resource Group Deployment Execution** section as the reference for adapting deployment to RG scope. + +If GitHub Copilot cannot reliably fetch or inspect these public links, stop and ask the user to provide a local clone or downloaded copy of the workshop repository. + +## Local reference MicroHack modules + +Use these local modules as implementation style references: + +1. Inventory Planning Agentic: + `/Users/alibekjakupov/Documents/current/ai-gov-hack/MicroHack/03-Azure/01-04-AI/03_Inventory_Planning_Agentic` + +2. Agentic Contract Lifecycle Management: + `/Users/alibekjakupov/Documents/current/ai-gov-hack/MicroHack/03-Azure/01-04-AI/04_Agentic_Contract_Lifecycle_Management` + +Study especially: + +- `challenges/` +- `labautomation/` +- `labautomation/deploy-lab.ps1` +- `labautomation/lab-defaults.json` +- any local helpers such as `run-local.ps1`, `deploy.ps1`, `deploy.sh` +- README conventions + +## Required MicroHack conventions + +Follow the existing MicroHack format exactly. + +### Folder structure + +The new lab should eventually follow this pattern: + +```text +/ + README.md + challenges/ + challenge-01.md + challenge-02.md + ... + labautomation/ + README.md + deploy-lab.ps1 + lab-defaults.json + run-local.ps1 # optional but recommended for local verification + deploy.ps1 # optional local helper + deploy.sh # optional local helper + infra/ # only if needed; goal is PowerShell-first automation + images/ + walkthrough/ + src/ # only if needed by the lab +``` + +### Platform deployment script contract + +`labautomation/deploy-lab.ps1` must keep this parameter block shape: + +```powershell +param( + [Parameter(Mandatory = $true)] + [ValidateSet('subscription', 'resourcegroup', 'resourcegroup-with-subscriptionowner')] + [string]$DeploymentType, + + [Parameter(Mandatory = $true)] + [string]$SubscriptionId, + + [string]$ResourceGroupName = "", + + [string[]]$PreferredLocation = @(), + + [string[]]$AllowedEntraUserIds = @() +) +``` + +For `resourcegroup` and `resourcegroup-with-subscriptionowner`: + +- Do not call `Connect-AzAccount`. +- Do not create the resource group. +- Assume the platform has already set the Azure context. +- Assume the resource group exists. +- Deploy all resources into `$ResourceGroupName`. + +For `subscription` mode: + +- Support only if consistent with existing reference labs. +- Prefer resource-group mode as the primary path. + +### lab-defaults.json + +Create a `labautomation/lab-defaults.json` similar to the reference labs: + +```json +{ + "$schema": "https://raw.githubusercontent.com/microsoft/MicroHack/refs/heads/main/lab-defaults-schema.json", + "groups": [], + "deploymentType": "resourcegroup", + "labsPerSubscription": 8, + "preferredLocation": "swedencentral, westeurope, norwayeast", + "estimatedDailyCostsUsd": 5.0 +} +``` + +Adjust cost and labs-per-subscription only if the Citadel resources require it. + +## Workshop-specific requirements + +The Norway workshop baseline is good. Preserve the notebooks. + +The workshop currently includes these notebooks: + +```text +1. llm-backend-onboarding-runner.ipynb +2. citadel-universal-llm-api-all-models-tests.ipynb +3. citadel-access-contracts-tests.ipynb +4. citadel-agent-frameworks-tests.ipynb +5. citadel-pii-processing-tests.ipynb +6. citadel-unified-ai-api-tests.ipynb +7. citadel-hosted-agent-with-agt.ipynb +8. publish-and-use-a2a-endpoint.ipynb +9. publish-and-use-hr-mcp-via-apim.ipynb +``` + +These notebooks should become the basis for the MicroHack `challenges/` material, but initially focus on **labautomation**. + +Important: the user believes the notebooks are good and should not be modified for now. + +## JWT / Entra ID requirement + +Skip the JWT-specific parts. + +The workshop troubleshooting says: + +> For JWT tests: ensure Entra ID setup has been completed + +For this MicroHack migration, ignore / skip JWT and Entra ID setup sections. Do not build lab automation that depends on attendee-specific Entra app registration for JWT testing unless explicitly requested later. + +## Deployment goal + +Recreate what the workshop's `azd up` deploys, but via MicroHack-compatible PowerShell automation. + +The deployment should provision the Citadel Governance Hub equivalent resources, including resources such as: + +- API Management +- Azure AI Foundry / Azure AI Services accounts and projects +- model deployments +- Cosmos DB +- Event Hub +- Key Vault +- Log Analytics +- Application Insights +- Logic App / workflow for usage ingestion if part of the original deployment +- managed identities +- RBAC assignments required for APIM, Logic App, Foundry, Key Vault, Cosmos, Event Hub +- networking resources if required by the workshop baseline + +Also account for the sample Foundry spoke deployment that the workshop currently performs after `azd up` with: + +```bash +./workshop/scripts/deploy-spoke-foundry.sh --spoke-suffix 1 +``` + +or PowerShell equivalent: + +```powershell +.\workshop\scripts\deploy-spoke-foundry.ps1 -SpokeSuffix 1 +``` + +Determine whether the MicroHack labautomation should deploy the spoke in the same platform run or expose it as a separate helper. Prefer a smooth attendee experience: resources needed for notebooks should already exist after platform provisioning where possible. + +## Existing workshop structure to inspect + +From the workshop source, inspect: + +```text +azure.yaml +bicep/ +bicep/infra/ +workshop/readme.md +workshop/scripts/ +workshop/scripts/deploy-spoke-foundry.ps1 +workshop/scripts/deploy-spoke-foundry.sh +workshop/scripts/cleanup-apim-defaults.ps1 +workshop/scripts/import-model-pricing.py +workshop/scripts/model-pricing.json +workshop/requirements.txt +workshop/pyproject.toml +workshop/governed-agent/ +workshop/hosted-agent/ +workshop/mcp-hr/ +``` + +The key task is to understand the Bicep/azd deployment graph and reproduce it in PowerShell or wrap RG-scoped deployment correctly. + +## Methodological plan + +Proceed in phases. Do not jump directly into editing. + +### Phase 1 - Inspect references + +1. Read the two local reference labs' `labautomation` folders. +2. Identify the exact MicroHack platform conventions: + - parameter contract + - output format + - `HackboxCredential` objects + - region fallback pattern + - RBAC pattern + - local `run-local.ps1` pattern + - error handling style +3. Read the workshop deployment guide, especially Existing Resource Group Deployment Execution. +4. Read the workshop Bicep/azd entry points and deployment scripts. +5. Produce a short internal map of: + - resources deployed by `azd up` + - outputs needed by notebooks + - resources deployed by the spoke script + - environment variables consumed by notebooks + +### Phase 2 - Decide deployment approach + +Choose the safest implementation strategy: + +Preferred options, in order: + +1. **Resource-group scoped PowerShell orchestration using Az modules / ARM REST**, following the Inventory lab style. +2. **PowerShell wrapper around resource-group-scoped Bicep**, if converting all Bicep manually would be risky. +3. Avoid subscription-scope deployment unless absolutely necessary. + +The user asked that the Bicep script be transformed to PowerShell scripts and placed in `labautomation`. That means the final attendee/platform entry point must be `deploy-lab.ps1`, even if some implementation still invokes RG-scoped templates internally. + +Be explicit if full Bicep-to-PowerShell conversion is too risky in one step; prefer a reliable phased migration. + +### Phase 3 - Create labautomation + +Create: + +```text +labautomation/ + README.md + deploy-lab.ps1 + lab-defaults.json + run-local.ps1 +``` + +Optional: + +```text +labautomation/deploy.ps1 +labautomation/deploy.sh +labautomation/infra/ +``` + +Only create `infra/` if necessary. + +`deploy-lab.ps1` should: + +1. Accept the MicroHack platform parameters. +2. Resolve candidate regions from `$PreferredLocation`, falling back to: + - `swedencentral` + - `westeurope` + - `norwayeast` +3. Use `$ResourceGroupName` for resourcegroup mode. +4. Generate deterministic DNS-safe suffixes using `Get-MhhStableHash`. +5. Deploy resources idempotently. +6. Assign required RBAC to every user in `$AllowedEntraUserIds`. +7. Avoid secrets where possible. +8. Output clear `[INFO]`, `[OK]`, `[WARN]` messages. +9. Return dashboard values using `@{ HackboxCredential = ... }`. + +### Phase 4 - Dashboard outputs + +Return all values attendees need to run the unchanged notebooks. + +Likely outputs include, depending on what the notebooks require: + +- ResourceGroupName +- APIM gateway endpoint +- APIM name +- APIM subscription key or instructions to retrieve it safely +- Foundry project endpoint +- Foundry account/project names +- model deployment names +- Cosmos DB endpoint/name +- Event Hub namespace/name +- Key Vault name +- Application Insights connection string +- Log Analytics workspace name +- spoke resource group name +- spoke Foundry project endpoint + +Inspect the notebooks before finalizing this list. Do not guess. + +### Phase 5 - Respect no-JWT scope + +Search for JWT-related setup, tests, variables, or app registration requirements. + +Do not automate JWT/Entra app setup unless absolutely required for non-JWT parts. + +If notebooks contain JWT cells, later challenge instructions should tell attendees to skip those cells/sections. + +### Phase 6 - Validate + +Validation should include: + +1. Static review of PowerShell syntax. +2. Local dry-run support through `run-local.ps1`. +3. Confirm script can run in `resourcegroup` mode. +4. Confirm it does not require subscription-level deployment scope except for role assignments the platform grants. +5. Confirm all `HackboxCredential` outputs are emitted. +6. Confirm all notebook-required values can be discovered from Azure or emitted to dashboard. + +Do not claim success without verification. + +## Coding style requirements + +Follow the style of the reference labs: + +- PowerShell 7 compatible. +- `$ErrorActionPreference = "Stop"`. +- Use helper functions for ARM REST calls if using `Invoke-AzRestMethod`. +- Throw on non-2xx control plane responses. +- Use idempotent create-or-update patterns. +- Use full `https://management.azure.com/...?...api-version=...` URIs if REST is used. +- Avoid broad silent catches. +- Use `[INFO]`, `[OK]`, `[WARN]` output prefixes. +- Never emit secrets in logs unless they are intended dashboard credentials. +- Use `HackboxCredential` output objects for attendee dashboard values. +- Keep platform parameter contract unchanged. + +## Important project context + +This is for a Belgium MicroHack on **Agentic Governance**. + +The user is adapting previous Norway workshop materials. + +Meeting context: + +- Deployment must be automated. +- Scope must be resource group, not subscription. +- The full deployment guide's Existing Resource Group Deployment Execution is the reference. +- The workshop notebooks are good and should be reused. +- The initial implementation focus is `labautomation`. + +## Ask before proceeding if blocked + +If any of these are true, stop and ask the user: + +- You cannot access the workshop repository contents from GitHub. +- You cannot inspect the local reference labs. +- You cannot identify which Bicep files/resources `azd up` deploys. +- You need the user to provide a local clone of the workshop repo. +- You are unsure where the new Agentic Governance module should be created. From 773ac7887351186548fc9b91f4cfa1fc47352804 Mon Sep 17 00:00:00 2001 From: ajakupov-microsoft Date: Tue, 25 Aug 2026 17:29:50 +0200 Subject: [PATCH 2/2] working pwshl script, notebooks migrated --- 03-Azure/01-04-AI/05_AI_Governance/README.md | 5 +- .../05_AI_Governance/challenges/azure.yaml | 12 + .../challenges/bicep/infra/abbreviations.json | 137 + .../infra/apim-gateway-upgrade/README.md | 176 + .../infra/apim-gateway-upgrade/main.bicep | 774 + .../apim-gateway-upgrade/main.bicepparam | 154 + .../infra/citadel-access-contracts/README.md | 851 + .../citadel-access-contracts-policy.md | 666 + .../infra/citadel-access-contracts/main.bicep | 206 + .../citadel-access-contracts/main.bicepparam | 383 + .../modules/apimOnboardService.bicep | 79 + .../modules/apimProduct.bicep | 60 + .../modules/apimSubscription.bicep | 35 + .../modules/foundryConnection.bicep | 179 + .../modules/kvSecrets.bicep | 24 + .../policies/default-ai-product-policy.xml | 42 + .../bicep/infra/entra-id-setup/README.md | 163 + .../bicep/infra/entra-id-setup/setup.ps1 | 429 + .../bicep/infra/foundry-integration/README.md | 320 + .../foundry-integration/connection-apim.bicep | 207 + .../infra/foundry-integration/main.bicep | 282 + .../infra/foundry-integration/main.bicepparam | 57 + .../modules/apim-connection-common.bicep | 96 + .../samples/custom-auth.bicepparam | 92 + .../samples/custom-headers.bicepparam | 74 + .../samples/dynamic-discovery.bicepparam | 67 + .../samples/full-config.bicepparam | 144 + .../parameters-custom-auth-config.json | 54 + .../samples/parameters-custom-headers.json | 53 + .../samples/parameters-dynamic-discovery.json | 42 + .../samples/parameters-static-models.json | 57 + .../samples/static-models.bicepparam | 84 + .../infra/llm-backend-onboarding/README.md | 465 + .../infra/llm-backend-onboarding/main.bicep | 233 + .../llm-backend-onboarding/main.bicepparam | 239 + .../modules/llm-backend-pools.bicep | 142 + .../modules/llm-backends.bicep | 122 + .../modules/llm-policy-fragments.bicep | 323 + .../policies/frag-get-available-models.xml | 72 + .../modules/policies/frag-metadata-config.xml | 107 + .../frag-responses-id-cache-store.xml | 75 + .../policies/frag-responses-id-security.xml | 224 + .../frag-set-backend-authorization.xml | 279 + .../policies/frag-set-backend-pools.xml | 33 + .../policies/frag-set-llm-requested-model.xml | 107 + .../modules/policies/frag-set-llm-usage.xml | 27 + .../policies/frag-set-target-backend-pool.xml | 271 + .../policies/frag-validate-model-access.xml | 63 + .../policies/models-inference-openapi.json | 243 + .../policies/universal-llm-api-policy.xml | 81 + .../policies/universal-llm-openapi.json | 330 + .../modules/universal-llm-api.bicep | 118 + .../challenges/bicep/infra/main.bicep | 1159 + .../challenges/bicep/infra/main.bicepparam | 335 + .../infra/main.parameters.dev.bicepparam | 56 + .../infra/main.parameters.prod.bicepparam | 115 + .../infra/modules/apic/apic-metadata.json | 74 + .../bicep/infra/modules/apic/apic.bicep | 221 + .../apic/remote-mcp-servers-SAMPLES.json | 58 + .../modules/apic/remote-mcp-servers.json | 3 + .../bicep/infra/modules/apic/servers.json | 110 + .../infra/modules/apim/README-llm-backends.md | 345 + .../ai-model-inference-api-spec.yaml | 816 + .../Azure AI Search Service API.openapi.yaml | 7505 ++++++ .../ai-search-api/ai-search-api-spec.yaml | 1639 ++ .../ai-search-index-2024-07-01-api-spec.json | 2112 ++ ...ai-search-service-2024-07-01-api-spec.json | 11547 +++++++++ .../apim/api-center-onboarding-all.bicep | 364 + .../modules/apim/api-center-onboarding.bicep | 163 + .../bicep/infra/modules/apim/api.bicep | 226 + .../bicep/infra/modules/apim/apim.bicep | 938 + ...ligence-2024-11-30-compressed.openapi.yaml | 3067 +++ .../document-intelligence-2024-11-30.json | 4807 ++++ ...ument-intelligence-2024-11-30.openapi.yaml | 3712 +++ .../infra/modules/apim/inference-api.bicep | 242 + .../modules/apim/inference-backend.bicep | 118 + .../language-api/language-api-2024-11-01.json | 8304 ++++++ .../modules/apim/llm-backend-pools.bicep | 150 + .../infra/modules/apim/llm-backends.bicep | 152 + .../modules/apim/llm-policy-fragments.bicep | 314 + .../modules/apim/llm-policy-fragments.json | 158 + .../infra/modules/apim/mcp-existing.bicep | 84 + .../infra/modules/apim/mcp-from-api.bicep | 103 + .../openai-api/OpenAI-Import.openapi.yaml | 13731 ++++++++++ .../openai-api/oai-api-spec-2024-02-01.yaml | 2320 ++ .../oai-api-spec-2024-05-01-preview.yaml | 8635 +++++++ .../openai-api/oai-api-spec-2024-06-01.yaml | 2409 ++ .../openai-api/oai-api-spec-2024-10-21.yaml | 2622 ++ .../apim/openai-api/oai-realtime-api-ws.json | 48 + .../ai-model-inference-api-policy.xml | 19 + .../policies/ai-search-index-api-policy.xml | 19 + .../policies/ai-search-service-api-policy.xml | 19 + .../policies/azure-open-ai-api-policy.xml | 122 + .../policies/doc-intelligence-api-policy.xml | 87 + .../apim/policies/frag-aad-auth-custom.xml | 45 + .../modules/apim/policies/frag-aad-auth.xml | 26 + .../frag-ai-foundry-compatibility.xml | 20 + .../policies/frag-ai-foundry-deployments.xml | 69 + .../modules/apim/policies/frag-ai-usage.xml | 32 + .../policies/frag-central-cache-manager.xml | 106 + .../modules/apim/policies/frag-entra-auth.xml | 45 + .../policies/frag-get-available-models.xml | 72 + .../modules/apim/policies/frag-llm-usage.xml | 63 + .../apim/policies/frag-metadata-config.xml | 107 + .../policies/frag-openai-usage-streaming.xml | 68 + .../apim/policies/frag-openai-usage.xml | 47 + .../apim/policies/frag-path-builder.xml | 199 + .../apim/policies/frag-pii-anonymization.xml | 275 + .../policies/frag-pii-deanonymization.xml | 20 + .../apim/policies/frag-pii-state-saving.xml | 143 + .../policies/frag-raise-throttling-events.xml | 19 + .../apim/policies/frag-request-processor.xml | 345 + .../frag-responses-id-cache-store.xml | 75 + .../policies/frag-responses-id-security.xml | 224 + .../apim/policies/frag-security-handler.xml | 220 + .../frag-set-backend-authorization.xml | 279 + .../apim/policies/frag-set-backend-pools.xml | 33 + .../policies/frag-set-llm-requested-model.xml | 107 + .../apim/policies/frag-set-llm-usage.xml | 27 + .../policies/frag-set-response-headers.xml | 89 + .../policies/frag-set-target-backend-pool.xml | 271 + .../policies/frag-strip-backend-headers.xml | 77 + .../apim/policies/frag-throttling-events.xml | 15 + .../policies/frag-validate-model-access.xml | 63 + .../apim/policies/mcp-default-policy.xml | 18 + .../apim/policies/openai-realtime-policy.xml | 42 + .../apim/policies/openai_api_policy.xml | 151 + .../openai_api_policy_dynamic_throttling.xml | 165 + .../policies/product-llm-oauth-access.xml | 46 + .../apim/policies/translator-api-policy.xml | 24 + ...ified-ai-api-deployment-by-name-policy.xml | 72 + .../unified-ai-api-deployments-policy.xml | 32 + .../apim/policies/unified-ai-api-policy.xml | 191 + .../unified-ai-product-subscription.xml | 33 + ...rsal-llm-api-deployment-by-name-policy.xml | 76 + .../universal-llm-api-deployments-policy.xml | 32 + .../policies/universal-llm-api-policy-v2.xml | 95 + .../infra/modules/apim/policy-fragments.bicep | 181 + .../modules/apim/sample/weather/openapi.json | 120 + .../modules/apim/sample/weather/policy.xml | 44 + .../apim/speech-api/speech-api-3-1.json | 6473 +++++ .../translator-api/translator-api-spec.yaml | 879 + .../infra/modules/apim/unified-ai-api.bicep | 214 + .../unified-ai-api/UnifiedAIWildcard.json | 132 + .../universal-llm-api/AIFoundryAzureAI.json | 2363 ++ .../universal-llm-api/AIFoundryOpenAI.json | 16648 ++++++++++++ .../universal-llm-api/AIFoundryOpenAIV1.json | 20744 +++++++++++++++ .../apim/universal-llm-api/PassThrough.json | 105 + .../Universal-LLM-API.openapi.yaml | 21443 ++++++++++++++++ .../Universal-LLM-Basic-API.openapi.yaml | 1685 ++ .../bicep/infra/modules/azure-roles.json | 10 + .../infra/modules/cosmos-db/cosmos-db.bicep | 282 + .../infra/modules/event-hub/event-hub.bicep | 140 + .../infra/modules/foundry/deployments.bicep | 26 + .../bicep/infra/modules/foundry/foundry.bicep | 332 + .../modules/functionapp/functionapp.bicep | 142 + .../modules/functionapp/storageaccount.bicep | 166 + .../modules/keyvault/keyvault-rbac.bicep | 36 + .../modules/keyvault/keyvault-secret.bicep | 25 + .../infra/modules/keyvault/keyvault.bicep | 151 + .../logicapp/api-connection-access.bicep | 23 + .../modules/logicapp/api-connection.json | 61 + .../infra/modules/logicapp/logicapp.bicep | 212 + .../applicationinsights-dashboard.bicep | 1235 + .../modules/monitor/applicationinsights.bicep | 52 + .../infra/modules/monitor/loganalytics.bicep | 51 + .../infra/modules/monitor/monitoring.bicep | 145 + .../bicep/infra/modules/networking/dns.bicep | 10 + .../private-endpoint-multi-dns.bicep | 86 + .../modules/networking/private-endpoint.bicep | 69 + .../infra/modules/networking/subnet.bicep | 15 + .../modules/networking/vnet-existing.bicep | 41 + .../bicep/infra/modules/networking/vnet.bicep | 352 + .../bicep/infra/modules/redis/redis.bicep | 185 + .../security/managed-identity-apim.bicep | 54 + .../security/managed-identity-usage.bicep | 41 + .../challenges/challenge-01.md | 2 +- .../challenges/challenge-02.md | 2 +- .../challenges/challenge-03.md | 2 +- .../challenges/challenge-04.md | 2 +- .../challenges/challenge-05.md | 2 +- .../challenges/challenge-06.md | 2 +- .../challenges/challenge-07.md | 2 +- .../challenges/challenge-08.md | 2 +- .../challenges/challenge-09.md | 2 +- .../challenges/shared/apimtools.py | 330 + .../challenges/shared/requirements.txt | 38 + .../challenges/shared/snippets/README.md | 36 + .../shared/snippets/api-http-requests.py | 63 + .../shared/snippets/create-az-deployment.py | 37 + .../snippets/create-az-resource-group.py | 10 + .../shared/snippets/deployment-outputs.py | 52 + .../shared/snippets/openai-api-requests.py | 30 + .../challenges/shared/utils.py | 424 + .../1. llm-backend-onboarding-runner.ipynb | 1224 + ...l-universal-llm-api-all-models-tests.ipynb | 820 + .../3. citadel-access-contracts-tests.ipynb | 1225 + .../4. citadel-agent-frameworks-tests.ipynb | 1543 ++ .../5. citadel-pii-processing-tests.ipynb | 2178 ++ .../6. citadel-unified-ai-api-tests.ipynb | 1299 + .../7. citadel-hosted-agent-with-agt.ipynb | 1339 + .../8. publish-and-use-a2a-endpoint.ipynb | 1181 + .../9. publish-and-use-hr-mcp-via-apim.ipynb | 820 + .../workshop/governed-agent/Dockerfile | 12 + .../workshop/governed-agent/agent.yaml | 17 + .../workshop/governed-agent/governance.py | 72 + .../workshop/governed-agent/main.py | 115 + .../policies/governance-policy.yaml | 25 + .../workshop/governed-agent/requirements.txt | 4 + .../workshop/guided-workshop-prompt.md | 108 + .../workshop/hosted-agent/Dockerfile | 18 + .../workshop/hosted-agent/agent.yaml | 19 + .../challenges/workshop/hosted-agent/main.py | 383 + .../hosted-agent/policies/hr-policy.yaml | 43 + .../workshop/hosted-agent/requirements.txt | 13 + .../workshop/mcp-hr/hosted-agent/Dockerfile | 26 + .../workshop/mcp-hr/hosted-agent/agent.yaml | 29 + .../workshop/mcp-hr/hosted-agent/main.py | 97 + .../mcp-hr/hosted-agent/requirements.txt | 13 + .../mcp-hr/hr-mcp-implementation-plan.md | 338 + .../workshop/mcp-hr/infra/README.md | 53 + .../workshop/mcp-hr/infra/main.bicep | 165 + .../infra/policies/hr-mcp-api-policy.xml | 18 + .../infra/policies/hr-mcp-product-policy.xml | 120 + .../workshop/mcp-hr/scripts/deploy-hr-mcp.ps1 | 1205 + .../workshop/mcp-hr/scripts/deploy-hr-mcp.sh | 1105 + .../mcp-hr/scripts/publish-hr-mcp-apim.ps1 | 173 + .../mcp-hr/scripts/publish-hr-mcp-apim.sh | 191 + .../mcp-hr/scripts/teardown-hr-mcp.ps1 | 275 + .../mcp-hr/scripts/teardown-hr-mcp.sh | 299 + .../mcp-hr/scripts/test-hr-mcp-apim.py | 517 + .../mcp-hr/scripts/test-hr-mcp-direct.py | 317 + .../workshop/mcp-hr/server/.dockerignore | 7 + .../workshop/mcp-hr/server/Dockerfile | 25 + .../challenges/workshop/mcp-hr/server/auth.py | 92 + .../workshop/mcp-hr/server/hr_data.py | 368 + .../workshop/mcp-hr/server/pyproject.toml | 12 + .../workshop/mcp-hr/server/requirements.txt | 213 + .../workshop/mcp-hr/server/server.py | 194 + .../workshop/mcp-hr/server/telemetry.py | 41 + .../challenges/workshop/mcp-hr/server/uv.lock | 1160 + .../challenges/workshop/pyproject.toml | 24 + .../challenges/workshop/readme.md | 994 + .../challenges/workshop/requirements.txt | 331 + .../scripts/cleanup-apim-defaults.ps1 | 165 + .../workshop/scripts/cleanup-apim-defaults.sh | 130 + .../workshop/scripts/deploy-spoke-foundry.ps1 | 494 + .../workshop/scripts/deploy-spoke-foundry.sh | 455 + .../workshop/scripts/import-model-pricing.py | 44 + .../workshop/scripts/model-pricing.json | 93 + .../workshop/scripts/run-all-notebooks.ps1 | 79 + .../workshop/scripts/run-all-notebooks.sh | 84 + .../challenges/workshop/uv.lock | 3197 +++ .../docs/phase4-evidence-tables.md | 4 +- .../labautomation/infra/resources.bicep | 153 +- .../labautomation/infra/resources.json | 1356 + .../labautomation/setup-notebook-env.ps1 | 10 +- 257 files changed, 195804 insertions(+), 75 deletions(-) create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/azure.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/abbreviations.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/citadel-access-contracts-policy.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimOnboardService.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimProduct.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimSubscription.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/foundryConnection.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/kvSecrets.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/policies/default-ai-product-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/setup.ps1 create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/connection-apim.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/modules/apim-connection-common.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-auth.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-headers.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/dynamic-discovery.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/full-config.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-auth-config.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-headers.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-dynamic-discovery.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-static-models.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/static-models.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backend-pools.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backends.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-get-available-models.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-metadata-config.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-cache-store.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-security.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-authorization.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-pools.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-requested-model.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-usage.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-target-backend-pool.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-validate-model-access.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/models-inference-openapi.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-openapi.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.dev.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.prod.bicepparam create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic-metadata.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers-SAMPLES.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/servers.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/README-llm-backends.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-model-inference/ai-model-inference-api-spec.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/Azure AI Search Service API.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-api-spec.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-index-2024-07-01-api-spec.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-service-2024-07-01-api-spec.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding-all.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/apim.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-api.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-backend.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/language-api/language-api-2024-11-01.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backend-pools.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backends.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-existing.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-from-api.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/OpenAI-Import.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-02-01.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-05-01-preview.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-06-01.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-10-21.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-realtime-api-ws.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-model-inference-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-index-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-service-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/azure-open-ai-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/doc-intelligence-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth-custom.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-compatibility.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-deployments.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-usage.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-central-cache-manager.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-entra-auth.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-get-available-models.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-llm-usage.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-metadata-config.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage-streaming.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-path-builder.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-anonymization.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-deanonymization.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-state-saving.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-raise-throttling-events.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-request-processor.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-cache-store.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-security.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-security-handler.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-authorization.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-pools.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-requested-model.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-usage.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-response-headers.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-target-backend-pool.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-strip-backend-headers.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-throttling-events.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-validate-model-access.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/mcp-default-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai-realtime-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy_dynamic_throttling.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/product-llm-oauth-access.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/translator-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployment-by-name-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployments-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-product-subscription.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployments-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-policy-v2.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policy-fragments.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/openapi.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/speech-api/speech-api-3-1.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/translator-api/translator-api-spec.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api/UnifiedAIWildcard.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryAzureAI.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAI.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAIV1.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/PassThrough.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-API.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-Basic-API.openapi.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/azure-roles.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/cosmos-db/cosmos-db.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/event-hub/event-hub.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/deployments.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/foundry.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/functionapp.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/storageaccount.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-rbac.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-secret.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection-access.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/logicapp.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights-dashboard.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/loganalytics.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/monitoring.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/dns.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint-multi-dns.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/subnet.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet-existing.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/redis/redis.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-apim.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-usage.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/apimtools.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/api-http-requests.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-deployment.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-resource-group.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/deployment-outputs.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/openai-api-requests.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/shared/utils.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/1. llm-backend-onboarding-runner.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/3. citadel-access-contracts-tests.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/4. citadel-agent-frameworks-tests.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/5. citadel-pii-processing-tests.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/6. citadel-unified-ai-api-tests.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/7. citadel-hosted-agent-with-agt.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/8. publish-and-use-a2a-endpoint.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/Dockerfile create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/agent.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/governance.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/main.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/policies/governance-policy.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/guided-workshop-prompt.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/Dockerfile create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/agent.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/main.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/policies/hr-policy.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/Dockerfile create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/agent.yaml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/main.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hr-mcp-implementation-plan.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/README.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/main.bicep create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-api-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-product-policy.xml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.sh create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.sh create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.sh create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-apim.py create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-direct.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/.dockerignore create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/Dockerfile create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/auth.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/hr_data.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/pyproject.toml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/server.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/telemetry.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/uv.lock create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/pyproject.toml create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/readme.md create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/requirements.txt create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.sh create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.sh create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/import-model-pricing.py create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/model-pricing.json create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.ps1 create mode 100755 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.sh create mode 100644 03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/uv.lock create mode 100644 03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.json diff --git a/03-Azure/01-04-AI/05_AI_Governance/README.md b/03-Azure/01-04-AI/05_AI_Governance/README.md index fef789a11..d85a1d432 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/README.md +++ b/03-Azure/01-04-AI/05_AI_Governance/README.md @@ -81,8 +81,9 @@ To complete this MicroHack you'll need: can bridge those credentials into a local `azd` environment the (unchanged) workshop notebooks read via `azd env get-value`. - The Azure CLI (`az`), logged in (`az login`) against your lab's subscription/tenant. -- The workshop's Python environment set up (`uv sync` or `pip install -r requirements.txt` - from the `reference/ai-hub-gateway-solution-accelerator/workshop/` folder). +- The workshop's Python environment set up (`uv sync` — see + [`challenges/workshop/readme.md`](challenges/workshop/readme.md) — or + `pip install -r requirements.txt` from the `challenges/workshop/` folder). - Comfort running and reading Jupyter notebooks in VS Code. > [!TIP] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/azure.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/azure.yaml new file mode 100644 index 000000000..e76000e09 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/azure.yaml @@ -0,0 +1,12 @@ +# Application name +name: ai-citadel-governance-hub +metadata: + template: ai-citadel-governance-hub +infra: + path: bicep/infra + provider: bicep +services: + usageProcessingLogicApp: + project: ./src/usage-ingestion-logicapp + language: js + host: function \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/abbreviations.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/abbreviations.json new file mode 100644 index 000000000..75b17bceb --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/abbreviations.json @@ -0,0 +1,137 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "apiCenterService": "apic-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "privateEndpoints": "pe-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/README.md new file mode 100644 index 000000000..053140167 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/README.md @@ -0,0 +1,176 @@ +# APIM Gateway Upgrade + +As the AI Governance Hub solution accelerator evolves, you may need to update the API Management gateway configuration to add new APIs, modify policies, or adjust logging settings. + +This deployment allows you to seamlessly apply updates to an existing APIM instance without needing to re-provision the entire service or associated infrastructure. + +Notes: +- Update the configuration of an **existing** API Management instance without re-provisioning the APIM service or surrounding landing-zone infrastructure. +- This deployment is designed to run **after** the main accelerator (`bicep/infra/main.bicep`) has provisioned the full environment. + +## What this deployment updates + +| Category | Resources Updated | +|---|---| +| **Policy Fragments** | All static fragments (auth, usage, throttling, PII, AI Foundry, Unified AI) and dynamic LLM fragments (backend pools, authorization, target routing, model access) | +| **APIs** | Universal LLM API, Azure OpenAI API, Unified AI Wildcard API, Azure AI Search API, OpenAI Realtime WebSocket API, Document Intelligence APIs — including OpenAPI specs, API-level policies, and operation-level policies | +| **LLM Backends** | Backend definitions, backend pools, and associated policy fragments for dynamic model routing | +| **Named Values** | UAMI client ID, Entra auth flag, client/tenant/audience, PII service URL/key, Content Safety URL, JWT authentication values (TenantId, AppRegistrationId, Issuer, OpenIdConfigUrl) | +| **Logging / Diagnostics** | APIM-level Application Insights diagnostic configuration and per-API Azure Monitor diagnostic configuration | +| **Redis Cache** | APIM cache entity backed by Azure Managed Redis (for semantic caching) | +| **Embeddings Backend** | APIM backend targeting AI Foundry embeddings endpoint (for semantic caching) | + +>**NOTE**: It is very important depending on the gab between the newer gateway implementation and the existing one, try to make the initial run of this upgrade deployment with as many feature flags turned **on** as possible to ensure the APIM instance is fully updated, this will mean that backend routing configurations must be in place as well. Use this in non-production environment first to detect any potential issues before applying to production. + +## What this deployment does NOT do + +- Provision a new APIM service instance +- Create or modify loggers (Application Insights logger and Azure Monitor logger must already exist) +- Configure Event Hub loggers +- Publish to API Center +- Deploy sample MCP servers +- Modify networking, VNet, or private endpoint settings +- Create managed identities, Key Vaults, or other infrastructure +- Provision Azure Managed Redis (the Redis instance must already exist for cache updates) + +## Prerequisites + +1. The APIM service referenced by `apimServiceName` must already exist in the target resource group. +2. The user-assigned managed identity referenced by `managedIdentityName` must already exist in the same resource group. +3. The following APIM loggers must be pre-provisioned on the APIM instance: + - `appinsights-logger` — Application Insights logger + - `azuremonitor` — Azure Monitor logger +4. Azure CLI authenticated with permissions to deploy to the target resource group. + +## Usage + +### 1. Configure Parameters + +Edit `main.bicepparam` to match your environment: + +```bicep +param apimServiceName = 'my-apim-instance' +param managedIdentityName = 'my-apim-identity' +``` + +### 2. Toggle Feature Flags + +Enable or disable individual configuration sections by setting the feature flag parameters to `true` or `false`: + +```bicep +// Only update policies and logging, skip API definitions +param updatePolicyFragments = true +param updateUniversalLLMApi = false +param updateAzureOpenAIApi = false +param updateUnifiedAiApi = false +param updateAppInsightsDiagnostics = true +param updateNamedValues = false +param updateJwtNamedValues = false +``` + +### 3. Configure LLM Backends + +Provide the LLM backend configuration array that matches your current environment: + +```bicep +param llmBackendConfig = [ + { + backendId: 'azure-openai-swedencentral' + backendType: 'azure-openai' + endpoint: 'https://my-openai.openai.azure.com' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-08-06' } + ] + priority: 1 + weight: 100 + } +] +``` + +### 4. Deploy + +Run the deployment scoped to the resource group containing the APIM instance: + +```bash +az deployment group create --name gateway-upgrade-$(date +%Y%m%d%H%M) --resource-group --template-file main.bicep --parameters main.bicepparam +``` + +## When to use this + +| Scenario | Use this deployment? | +|---|---| +| Updated API policies (routing logic, rate limiting, auth) | **Yes** | +| Changed LLM backend configuration (new models, endpoints) | **Yes** | +| Updated policy fragments (usage tracking, PII, throttling) | **Yes** | +| Tuning Application Insights or Azure Monitor log capture | **Yes** | +| Updating named values (audience, PII URL, Content Safety URL) | **Yes** | +| Adding a new LLM backend or model to existing pools | **Yes** | +| Updating JWT authentication configuration | **Yes** | +| Updating Unified AI Wildcard API | **Yes** | +| Updating Redis cache or embeddings backend config | **Yes** | +| Initial environment provisioning | No — use `bicep/infra/main.bicep` | +| Changing APIM SKU, networking, or VNet configuration | No — use `bicep/infra/main.bicep` | +| Adding Event Hub loggers | No — use `bicep/infra/main.bicep` | +| Publishing to API Center | No — use `bicep/infra/main.bicep` | + +## Parameter Reference + +### Core Parameters + +| Parameter | Required | Description | +|---|---|---| +| `apimServiceName` | Yes | Name of the existing APIM instance | +| `managedIdentityName` | Yes | Name of the existing user-assigned managed identity | + +### Feature Flags + +| Parameter | Default | Description | +|---|---|---| +| `updatePolicyFragments` | `true` | Update static policy fragments | +| `updateUniversalLLMApi` | `true` | Update Universal LLM API spec and policy | +| `updateAzureOpenAIApi` | `true` | Update Azure OpenAI API spec and policy | +| `updateUnifiedAiApi` | `true` | Update Unified AI Wildcard API, product, and policy | +| `updateAzureAISearchApi` | `false` | Update Azure AI Search API spec and policy | +| `updateOpenAIRealtimeApi` | `false` | Update OpenAI Realtime WebSocket API | +| `updateDocumentIntelligenceApi` | `false` | Update Document Intelligence APIs | +| `updateAppInsightsDiagnostics` | `true` | Update APIM-level App Insights diagnostics | +| `updateNamedValues` | `true` | Update APIM named values | +| `updateJwtNamedValues` | `true` | Update JWT authentication named values | +| `updateLLMBackends` | `true` | Update LLM backend definitions | +| `updateLLMBackendPools` | `true` | Update LLM backend pools | +| `updateLLMPolicyFragments` | `true` | Update dynamic LLM policy fragments | +| `updateRedisCache` | `false` | Update APIM Redis cache entity | +| `updateEmbeddingsBackend` | `false` | Update APIM embeddings backend | + +### Feature-Specific Parameters + +| Parameter | Default | Description | +|---|---|---| +| `enablePIIAnonymization` | `true` | Enable PII anonymization policy fragments | +| `enableAIModelInference` | `true` | Enable AI model inference fragments | +| `entraAuth` | `false` | Use Entra ID auth (disables subscription keys) | +| `enableUnifiedAiApi` | `true` | Enable the Unified AI Wildcard API | +| `enableJwtAuth` | `false` | Enable JWT authentication named values and security-handler fragment | +| `jwtTenantId` | `''` | JWT Tenant ID (required when `enableJwtAuth` is true) | +| `jwtAppRegistrationId` | `''` | JWT App Registration Client ID (required when `enableJwtAuth` is true) | +| `enableRedisCache` | `false` | Enable APIM Redis cache entity (requires `redisCacheConnectionString`) | +| `enableEmbeddingsBackend` | `false` | Enable APIM embeddings backend (requires `embeddingsBackendUrl`) | + +### Logging Settings + +| Parameter | Description | +|---|---| +| `azureMonitorLogSettings` | Frontend/backend headers & body bytes, LLM log settings for Azure Monitor | +| `appInsightsLogSettings` | Headers to capture and body bytes for Application Insights | + +## File structure + +``` +apim-gateway-upgrade/ +├── main.bicep # Deployment template (resource group scope) +├── main.bicepparam # Parameter file — configure before deploying +└── README.md # This guide +``` + +All API specs, policy XML files, and sub-modules are referenced from `../modules/apim/` — no duplication of policy or spec files. diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicep new file mode 100644 index 000000000..54d4f56e6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicep @@ -0,0 +1,774 @@ +/** + * @module apim-gateway-upgrade + * @description Updates the configuration of an existing APIM instance provisioned by the main + * AI Hub Gateway Solution Accelerator. This deployment targets policy fragments, + * API definitions & policies, logging/diagnostics, and named values — without + * re-provisioning the APIM service or any surrounding infrastructure. + * + * Scope: Resource Group (the RG that already contains the APIM instance) + * + * Prerequisites: + * - APIM service already exists in the target resource group + * - Application Insights logger ('appinsights-logger') already exists on the APIM instance + * - Azure Monitor logger ('azuremonitor') already exists on the APIM instance + * - User-assigned managed identity already exists (for backend auth) + */ + +targetScope = 'resourceGroup' + +// ===================================================================== +// CORE PARAMETERS +// ===================================================================== + +@description('Name of the existing API Management service to update') +param apimServiceName string + +@description('Name of the existing user-assigned managed identity used by APIM for backend auth') +param managedIdentityName string + +// ===================================================================== +// FEATURE FLAGS — control which configuration sections are applied +// ===================================================================== + +@description('Update policy fragments on the APIM instance') +param updatePolicyFragments bool = true + +@description('Update the Universal LLM API definition and policy') +param updateUniversalLLMApi bool = true + +@description('Update the Azure OpenAI API definition and policy') +param updateAzureOpenAIApi bool = true + +@description('Update the Azure AI Search API definition and policy') +param updateAzureAISearchApi bool = false + +@description('Update the OpenAI Realtime WebSocket API definition and policy') +param updateOpenAIRealtimeApi bool = false + +@description('Update the Document Intelligence API definitions and policies') +param updateDocumentIntelligenceApi bool = false + +@description('Update APIM-level Application Insights diagnostic configuration') +param updateAppInsightsDiagnostics bool = true + +@description('Update APIM named values (auth, Entra, PII, Content Safety)') +param updateNamedValues bool = true + +@description('Update LLM backend definitions') +param updateLLMBackends bool = true + +@description('Update LLM backend pools') +param updateLLMBackendPools bool = true + +@description('Update LLM policy fragments (set-backend-pools, set-backend-authorization, etc.)') +param updateLLMPolicyFragments bool = true + +@description('Update the Unified AI Wildcard API definition, product, and policy') +param updateUnifiedAiApi bool = true + +@description('Update JWT authentication named values (JWT-TenantId, JWT-AppRegistrationId, JWT-Issuer, JWT-OpenIdConfigUrl)') +param updateJwtNamedValues bool = true + +@description('Update APIM Redis cache entity configuration') +param updateRedisCache bool = false + +@description('Update APIM embeddings backend configuration') +param updateEmbeddingsBackend bool = false + +// ===================================================================== +// FEATURE-SPECIFIC PARAMETERS +// ===================================================================== + +@description('Enable PII Anonymization policy fragments') +param enablePIIAnonymization bool = true + +@description('Enable AI Model Inference policy fragments') +param enableAIModelInference bool = true + +@description('Use Entra ID authentication (disables subscription key requirement on APIs)') +param entraAuth bool = false + +@description('Enable the Unified AI Wildcard API (3rd API alongside Azure OpenAI and Universal LLM)') +param enableUnifiedAiApi bool = true + +@description('Enable JWT authentication support across all APIs (creates JWT named values and security-handler fragment)') +param enableJwtAuth bool = false + +@description('JWT Tenant ID (required when enableJwtAuth is true)') +param jwtTenantId string = '' + +@description('JWT App Registration Client ID (required when enableJwtAuth is true)') +param jwtAppRegistrationId string = '' + +// ===================================================================== +// NAMED VALUE PARAMETERS +// ===================================================================== + +@description('Client App ID for Entra-based APIM authentication') +param clientAppId string = ' ' + +@description('Tenant ID for Entra ID authentication') +param tenantId string = tenant().tenantId + +@description('OAuth audience for backend service authentication') +param audience string = 'https://cognitiveservices.azure.com/.default' + +@description('URL of the Azure AI Language service (used for PII)') +param aiLanguageServiceUrl string = '' + +@description('URL of the Azure Content Safety service') +param contentSafetyServiceUrl string = '' + +// ===================================================================== +// LLM BACKEND CONFIGURATION +// ===================================================================== + +@description('Configuration array for LLM backends supporting multiple providers and models') +param llmBackendConfig array = [] + +// ===================================================================== +// REDIS CACHE & EMBEDDINGS CONFIGURATION +// ===================================================================== + +@description('Enable APIM cache configuration backed by Azure Managed Redis') +param enableRedisCache bool = false + +@secure() +@description('Runtime connection string to the Azure Managed Redis instance (used for APIM caches)') +param redisCacheConnectionString string = '' + +@description('APIM cache entity name for the Redis-backed cache') +param apimRedisCacheName string = 'redis-cache' + +@description('Enable an APIM backend that targets the AI Foundry embeddings endpoint') +param enableEmbeddingsBackend bool = false + +@description('URL for the AI Foundry embeddings endpoint') +param embeddingsBackendUrl string = '' + +@description('APIM backend ID for the embeddings backend') +param embeddingsBackendId string = 'foundry-embeddings' + +// ===================================================================== +// AZURE AI SEARCH CONFIGURATION +// ===================================================================== + +@description('Array of AI Search instances to register as backends (only used when updateAzureAISearchApi is true)') +param aiSearchInstances array = [] + +// ===================================================================== +// LOGGING / DIAGNOSTICS PARAMETERS +// ===================================================================== + +@description('Azure Monitor diagnostic log settings for inference APIs') +param azureMonitorLogSettings object = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +@description('Application Insights diagnostic log settings for inference APIs') +param appInsightsLogSettings object = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} + +// ===================================================================== +// EXISTING RESOURCE REFERENCES +// ===================================================================== + +resource apimService 'Microsoft.ApiManagement/service@2024-05-01' existing = { + name: apimServiceName +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: managedIdentityName +} + +// Reference existing loggers (assumed already provisioned) +resource appInsightsLogger 'Microsoft.ApiManagement/service/loggers@2024-05-01' existing = { + name: 'appinsights-logger' + parent: apimService +} + +resource azMonitorLogger 'Microsoft.ApiManagement/service/loggers@2024-05-01' existing = { + name: 'azuremonitor' + parent: apimService +} + +// ===================================================================== +// NAMED VALUES +// ===================================================================== + +resource uamiNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'uami-client-id' + parent: apimService + properties: { + displayName: 'uami-client-id' + secret: true + value: managedIdentity.properties.clientId + } +} + +resource entraAuthNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'entra-auth' + parent: apimService + properties: { + displayName: 'entra-auth' + secret: false + value: string(entraAuth) + } +} + +resource clientIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'client-id' + parent: apimService + properties: { + displayName: 'client-id' + secret: true + value: clientAppId + } +} + +resource tenantIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'tenant-id' + parent: apimService + properties: { + displayName: 'tenant-id' + secret: true + value: tenantId + } +} + +resource audienceNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'audience' + parent: apimService + properties: { + displayName: 'audience' + secret: true + value: audience + } +} + +resource piiServiceUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues && enablePIIAnonymization) { + name: 'piiServiceUrl' + parent: apimService + properties: { + displayName: 'piiServiceUrl' + secret: false + value: aiLanguageServiceUrl + } +} + +resource piiServiceKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues && enablePIIAnonymization) { + name: 'piiServiceKey' + parent: apimService + properties: { + displayName: 'piiServiceKey' + secret: true + value: 'replace-with-language-service-key-if-needed' + } +} + +resource contentSafetyServiceUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = if (updateNamedValues) { + name: 'contentSafetyServiceUrl' + parent: apimService + properties: { + displayName: 'contentSafetyServiceUrl' + secret: false + value: contentSafetyServiceUrl + } +} + +// ===================================================================== +// CONTENT SAFETY BACKEND +// ===================================================================== + +resource contentSafetyBackend 'Microsoft.ApiManagement/service/backends@2024-05-01' = if (updateNamedValues && !empty(contentSafetyServiceUrl)) { + name: 'content-safety-backend' + parent: apimService + properties: { + description: 'Content Safety Service Backend' + url: contentSafetyServiceUrl + protocol: 'http' + tls: { + validateCertificateChain: true + validateCertificateName: true + } + credentials: { + managedIdentity: { + clientId: managedIdentity.properties.clientId + resource: 'https://cognitiveservices.azure.com' + } + } + } +} + +// ===================================================================== +// JWT AUTHENTICATION NAMED VALUES +// ===================================================================== + +var jwtTenantIdValue = !empty(jwtTenantId) ? jwtTenantId : subscription().tenantId +var jwtAppRegIdValue = !empty(jwtAppRegistrationId) ? jwtAppRegistrationId : 'not-configured' + +resource jwtTenantIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = if (updateJwtNamedValues) { + name: 'JWT-TenantId' + parent: apimService + properties: { + displayName: 'JWT-TenantId' + value: enableJwtAuth ? jwtTenantIdValue : 'not-configured' + } +} + +resource jwtAppRegistrationIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = if (updateJwtNamedValues) { + name: 'JWT-AppRegistrationId' + parent: apimService + properties: { + displayName: 'JWT-AppRegistrationId' + value: enableJwtAuth ? jwtAppRegIdValue : 'not-configured' + } +} + +resource jwtIssuerNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = if (updateJwtNamedValues) { + name: 'JWT-Issuer' + parent: apimService + properties: { + displayName: 'JWT-Issuer' + value: enableJwtAuth ? '${environment().authentication.loginEndpoint}${jwtTenantIdValue}/v2.0' : 'not-configured' + } +} + +resource jwtOpenIdConfigUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = if (updateJwtNamedValues) { + name: 'JWT-OpenIdConfigUrl' + parent: apimService + properties: { + displayName: 'JWT-OpenIdConfigUrl' + value: enableJwtAuth ? '${environment().authentication.loginEndpoint}${jwtTenantIdValue}/v2.0/.well-known/openid-configuration' : 'not-configured' + } +} + +// ===================================================================== +// REDIS CACHE & EMBEDDINGS BACKEND +// ===================================================================== + +resource redisCache 'Microsoft.ApiManagement/service/caches@2024-06-01-preview' = if (updateRedisCache && enableRedisCache) { + name: apimRedisCacheName + parent: apimService + properties: { + connectionString: redisCacheConnectionString + useFromLocation: 'default' + description: 'Azure Managed Redis cache for APIM Semantic Cache' + } +} + +resource embeddingsBackend 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = if (updateEmbeddingsBackend && enableEmbeddingsBackend) { + name: embeddingsBackendId + parent: apimService + properties: { + description: 'AI Foundry embeddings backend' + url: embeddingsBackendUrl + protocol: 'http' + credentials: { + managedIdentity: { + clientId: managedIdentity.properties.clientId + resource: 'https://cognitiveservices.azure.com' + } + } + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +} + +// ===================================================================== +// POLICY FRAGMENTS (static — from policy-fragments.bicep) +// ===================================================================== + +module policyFragments '../modules/apim/policy-fragments.bicep' = if (updatePolicyFragments) { + name: 'apim-policy-fragments-upgrade' + params: { + apimServiceName: apimService.name + enablePIIAnonymization: enablePIIAnonymization + enableAIModelInference: enableAIModelInference + enableUnifiedAiApi: enableUnifiedAiApi + } + dependsOn: [ + clientIdNamedValue + entraAuthNamedValue + audienceNamedValue + tenantIdNamedValue + piiServiceUrlNamedValue + piiServiceKeyNamedValue + jwtTenantIdNamedValue + jwtAppRegistrationIdNamedValue + jwtIssuerNamedValue + jwtOpenIdConfigUrlNamedValue + ] +} + +// ===================================================================== +// LLM BACKENDS, POOLS & DYNAMIC POLICY FRAGMENTS +// ===================================================================== + +module llmBackends '../modules/apim/llm-backends.bicep' = if (updateLLMBackends) { + name: 'llm-backends-upgrade' + params: { + apimServiceName: apimService.name + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + configureCircuitBreaker: true + } +} + +module llmBackendPools '../modules/apim/llm-backend-pools.bicep' = if (updateLLMBackendPools && updateLLMBackends) { + name: 'llm-backend-pools-upgrade' + params: { + apimServiceName: apimService.name + backendDetails: llmBackends.outputs.backendDetails + } +} + +module llmPolicyFragments '../modules/apim/llm-policy-fragments.bicep' = if (updateLLMPolicyFragments && updateLLMBackendPools && updateLLMBackends) { + name: 'llm-policy-fragments-upgrade' + params: { + apimServiceName: apimService.name + policyFragmentConfig: llmBackendPools.outputs.policyFragmentConfig + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + } +} + +// ===================================================================== +// AI SEARCH BACKENDS +// ===================================================================== + +resource aiSearchBackends 'Microsoft.ApiManagement/service/backends@2022-08-01' = [for (instance, i) in aiSearchInstances: if (updateAzureAISearchApi) { + name: instance.name + parent: apimService + properties: { + description: instance.description + url: instance.url + protocol: 'http' + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +}] + +// ===================================================================== +// APIs — Inference APIs (Universal LLM & Azure OpenAI) +// ===================================================================== + +module apiUniversalLLM '../modules/apim/inference-api.bicep' = if (updateUniversalLLMApi) { + name: 'universal-llm-api-upgrade' + params: { + apiManagementName: apimService.name + inferenceAPIName: 'universal-llm-api' + inferenceAPIPath: '' + inferenceAPIType: 'AzureAI' + inferenceAPIDisplayName: 'Universal LLM API' + inferenceAPIDescription: 'Universal LLM API to route requests to different LLM providers including Azure OpenAI, AI Foundry and 3rd party models.' + allowSubscriptionKey: entraAuth ? false : true + apimLoggerId: azMonitorLogger.id + policyXml: loadTextContent('../modules/apim/policies/universal-llm-api-policy-v2.xml') + azureMonitorLogSettings: azureMonitorLogSettings + appInsightsLogSettings: appInsightsLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + ] +} + +module apimOpenaiApi '../modules/apim/inference-api.bicep' = if (updateAzureOpenAIApi) { + name: 'azure-openai-api-upgrade' + params: { + apiManagementName: apimService.name + inferenceAPIName: 'azure-openai-api' + inferenceAPIPath: '' + inferenceAPIType: 'AzureOpenAI' + inferenceAPIDisplayName: 'Azure OpenAI API' + inferenceAPIDescription: 'Azure OpenAI API to route requests to different LLM providers including Azure OpenAI, AI Foundry and 3rd party models.' + allowSubscriptionKey: entraAuth ? false : true + apimLoggerId: azMonitorLogger.id + policyXml: loadTextContent('../modules/apim/policies/azure-open-ai-api-policy.xml') + azureMonitorLogSettings: azureMonitorLogSettings + appInsightsLogSettings: appInsightsLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + ] +} + +// ===================================================================== +// APIs — Unified AI Wildcard API +// ===================================================================== + +module apiUnifiedAI '../modules/apim/unified-ai-api.bicep' = if (updateUnifiedAiApi) { + name: 'unified-ai-api-upgrade' + params: { + apiManagementName: apimService.name + enabled: enableUnifiedAiApi + apimLoggerId: azMonitorLogger.id + azureMonitorLogSettings: azureMonitorLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + ] +} + +// ===================================================================== +// APIs — AI Foundry Deployment Operations (operation-level policies) +// ===================================================================== + +// Universal LLM API operation policies +resource universalLLMApi 'Microsoft.ApiManagement/service/apis@2022-08-01' existing = if (updateUniversalLLMApi) { + name: 'universal-llm-api' + parent: apimService + dependsOn: [ + apiUniversalLLM + ] +} + +resource universalLlmDeploymentOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = if (updateUniversalLLMApi) { + name: 'deployments' + parent: universalLLMApi +} + +resource universalLlmDeploymentByNameOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = if (updateUniversalLLMApi) { + name: 'deployment-by-name' + parent: universalLLMApi +} + +resource universalLlmDeploymentOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = if (updateUniversalLLMApi) { + name: 'policy' + parent: universalLlmDeploymentOperation + properties: { + format: 'rawxml' + value: loadTextContent('../modules/apim/policies/universal-llm-api-deployments-policy.xml') + } +} + +resource universalLlmDeploymentByNameOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = if (updateUniversalLLMApi) { + name: 'policy' + parent: universalLlmDeploymentByNameOperation + properties: { + format: 'rawxml' + value: loadTextContent('../modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml') + } +} + +// Azure OpenAI API operation policies +resource openAIApi 'Microsoft.ApiManagement/service/apis@2022-08-01' existing = if (updateAzureOpenAIApi) { + name: 'azure-openai-api' + parent: apimService + dependsOn: [ + apimOpenaiApi + ] +} + +resource openAIDeploymentOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = if (updateAzureOpenAIApi) { + name: 'deployments' + parent: openAIApi +} + +resource openAIDeploymentByNameOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = if (updateAzureOpenAIApi) { + name: 'deployment-by-name' + parent: openAIApi +} + +resource openAIDeploymentOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = if (updateAzureOpenAIApi) { + name: 'policy' + parent: openAIDeploymentOperation + properties: { + format: 'rawxml' + value: loadTextContent('../modules/apim/policies/universal-llm-api-deployments-policy.xml') + } +} + +resource openAIDeploymentByNameOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = if (updateAzureOpenAIApi) { + name: 'policy' + parent: openAIDeploymentByNameOperation + properties: { + format: 'rawxml' + value: loadTextContent('../modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml') + } +} + +// ===================================================================== +// APIs — Azure AI Search +// ===================================================================== + +module apimAiSearchIndexApi '../modules/apim/api.bicep' = if (updateAzureAISearchApi) { + name: 'ai-search-index-api-upgrade' + params: { + serviceName: apimService.name + apiName: 'azure-ai-search-index-api' + path: 'search' + apiRevision: '1' + apiDispalyName: 'Azure AI Search Index API (index services)' + subscriptionRequired: entraAuth ? false : true + subscriptionKeyName: 'api-key' + openApiSpecification: loadTextContent('../modules/apim/ai-search-api/ai-search-index-2024-07-01-api-spec.json') + apiDescription: 'Azure AI Search Index Client APIs' + policyDocument: loadTextContent('../modules/apim/policies/ai-search-index-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +// ===================================================================== +// APIs — OpenAI Realtime WebSocket +// ===================================================================== + +module apimOpenAIRealtimeApi '../modules/apim/api.bicep' = if (updateOpenAIRealtimeApi) { + name: 'openai-realtime-ws-api-upgrade' + params: { + serviceName: apimService.name + apiName: 'openai-realtime-ws-api' + path: 'openai/realtime' + apiRevision: '1' + apiDispalyName: 'Azure OpenAI Realtime API' + subscriptionRequired: entraAuth ? false : true + subscriptionKeyName: 'api-key' + openApiSpecification: 'NA' + apiDescription: 'Access Azure OpenAI Realtime API for real-time voice and text conversion.' + policyDocument: loadTextContent('../modules/apim/policies/openai-realtime-policy.xml') + enableAPIDeployment: true + serviceUrl: 'wss://to-be-replaced-by-policy' + apiType: 'websocket' + apiProtocols: ['wss'] + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +// ===================================================================== +// APIs — Document Intelligence +// ===================================================================== + +module apimDocumentIntelligenceLegacy '../modules/apim/api.bicep' = if (updateDocumentIntelligenceApi) { + name: 'doc-intel-legacy-api-upgrade' + params: { + serviceName: apimService.name + apiName: 'document-intelligence-api-legacy' + path: 'formrecognizer' + apiRevision: '1' + apiDispalyName: 'Document Intelligence API (Legacy)' + subscriptionRequired: entraAuth ? false : true + subscriptionKeyName: 'Ocp-Apim-Subscription-Key' + openApiSpecification: loadTextContent('../modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml') + apiDescription: 'Uses (/formrecognizer) url path. Extracts content, layout, and structured data from documents.' + policyDocument: loadTextContent('../modules/apim/policies/doc-intelligence-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +module apimDocumentIntelligence '../modules/apim/api.bicep' = if (updateDocumentIntelligenceApi) { + name: 'doc-intel-api-upgrade' + params: { + serviceName: apimService.name + apiName: 'document-intelligence-api' + path: 'documentintelligence' + apiRevision: '1' + apiDispalyName: 'Document Intelligence API' + subscriptionRequired: entraAuth ? false : true + subscriptionKeyName: 'Ocp-Apim-Subscription-Key' + openApiSpecification: loadTextContent('../modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml') + apiDescription: 'Uses (/documentintelligence) url path. Extracts content, layout, and structured data from documents.' + policyDocument: loadTextContent('../modules/apim/policies/doc-intelligence-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +// ===================================================================== +// LOGGING — Application Insights Diagnostics (APIM-level) +// ===================================================================== + +resource apimAppInsightsDiagnostics 'Microsoft.ApiManagement/service/diagnostics@2024-05-01' = if (updateAppInsightsDiagnostics) { + parent: apimService + name: 'applicationinsights' + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + verbosity: 'information' + logClientIp: true + loggerId: appInsightsLogger.id + metrics: true + sampling: { + samplingType: 'fixed' + percentage: 100 + } + frontend: { + request: { + headers: appInsightsLogSettings.headers + body: { + bytes: appInsightsLogSettings.body.bytes + } + } + response: { + headers: appInsightsLogSettings.headers + body: { + bytes: appInsightsLogSettings.body.bytes + } + } + } + backend: { + request: { + headers: appInsightsLogSettings.headers + body: { + bytes: appInsightsLogSettings.body.bytes + } + } + response: { + headers: appInsightsLogSettings.headers + body: { + bytes: appInsightsLogSettings.body.bytes + } + } + } + } +} + +// ===================================================================== +// OUTPUTS +// ===================================================================== + +@description('Name of the updated APIM instance') +output apimName string = apimService.name + +@description('Gateway URL of the APIM instance') +output apimGatewayUrl string = apimService.properties.gatewayUrl diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicepparam new file mode 100644 index 000000000..4cb764718 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/apim-gateway-upgrade/main.bicepparam @@ -0,0 +1,154 @@ +using './main.bicep' + +// ===================================================================== +// CORE — Identify the existing APIM instance and managed identity +// ===================================================================== + +param apimServiceName = '' +param managedIdentityName = '' + +// ===================================================================== +// POLICY FRAGMENTS +// ===================================================================== + +param updatePolicyFragments = true +param enablePIIAnonymization = true +param enableAIModelInference = true + +// ===================================================================== +// NAMED VALUES (auth, Entra, PII, Content Safety) +// ===================================================================== + +param updateNamedValues = true +param entraAuth = false +param clientAppId = ' ' +// param tenantId — defaults to current tenant +param audience = 'https://cognitiveservices.azure.com/.default' +param aiLanguageServiceUrl = '' +param contentSafetyServiceUrl = '' + +// ===================================================================== +// JWT AUTHENTICATION NAMED VALUES +// Required when enableJwtAuth = true +// ===================================================================== + +param updateJwtNamedValues = true +param enableJwtAuth = false +param jwtTenantId = '' +param jwtAppRegistrationId = '' + +// ===================================================================== +// LLM BACKENDS, POOLS & DYNAMIC POLICY FRAGMENTS +// Define all LLM backends and their supported models. +// Uncomment and configure the array below to match your environment. +// ===================================================================== + +param updateLLMBackends = true +param updateLLMBackendPools = true +param updateLLMPolicyFragments = true +param llmBackendConfig = [ + // Example: Azure OpenAI backend + // { + // backendId: 'azure-openai-swedencentral' + // backendType: 'azure-openai' + // endpoint: 'https://my-openai.openai.azure.com' + // authScheme: 'managedIdentity' + // supportedModels: [ + // { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-08-06' } + // ] + // priority: 1 + // weight: 100 + // } + // Example: AI Foundry backend + // { + // backendId: 'ai-foundry-eastus' + // backendType: 'ai-foundry' + // endpoint: 'https://my-project.eastus.inference.ml.azure.com' + // authScheme: 'managedIdentity' + // supportedModels: [ + // { name: 'gpt-4o-mini', sku: 'GlobalStandard', capacity: 50, modelFormat: 'OpenAI', modelVersion: '2024-07-18' } + // ] + // priority: 1 + // weight: 100 + // } +] + +// ===================================================================== +// INFERENCE APIs — Universal LLM & Azure OpenAI +// ===================================================================== + +param updateUniversalLLMApi = true +param updateAzureOpenAIApi = true + +// ===================================================================== +// UNIFIED AI WILDCARD API +// ===================================================================== + +param updateUnifiedAiApi = true +param enableUnifiedAiApi = true + +// ===================================================================== +// AZURE AI SEARCH API +// ===================================================================== + +param updateAzureAISearchApi = false +param aiSearchInstances = [ + // { + // name: 'my-search-instance' + // description: 'Azure AI Search Service' + // url: 'https://my-search.search.windows.net' + // } +] + +// ===================================================================== +// OPENAI REALTIME WEBSOCKET API +// ===================================================================== + +param updateOpenAIRealtimeApi = false + +// ===================================================================== +// DOCUMENT INTELLIGENCE APIs +// ===================================================================== + +param updateDocumentIntelligenceApi = false + +// ===================================================================== +// REDIS CACHE & EMBEDDINGS BACKEND +// Required when updateRedisCache or updateEmbeddingsBackend = true +// ===================================================================== + +param updateRedisCache = false +param enableRedisCache = false +param redisCacheConnectionString = '' + +param updateEmbeddingsBackend = false +param enableEmbeddingsBackend = false +param embeddingsBackendUrl = '' + +// ===================================================================== +// LOGGING / DIAGNOSTICS SETTINGS +// Customize what is captured in Application Insights and Azure Monitor. +// ===================================================================== + +param updateAppInsightsDiagnostics = true + +param azureMonitorLogSettings = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +param appInsightsLogSettings = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/README.md new file mode 100644 index 000000000..21486fac5 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/README.md @@ -0,0 +1,851 @@ +# 📜 Citadel Access Contracts - Use-case onboarding + +## Overview + +Automate the onboarding of AI use cases to your APIM-based AI Gateway with a streamlined, infrastructure-as-code approach using **Bicep parameter files** (`.bicepparam`). + +This package eliminates manual APIM configuration by providing: +- 📦 **Automated Product Creation**: Per-service APIM products with naming `---` +- 🔌 **API Integration**: Automatic API attachment to product with custom or default policies +- 🔑 **Subscription Management**: Auto-generated subscription with secure API keys +- 🔐 **Flexible Secret Storage**: Optional Azure Key Vault integration or direct credential output +- 🤖 **Azure Microsoft Foundry Integration**: Optional APIM connection creation for Foundry agents +- 📝 **Declarative Configuration**: Simple `.bicepparam` & `.xml` files for version control per use case +- 🔐 **JWT Authentication**: Optional layered API Key + JWT Bearer token authentication per product + +## What Gets Created + +| Resource | Naming Pattern | Description | +|----------|----------------|-------------| +| **APIM Product** | `{code}-{BU}-{UseCase}-{ENV}` | Product per service (e.g., `LLM-Healthcare-PatientAssistant-DEV`) with attached APIs and policies | +| **APIM Subscription** | `{product}-SUB-01` | Subscription with API key | +| **Key Vault Secrets** | `{secretName}` | Endpoint URL and API key (optional) | +| **Foundry Connection** | `{prefix}-{code}` | APIM connection for Microsoft Foundry agents (optional) | + +## Key Features + +✨ **Simplified Parameters**: No need for full resource IDs - just API names +🔄 **Optional Key Vault**: Choose between Key Vault storage or direct output +🤖 **Optional Foundry Integration**: Create APIM connections for AI agents +📋 **Policy Templates**: Pre-built policies for common use cases +🎯 **Multi-Service Support**: Onboard multiple AI services in one deployment +🔒 **Secure by Default**: Credentials stored in Key Vault or marked as secrets +📊 **Production Ready**: Designed for scale and aligned with DevOps practices +--- + +## Deployment quick reference + +below are the high-level steps to deploy the use case onboarding Bicep package before diving into the detailed documentation: + +0. **Create a folder dedicated for use-cases contracts**: a folder like `contracts` in the citadel-access-contracts module to hold your use case specific files. +1. **Create a use-case contract folder**: under the contracts folder, create a new folder for your use case following the pattern `-` (e.g. `sales-assistant`, `hr-chatagent`). +2. **Create an environment subfolder**: under the use case folder, create a subfolder for each environment (e.g. `dev`, `test`, `prod`). +3. **Prepare Parameter File**: Create new use case `.bicepparam` file (you can use `main.bicepparam` as a base) under the environment folder. +4. **Create/Customize APIM Policy**: Use default or create a custom XML policy. For simplicity, policy file can be named `ai-product-policy.xml` and placed in the same environment folder. +5. **Deploy template with the prepared parameter file**: +```bash +# This can be executed in CLI or through a DevOps pipeline +az deployment sub create --name --location --template-file main.bicep --parameters contracts///main.bicepparam +``` + +>NOTE: Ensure that you are updating values according to your environment and folder structure. + +## 🗺️ Architecture Overview + +### Deployment Flow + +```mermaid +flowchart TB + subgraph Input["📥 Inputs"] + P1[bicepparam file] + P5[Policy XML files] + end + + subgraph Deploy["🚀 Deployment"] + D1[main.bicep] + D2[Create Products] + D3[Attach APIs to Products] + D4[Apply Policies] + D5[Create Subscriptions] + D6{Use Key Vault?} + D7[Store Secrets in KV] + D8[Output Credentials] + D9{Use Foundry?} + D10[Create Foundry Connections] + end + + subgraph Output["📤 Outputs"] + O1[Products Created] + O2[Subscription Keys] + O3[KV Secret Names] + O4[Direct Credentials] + O5[Foundry Connections] + end + + Input --> Deploy + D1 --> D2 --> D3 --> D4 --> D5 --> D6 + D6 -->|Yes| D7 --> O3 + D6 -->|No| D8 --> O4 + D5 --> D9 + D9 -->|Yes| D10 --> O5 + D9 -->|No| O2 + D2 --> O1 + D5 --> O2 +``` + +### Runtime Request Flow + +Below is a suggested flow for client applications (i.e. agents) interacting with the onboarded services via the Citadel Access Contracts: + +```mermaid +sequenceDiagram + participant App as AI Agent/App + participant Entra as Microsoft Entra ID + participant KV as Azure Key Vault + participant Foundry as Microsoft Foundry + participant APIM as AI Gateway + participant AI as AI Services + + alt Using Key Vault + App->>KV: Get endpoint + API key + KV-->>App: Return secrets + else Using Foundry Connection + App->>Foundry: Use APIM connection + Foundry->>Foundry: Get stored credentials + else Direct Credentials + Note over App: Use credentials from deployment output + end + + opt JWT Authentication Enabled + App->>Entra: Request JWT token (client credentials) + Entra-->>App: Return Bearer token + end + + App->>APIM: HTTPS request with api-key + optional Bearer token + APIM->>APIM: Validate subscription key (always required) + APIM->>APIM: Apply product policy (check jwtRequired) + opt JWT Required by Product + APIM->>APIM: Validate JWT via security-handler fragment + end + APIM->>AI: Forward to backend service + AI-->>APIM: Response + APIM->>APIM: Logs & metrics + APIM-->>App: Response with usage headers +``` + +--- + +## 📁 Repository Structure + +This is a submodule focused on Citadel Access Contracts. The folder structure is as follows: + +``` +citadel-access-contracts/ +├── main.bicep # Main orchestration template +├── main.bicepparam # Base parameter file +├── modules/ +│ ├── apimOnboardService.bicep # Product + subscription creation +│ ├── apimProduct.bicep # APIM product module +│ ├── apimSubscription.bicep # Subscription module +│ ├── kvSecrets.bicep # Key Vault secret storage +│ └── foundryConnection.bicep # Azure Microsoft Foundry connection module +├── policies/ +│ └── default-ai-product-policy.xml # Default product policy +├── contracts/ # Use-case contracts folder for source control +│ └── / # Use case folder (e.g., sales-assistant) +│ ├── dev/ # Environment subfolder +│ │ ├── main.bicepparam # Use case specific parameters +│ │ └── ai-product-policy.xml # Custom policy file +│ ├── test/ # Test environment +│ │ ├── main.bicepparam +│ │ └── ai-product-policy.xml +│ └── prod/ # Production environment +│ ├── main.bicepparam +│ └── ai-product-policy.xml +``` + +--- + +## 🔧 Parameter File Reference + +### Main Parameters (main.bicepparam) + +| Parameter | Type | Required | Description | Example | +|-----------|------|----------|-------------|---------| +| `apim` | object | ✅ | APIM instance coordinates | `{ subscriptionId, resourceGroupName, name }` | +| `keyVault` | object | ✅* | Key Vault for secrets (*required even if not used) | `{ subscriptionId, resourceGroupName, name }` | +| `useTargetAzureKeyVault` | bool | ❌ | Store secrets in Key Vault (default: `false`) | `true` or `false` | +| `useCase` | object | ✅ | Use case naming context | `{ businessUnit, useCaseName, environment }` | +| `apiNameMapping` | object | ✅ | Map service codes to API names | `{ OAI: ["azure-openai-service-api"], ... }` | +| `services` | array | ✅ | Services to onboard | See [Services Schema](#services-schema) below | +| `productTerms` | string | ❌ | Product terms of service | "By using this product..." | + +> **JWT Authentication**: JWT validation is configured per access contract via the `policies.jwtAuth.enabled` field in the Agent Access Contract Request JSON (see [Base Access Contract Request](#jwt-authentication-for-access-contracts)). When enabled, the product policy sets `jwtRequired=true`, and the `security-handler` fragment enforces JWT Bearer token validation in addition to the API key. + +| `useTargetFoundry` | bool | ❌ | Create Foundry connections (default: `false`) | `true` or `false` | +| `foundry` | object | ❌* | Microsoft Foundry coordinates (*required if useTargetFoundry=true) | `{ subscriptionId, resourceGroupName, accountName, projectName }` | +| `foundryConfig` | object | ❌ | Foundry connection configuration | See [Foundry Config](#foundry-configuration) below | + + +#### Service Code mapping + +Map service codes (which is a short acronym that represents the category of services fall under) to their API-id in APIM: + +```bicep +{ + LLM: ["azure-openai-api", "universal-llm-api"] + OAIRT: ["openai-realtime-ws-api"] + DOC: ["document-intelligence-api", "document-intelligence-api-legacy"] + SRCH: ["azure-ai-search-index-api"] + // ... add more services +} + +``` + +**Note**: API-id must already exist in your APIM instance. The deployment will fail if an API name is not found. + +Above is the list of supported APIs that are provisioned by default by the accelerator. + +#### Adding custom APIs + +You can onboard any number of other APIs as well to support your custom services. Add the newly added API-ids to the mapping above accordingly. + +#### Multi-service Bundles + +Mapping currently is suggested to focus on a specific category of services (e.g., LLM, Document Intelligence, etc.) + +You can create unique mappings that mix different service types under one bundle if needed (this will require the product policy to be aware of that mix to apply the correct policies based on the service type like using tokens-per-mint limits for LLM and request-per-min limits for Document Intelligence). + +#### Use-case service assignment schema + +Each service in the `services` array: + +```bicep +{ + code: string // Service code (e.g., "LLM", "DOC", "SRCH") + endpointSecretName: string // Name for endpoint secret in Key Vault + apiKeySecretName: string // Name for API key secret in Key Vault + policyXml: string // Optional: Custom policy XML (empty = use default) +} +``` + +This will create one APIM product + subscription + Key Vault secrets per service code and leverage the referenced policy XML for that product. + +This is an array to allow multiple services to be assigned to specific use case (like granting both LLM and document intelligence access to the same application). + +But each service will have its own product + subscription + secrets (i.e llm will have a different key from document intelligence). + +--- + +## 🧱 What gets created + +| Component | Scope | Naming | Notes | +|-----------|-------|--------|-------| +| APIM Product | APIM | `---` | One per service code you include | +| APIM Subscription | APIM | `-SUB-01` | Primary key is captured into Key Vault | +| Key Vault Secrets | KV | `endpointSecretName`, `apiKeySecretName` | One endpoint + one key per service | +| Foundry Connection | Microsoft Foundry | `-` | One connection per service (if enabled) | + +Naming examples +- Product: `LLM-Retail-FinancialAssistant-DEV` +- Subscription: `LLM-Retail-FinancialAssistant-DEV-SUB-01` +- Foundry Connection: `Retail-FinancialAssistant-DEV-LLM` + +--- + +## ✅ Prerequisites + +### Azure Resources + +| Resource | Requirement | How to Verify | +|----------|-------------|---------------| +| **Citadel Compliant APIM Instance** | with published APIs matching your `apiNameMapping` | `az apim api list -g -n ` | +| **Azure Key Vault** | Accessible with secret set permissions (if using KV) | `az keyvault show -n ` | +| **Azure Microsoft Foundry** | Account and project must exist (if using Foundry) | `az cognitiveservices account show -n -g ` | + +### Permissions Required + +The deployment identity needs: + +| Scope | Role | Purpose | +|-------|------|---------| +| APIM Resource Group | `API Management Service Contributor` | Create products and subscriptions | +| Target Key Vault (if used) | `Key Vault Secrets Officer` | Write secrets | +| Microsoft Foundry Resource Group (if used) | `Contributor` | Create connections | +| Subscription | `Reader` | Reference existing resources | + +--- + +## ⚡ Quick Start Guide + +### Step 1: Create use case folder with environment subfolder + +Create a folder structure following the pattern `contracts///` with copies of both the main.bicepparam and default policy as a base. + +```powershell +# Create the folder structure: contracts/healthcare-chatbot/dev/ +mkdir -p bicep/infra/citadel-access-contracts/contracts/healthcare-chatbot/dev +cd bicep/infra/citadel-access-contracts/contracts/healthcare-chatbot/dev + +# Copy template files (note: path goes up 3 levels due to environment subfolder) +cp ../../../main.bicepparam main.bicepparam +cp ../../../policies/default-ai-product-policy.xml ai-product-policy.xml +``` + + +### Step 2: Configure Your Parameters + +```powershell +# Edit main.bicepparam in the environment folder +code main.bicepparam +``` + +Update these values: + +```bicep +using '../../../main.bicep' + +param apim = { + subscriptionId: 'YOUR-SUBSCRIPTION-ID' // ← Update + resourceGroupName: 'YOUR-APIM-RESOURCE-GROUP' // ← Update + name: 'YOUR-APIM-NAME' // ← Update +} + +param keyVault = { + subscriptionId: 'YOUR-SUBSCRIPTION-ID' // ← Update + resourceGroupName: 'YOUR-KV-RESOURCE-GROUP' // ← Update + name: 'YOUR-KV-NAME' // ← Update +} + +param useTargetAzureKeyVault = true // false to output credentials directly + +param useCase = { + businessUnit: 'YourDepartment' // ← Update + useCaseName: 'YourUseCaseName' // ← Update + environment: 'DEV' // DEV, TEST, PROD +} + +// Verify these API names exist in your APIM +param apiNameMapping = { + LLM: ['azure-openai-api', 'universal-llm-api'] + DOC: ['document-intelligence-api', 'document-intelligence-api-legacy'] +} + +param services = [ + { + code: 'LLM' + endpointSecretName: 'OPENAI-ENDPOINT' + apiKeySecretName: 'OPENAI-API-KEY' + policyXml: loadTextContent('ai-product-policy.xml') // or '' for default + } + // Add more services as needed +] +``` + +### Step 3: Validate Configuration + +```powershell +# Preview what will be created (run from the environment folder) +az deployment sub what-if ` + --location swedencentral ` + --template-file ../../../main.bicep ` + --parameters main.bicepparam +``` + +### Step 4: Deploy + +```powershell +# Deploy at subscription scope (run from the environment folder) +az deployment sub create ` + --name healthcare-chatbot-dev-onboarding ` + --location swedencentral ` + --template-file ../../../main.bicep ` + --parameters main.bicepparam + +# Or run from the citadel-access-contracts root folder: +az deployment sub create ` + --name healthcare-chatbot-dev-onboarding ` + --location swedencentral ` + --template-file main.bicep ` + --parameters contracts/healthcare-chatbot/dev/main.bicepparam +``` + +### Step 5: Verify Deployment + +```powershell +# Check products created +az apim product list ` + --resource-group YOUR-APIM-RG ` + --service-name YOUR-APIM-NAME ` + --query "[?contains(name, 'REPLACE-PRODUCT-NAME')].{Name:name, State:state}" + +# If using Key Vault, check secrets +az keyvault secret list ` + --vault-name YOUR-KV-NAME ` + --query "[?contains(name, 'openai')].name" +``` + +### Step 6: Use the Service + +```python +from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient + +# Get credentials from Key Vault +credential = DefaultAzureCredential() +kv_client = SecretClient( + vault_url="https://YOUR-KV-NAME.vault.azure.net/", + credential=credential +) + +endpoint = kv_client.get_secret("openai-endpoint").value +api_key = kv_client.get_secret("openai-api-key").value + +# Use with your application +print(f"Endpoint: {endpoint}") +# api_key is ready to use as api-key header +``` + +Also you can use the [citadel-access-contracts-tests](../../../validation/citadel-access-contracts-tests.ipynb) notebook to validate end-to-end connectivity of the newly created access contract. + +--- + +## 🔑 Secret Management Options + +Access contract generates sensitive keys for each service onboarded. You have two options to manage these secrets: + +### Option 1: Use Key Vault (Recommended) + +**When to use**: Production deployments, applications with managed identities + +```bicep +param useTargetAzureKeyVault = true + +param keyVault = { + subscriptionId: 'YOUR-SUB-ID' + resourceGroupName: 'YOUR-KV-RG' + name: 'YOUR-KV-NAME' +} +``` + +**Benefits**: +- ✅ Centralized secret management +- ✅ Automatic rotation support +- ✅ Access auditing +- ✅ Integration with managed identities + +**Retrieval**: +```python +from azure.keyvault.secrets import SecretClient +kv_client = SecretClient(vault_url="https://.vault.azure.net/", credential=credential) +endpoint = kv_client.get_secret("llm-endpoint").value +``` + +### Option 2: Direct Output (CI/CD) + +**When to use**: CI/CD pipelines, serverless functions, non-Azure environments where these values needs to be stored in some other secret store + +```bicep +param useTargetAzureKeyVault = false + +// keyVault still required but can use placeholders as values are not considered. +param keyVault = { + subscriptionId: '00000000-0000-0000-0000-000000000000' + resourceGroupName: 'placeholder' + name: 'placeholder' +} +``` + +**Benefits**: +- ✅ No Key Vault dependency +- ✅ Direct credential access +- ✅ Works in any environment + +**Retrieval from Deployment Output**: +```powershell +$output = az deployment sub show ` + --name my-deployment ` + --query properties.outputs.endpoints.value -o json | ConvertFrom-Json + +$oaiEndpoint = ($output | Where-Object { $_.code -eq 'LLM' }).endpoint +$oaiKey = ($output | Where-Object { $_.code -eq 'LLM' }).apiKey + +# Store in CI/CD variables (NOT RECOMMENDED. Just for demonstration) +Write-Host "##vso[task.setvariable variable=LLM_ENDPOINT;issecret=true]$oaiEndpoint" +Write-Host "##vso[task.setvariable variable=LLM_KEY;issecret=true]$oaiKey" +``` + +⚠️ **Security Note**: When using direct output, handle credentials as secrets in your CI/CD system. + +### Option 3: Use Microsoft Foundry Connections (Recommended for Agents) + +**When to use**: Building AI agents in Microsoft Foundry that need APIM gateway access + +```bicep +param useTargetFoundry = true + +param foundry = { + subscriptionId: 'YOUR-FOUNDRY-SUB-ID' + resourceGroupName: 'YOUR-FOUNDRY-RG' + accountName: 'YOUR-FOUNDRY-ACCOUNT' + projectName: 'YOUR-PROJECT-NAME' +} + +param foundryConfig = { + connectionNamePrefix: '' // Empty = use useCase naming + deploymentInPath: 'false' // Model in request body + isSharedToAll: false // Share with project users + inferenceAPIVersion: '' // APIM defaults + deploymentAPIVersion: '' // APIM defaults + staticModels: [] // Dynamic discovery + listModelsEndpoint: '' // APIM defaults + getModelEndpoint: '' // APIM defaults + deploymentProvider: '' // AzureOpenAI format + customHeaders: {} // No custom headers + authConfig: {} // Default api-key header +} +``` + +**Benefits**: +- ✅ Seamless integration with Microsoft Foundry agents +- ✅ Credentials stored securely in Foundry connection +- ✅ Supports the "Bring Your Own AI Gateway" pattern +- ✅ Automatic model discovery from APIM +- ✅ No need to manage secrets separately + +**Usage in Foundry Agent**: + +```python +import os +from azure.ai.projects import AIProjectClient +from azure.identity import DefaultAzureCredential + +# Connection name follows pattern: - +# Example: HR-ChatBot-DEV-LLM +connection_name = "HR-ChatBot-DEV-LLM" +model_deployment = f"{connection_name}/gpt-4o" + +os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"] = model_deployment + +client = AIProjectClient( + credential=DefaultAzureCredential(), + endpoint="https://your-foundry.cognitiveservices.azure.com/" +) + +# Create agent using the APIM connection +agent = client.agents.create_agent( + model=model_deployment, + name="my-hr-assistant", + instructions="You are a helpful HR assistant." +) +``` + +### Foundry Configuration Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `connectionNamePrefix` | string | `''` | Custom prefix for connection names. Empty uses `--` | +| `deploymentInPath` | string | `'false'` | `'true'`: model in URL, `'false'`: model in body | +| `isSharedToAll` | bool | `false` | Share connection with all project users | +| `inferenceAPIVersion` | string | `''` | API version for chat/embeddings (empty = APIM defaults) | +| `deploymentAPIVersion` | string | `''` | API version for discovery (empty = APIM defaults) | +| `staticModels` | array | `[]` | Fixed model list (skips discovery) | +| `listModelsEndpoint` | string | `''` | Custom list endpoint (empty = `/deployments`) | +| `getModelEndpoint` | string | `''` | Custom get endpoint (empty = `/deployments/{id}`) | +| `deploymentProvider` | string | `''` | Discovery format (`AzureOpenAI` or `OpenAI`) | +| `customHeaders` | object | `{}` | Additional headers for requests | +| `authConfig` | object | `{}` | Custom auth header config | + +### Combined Targets Example + +You can use Key Vault AND Foundry together: + +```bicep +// Store secrets in Key Vault for traditional apps +param useTargetAzureKeyVault = true +param keyVault = { + subscriptionId: 'YOUR-SUB-ID' + resourceGroupName: 'YOUR-KV-RG' + name: 'YOUR-KV-NAME' +} + +// Also create Foundry connections for AI agents +param useTargetFoundry = true +param foundry = { + subscriptionId: 'YOUR-FOUNDRY-SUB-ID' + resourceGroupName: 'YOUR-FOUNDRY-RG' + accountName: 'YOUR-FOUNDRY-ACCOUNT' + projectName: 'YOUR-PROJECT-NAME' +} +``` + +This creates: +- APIM products and subscriptions +- Key Vault secrets for traditional applications +- Foundry APIM connections for AI agents + +All using the same subscription keys, ensuring consistent governance. + +--- + +## 📝 Creating Custom Policies + +### Using Default Policy + +The simplest approach - omit `policyXml` or set it to empty string: + +```bicep +param services = [ + { + code: 'LLM' + endpointSecretName: 'LLM-ENDPOINT' + apiKeySecretName: 'LLM-KEY' + policyXml: '' // Uses policies/default-ai-product-policy.xml + } +] +``` + +**Default policy includes**: +- Model restrictions (GPT-4o, deepseek-r1) +- Token limits (300 tokens/min + 10,000 tokens/month) +- Content safety checks + +### Creating Custom Policy + +**Step 1**: Create policy XML file in your use case folder + +You can use the [default-ai-product-policy.xml](./policies/default-ai-product-policy.xml) as a base policy for LLM and extend/modify it as needed. + +Additional policy capabilities can be found in [Citadel-Access-Contracts-Policy.md](./citadel-access-contracts-policy.md) for more options and patterns that can be leveraged. + +>Note: APIM has power policy engine that can be used even beyond what is provided in Citadel Governance Hub. Leverage agentic development like GitHub Copilot to help you create custom policies based on your requirements. + +**Step 2**: Reference policy in bicepparam + +```bicep +param services = [ + { + code: 'LLM' + endpointSecretName: 'LLM-ENDPOINT' + apiKeySecretName: 'LLM-KEY' + policyXml: loadTextContent('my-custom-policy.xml') + } +] +``` + +--- + +## 🔄 Advanced Scenarios + +### Multiple Services in One Use Case + +Onboard multiple AI services simultaneously: + +```bicep +param apiNameMapping = { + LLM: ['azure-openai-api', 'universal-llm-api'] + DOC: ['document-intelligence-api', 'document-intelligence-api-legacy'] + SRCH: ['azure-ai-search-index-api'] + OAIRT: ['openai-realtime-ws-api'] +} + +param services = [ + { + code: 'LLM' + endpointSecretName: 'MULTI-LLM-ENDPOINT' + apiKeySecretName: 'MULTI-LLM-KEY' + policyXml: loadTextContent('llm-policy.xml') + } + { + code: 'DOC' + endpointSecretName: 'MULTI-DOC-ENDPOINT' + apiKeySecretName: 'MULTI-DOC-KEY' + policyXml: loadTextContent('doc-policy.xml') + } + { + code: 'SRCH' + endpointSecretName: 'MULTI-SEARCH-ENDPOINT' + apiKeySecretName: 'MULTI-SEARCH-KEY' + policyXml: '' // Use default + } +] +``` + +**Result**: Creates 3 APIM products, 3 subscriptions, 6 Key Vault secrets (endpoint + key for each service). + +--- + +## 📤 Deployment Outputs + +After deployment, the following outputs are available: + +### When Using Key Vault (`useTargetAzureKeyVault = true`) + +| Output | Type | Description | Example | +|--------|------|-------------|---------| +| `apimGatewayUrl` | string | APIM gateway base URL | `https://apim-gateway.azure-api.net` | +| `useKeyVault` | bool | Always `true` | `true` | +| `products[]` | array | Created products | `[{ productId: "OAI-Healthcare-...", displayName: "..." }]` | +| `subscriptions[]` | array | KV secret names | `[{ name: "OAI-...-SUB-01", keyVaultApiKeySecretName: "openai-api-key", ... }]` | + +**Access secrets from Key Vault**: +```powershell +$secretNames = (az deployment sub show --name my-deployment --query properties.outputs.subscriptions.value -o json | ConvertFrom-Json) +$endpoint = az keyvault secret show --vault-name --name ($secretNames[0].keyVaultEndpointSecretName) --query value -o tsv +``` + +### When NOT Using Key Vault (`useTargetAzureKeyVault = false`) + +| Output | Type | Description | Contains Secrets | +|--------|------|-------------|------------------| +| `apimGatewayUrl` | string | APIM gateway base URL | No | +| `useKeyVault` | bool | Always `false` | No | +| `products[]` | array | Created products | No | +| `endpoints[]` | array | **Direct credentials** | ⚠️ **YES** | + +**Endpoints output structure**: +```json +[ + { + "code": "OAI", + "productId": "OAI-Healthcare-PatientAssistant-DEV", + "subscriptionName": "OAI-Healthcare-PatientAssistant-DEV-SUB-01", + "endpoint": "https://apim-gateway.azure-api.net/openai", + "apiKey": "abc123...xyz" // ⚠️ Sensitive + } +] +``` + +**Extract credentials**: +```powershell +$output = az deployment sub show --name my-deployment --query properties.outputs.endpoints.value -o json | ConvertFrom-Json +$oaiCreds = $output | Where-Object { $_.code -eq 'OAI' } + +# Store securely in CI/CD +Write-Host "##vso[task.setvariable variable=OAI_ENDPOINT;issecret=true]$($oaiCreds.endpoint)" +Write-Host "##vso[task.setvariable variable=OAI_KEY;issecret=true]$($oaiCreds.apiKey)" +``` + +⚠️ **Security Warning**: The `endpoints` output contains sensitive API keys when not using Key Vault. Always: +- Mark as secrets in CI/CD systems +- Never log or display in plaintext +- Store in secure secret management systems +- Rotate keys regularly + +--- + +## � JWT Authentication for Access Contracts + +Access Contracts support layered authentication: **API Key is always required**, with optional **JWT Bearer token** validation per product. This is configured during contract creation, not at deployment time. + +### Prerequisites + +- The Citadel Governance Hub must be deployed with `entraAuth=true` so that APIM JWT named values (`JWT-TenantId`, `JWT-AppRegistrationId`, `JWT-Issuer`, `JWT-OpenIdConfigUrl`) and the `security-handler` policy fragment are provisioned +- An Entra ID App Registration exists (auto-provisioned by the `entra-id` Bicep module or provided manually) +- Client secret is stored in Key Vault as `ENTRA-APP-CLIENT-SECRET` + +### How It Works + +1. The product policy snippet `jwt-auth.xml` sets `jwtRequired=true` in the APIM context +2. The `security-handler` fragment detects the auth method (`api-key`, `jwt`, `api-key-jwt`) +3. When `jwtRequired=true`, the fragment validates the JWT Bearer token against the Entra ID OpenID configuration +4. Requests without a valid JWT are rejected with `401 Unauthorized` + +### Authentication Flow + +| Scenario | Headers Required | Result | +|----------|-----------------|--------| +| API Key only (JWT not enabled) | `api-key: {key}` | ✅ Allowed | +| API Key + JWT (JWT enabled) | `api-key: {key}` + `Authorization: Bearer {token}` | ✅ Allowed | +| API Key only (JWT enabled) | `api-key: {key}` | ❌ 401 - JWT required | +| JWT only (no API Key) | `Authorization: Bearer {token}` | ❌ 401 - API key required | +| Invalid JWT (JWT enabled) | `api-key: {key}` + `Authorization: Bearer {invalid}` | ❌ 401 - Invalid token | + +### Enabling JWT via Agent Access Contract Request + +When using the `base-access-contract-request` module, enable JWT in the contract JSON: + +```json +{ + "contractInfo": { + "businessUnit": "Security", + "useCaseName": "SecureAgent", + "environment": "DEV" + }, + "policies": { + "jwtAuth": { "enabled": true }, + "modelAccess": { + "enabled": true, + "allowedModels": ["gpt-4o", "gpt-4o-mini"] + } + }, + "services": [ + { + "code": "LLM", + "endpointSecretName": "SECURE-AGENT-ENDPOINT", + "apiKeySecretName": "SECURE-AGENT-KEY" + } + ] +} +``` + +### Enabling JWT via Custom Product Policy + +For manual access contracts (not using the agent request module), add the JWT snippet to your product policy XML: + +```xml + + + + + + + + + + + + + + + + + + +``` + +### Acquiring a JWT Token + +Use the client credentials flow to obtain a JWT token from Microsoft Entra ID: + +```http +POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token +Content-Type: application/x-www-form-urlencoded + +grant_type=client_credentials +&client_id={entra-app-client-id} +&client_secret={entra-app-client-secret} +&scope={audience}/.default +``` + +Then include the token in API requests: + +```http +POST https://{apim-gateway}/openai/deployments/gpt-4o/chat/completions?api-version=2024-02-15-preview +api-key: {subscription-key} +Authorization: Bearer {jwt-token} +Content-Type: application/json +``` + +### Validation + +Use the [JWT Access Contract Validation Notebook](../../../test/citadel-jwt-access-contract-tests.ipynb) to test end-to-end JWT authentication with access contracts. + +--- + +## �📞 Support + +For issues or questions: +- **GitHub Issues**: [Report bugs or request features](https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator/issues) + +--- diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/citadel-access-contracts-policy.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/citadel-access-contracts-policy.md new file mode 100644 index 000000000..7a66992a3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/citadel-access-contracts-policy.md @@ -0,0 +1,666 @@ +# AI Citadel Access Contracts Policy + +This guide to expand on what policies are available out of the box for use with the AI Citadel Access Contracts Bicep package, and how to customize them for each use case being onboarded. + +## Available Policies Snippets + +The following policy snippets can be applied as needed for the product policy access as part of the `Citadel Access Contracts`: + +### Model Access Control Policy + +The model access control policy restricts which LLM models a product can access. This is implemented using the `validate-model-access` policy fragment. + +**Basic Usage:** + +```xml + + + + + + + + + + +``` + +**How It Works:** + +1. The `set-llm-requested-model` fragment extracts the model from the request: + - From request body `{"model": "gpt-4o", ...}` for Universal LLM API + - From URL path `/deployments/{deployment-id}/...` for Azure OpenAI API + - Returns `"non-llm-request"` for GET operations (like listing available models) + +2. The `validate-model-access` fragment validates the requested model: + - **Non-LLM requests** (GET operations): Usually reference meta data endpoints that discover allowed models + - **Empty `allowedModels`**: All models are allowed + - **Model not in list**: Returns 401 Unauthorized with structured JSON error + +**Error Response Format:** + +When access is denied, the policy returns a structured JSON error: + +```json +{ + "error": { + "message": "Access to model 'gpt-4' is not allowed for this product.", + "type": "access_error", + "code": "unauthorized_model_access", + "allowed_models": "gpt-4o,deepseek-r1" + } +} +``` + +**Configuration Options:** + +| Variable | Description | Example | +|----------|-------------|---------| +| `allowedModels` | Comma-separated list of allowed model names (no white-space) | `"gpt-4o,deepseek-r1,Phi-4"` | + +>**NOTE:** Non-LLM requests (such as GET operations for listing available models) are automatically allowed and do not require model validation. This ensures auxiliary endpoints function without needing a model parameter. + +### Model Capacity Management Policy + +The below policy snippet, enforces a token limit per subscription but for all models being access via this product. + +```xml + + + + +``` + +To further control capacity management per model per subscription, you can extend the above policy snippet to include model specific token limits by leveraging the `requestedModel` variable set via the `set-llm-requested-model` fragment. + +```xml + + + + + + + + + + + + + + + + + +``` + +### LLM Usage Customization Policy + +By default, AI Citadel Gateway is configured to collect the following data points for LLM usage tracking: +- Standard Dimensions (currently can't be modified): + - Region + - Service ID + - Service Name + - Service Type +- Citadel Added Dimensions: + - Product Name + - DeploymentName (based on requestedModel variable) + - Backend ID + - appId (looks for variable named appId, fall back to subscription ID and then to "Portal-Admin" if not found) +- Custom dimensions: + - customDimension1 (by default looks for a variable named customDimension1) + - customDimension2 (by default looks for a variable named customDimension2) + +Standard setup is already included in the default policies, but you can customize it further by setting up the following variables in the product policy inbound section: + +```xml + + + + + + + + + +``` + +>NOTE: The above policy fragment assumes that the client application is passing `x-app-id`, `x-sub-agent-id` and `x-enduser-id` headers in the request. You can modify the header names as per your requirements or use different approach to set these variables. + +### Semantic Cache Policy + +TBD + +### Configuring Alerts Policy + +Collecting throttling events can help in setting up alerts in Application Insights. You can configure the following variables in the product policy outbound section to customize the throttling event details: + +```xml + + + + + + + + + +``` + +Based on this policy, you can configure alerts in Application Insights to monitor for high throttling events and take necessary actions. + +>NOTE: Detailed guide on how to setup throttling events handling can be found in [Throttling Events Handling Guide](./throttling-events-handling.md) + + +### Response Headers Policy + +The `set-response-headers` policy fragment injects `UAIG-*` response headers that expose internal gateway state for debugging and observability. These headers help trace request processing through the gateway, including authentication context, model routing, backend selection, and cache operations. + +**By default, response headers are disabled.** To enable them for a specific product, set the `enableResponseHeaders` variable to `true` in the product policy inbound section. + +**Basic Usage:** + +```xml + + + + + +``` + +**Headers Returned (when enabled):** + +| Header | Source Variable | Description | +|--------|----------------|-------------| +| `UAIG-Auth-Type` | `auth-type` | Authentication method (`api-key`, `jwt`, `api-key-jwt`, `none`) | +| `UAIG-User-Id` | `user-id` | Authenticated user identifier | +| `UAIG-Subscription` | `subscription-name` | APIM subscription name | +| `UAIG-Model-Id` | `requestedModel` | Requested LLM model name | +| `UAIG-API-Type` | `api-type` | Detected API type (e.g., `azure-openai`, `universal-llm`) | +| `UAIG-Processed-Path` | `routing-processed-path` | Processed request path used for routing | +| `UAIG-API-Version` | `selected-api-version` | Selected API version | +| `UAIG-Is-Streaming` | `is-streaming` | Whether the request is a streaming request | +| `UAIG-Backend` | `selected-backend` | Selected backend pool | +| `UAIG-Final-Path` | `finalPath` | Final backend path after rewriting | +| `UAIG-Cache-Operation` | `cache-operation` | Cache operation performed (hit/miss/skip) | +| `UAIG-Request-Id` | — | APIM request correlation ID | +| `UAIG-Gateway-Region` | — | Azure region of the APIM gateway | + +**How It Works:** + +1. The `set-response-headers` fragment is included in the outbound and on-error sections of all three API policies (Azure OpenAI, Universal LLM, Unified AI) +2. The fragment checks the `enableResponseHeaders` variable — if not set or `false`, no headers are injected +3. When enabled via a product policy, the fragment adds all `UAIG-*` headers to the response using values set by upstream fragments during request processing + +**Configuration Options:** + +| Variable | Type | Default | Description | +|----------|------|---------|-------------| +| `enableResponseHeaders` | bool | `false` | Set to `@(true)` to enable response header injection | + +>**NOTE:** Response headers expose internal gateway state and should only be enabled for development/debugging products. Avoid enabling them in production access contracts to prevent leaking internal routing details to clients. + +### Content Safety Policy + +Content safety can be enforced at a gateway level using the built-in content safety policy. You can configure the content safety policy to block or flag content based on your organization's requirements. + +>NOTE: Content Safety has a context input limit of **10K** characters. If the content to be evaluated exceeds this limit, the policy will return a 413 Payload Too Large error. To handle this, you can set up a custom policy to split longer input content before passing it to the content safety policy. + +```xml + + + + + + + + + + + + +``` + +### JWT Authentication Policy + +JWT (JSON Web Token) authentication adds a second security layer on top of subscription API keys. When enabled for a product, clients must provide both an `api-key` header and an `Authorization: Bearer {token}` header. + +JWT validation is handled by the unified `security-handler` policy fragment, which is included in **all three API endpoints** (Azure OpenAI API, Universal LLM API, and Unified AI API). The fragment validates the token's audience, issuer, signature, and expiry against either gateway-level APIM named values or per-product custom overrides. + +> **Full JWT setup guide:** See [JWT Authentication Guide](../../../guides/entraid-auth-validation.md) for gateway-level configuration. +> **Client identity & permissions:** See [JWT Client Identity and Permissions Guide](../../../guides/jwt-client-identity-permissions.md) for configuring client applications to acquire tokens. + +**Prerequisites:** +- APIM named values configured: `JWT-TenantId`, `JWT-AppRegistrationId`, `JWT-Issuer`, `JWT-OpenIdConfigUrl` +- For Microsoft Entra ID: Run the `bicep/infra/entra-id-setup` module to auto-provision app registration and named values +- For other identity providers: Manually configure the APIM named values or use per-product custom overrides + +**APIM Named Values for JWT Configuration:** + +| Named Value | Description | Example (Entra ID) | Example (Auth0) | +|-------------|-------------|---------------------|-----------------| +| `JWT-OpenIdConfigUrl` | OpenID Connect discovery endpoint | `https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration` | `https://{domain}/.well-known/openid-configuration` | +| `JWT-Issuer` | Expected token issuer | `https://login.microsoftonline.com/{tenant}/v2.0` | `https://{domain}/` | +| `JWT-AppRegistrationId` | Expected audience claim | `api://{client-id}` | `https://your-api-identifier` | + +**Basic Usage - Enable JWT with Gateway Defaults:** + +```xml + + + + + + + +``` + +**Advanced Usage - Enable JWT with Custom Identity Provider:** + +Access contracts can override the gateway's default JWT settings by setting custom variables. The `security-handler` checks for these overrides first, then falls back to the APIM named values if not set. + +| Variable | Description | Falls back to Named Value | +|----------|-------------|---------------------------| +| `jwtAudience` | Custom audience claim to validate | `JWT-AppRegistrationId` | +| `jwtIssuer` | Custom token issuer to validate | `JWT-Issuer` | +| `jwtOpenIdConfigUrl` | Custom OpenID Connect discovery URL | `JWT-OpenIdConfigUrl` | + +```xml + + + + + + + + + + + + +``` + +You can override any combination of settings — unset variables fall back to the gateway defaults: + +```xml + + + + + + + +``` + +**How It Works:** + +1. The `security-handler` fragment (included in the API-level policy via ``) detects the authentication method: + - `api-key` — only subscription key provided + - `jwt` — only Bearer token provided + - `api-key-jwt` — both provided + - `none` — neither provided + +2. API key is always validated first (APIM subscription validation) + +3. If `jwtRequired` is `"true"` (set by product policy), JWT validation is enforced: + - Token is validated against the OpenID Connect configuration endpoint + - Audience, issuer, and signature are verified + - User identity is extracted from the `azp` claim (client credentials flow) + - Custom overrides (`jwtAudience`, `jwtIssuer`, `jwtOpenIdConfigUrl`) are used if set, otherwise APIM named values apply + +4. If a Bearer token is provided but `jwtRequired` is not set, the token is still validated (opportunistic validation) + +5. This behavior is **uniform across all three API endpoints** — the same `security-handler` fragment executes regardless of whether the request arrives via Azure OpenAI, Universal LLM, or Unified AI API + +**Output Variables Set by Security Handler:** + +| Variable | Description | Example Values | +|----------|-------------|----------------| +| `auth-type` | Authentication method detected | `"api-key"`, `"jwt"`, `"api-key-jwt"`, `"none"` | +| `subscription-name` | APIM subscription name | `"LLM-HR-ChatAgent-DEV-SUB-01"` | +| `user-id` | User identifier (from JWT or subscription) | `"app-client-id"` or `"subscription-name"` | + +**Error Responses:** + +| Scenario | HTTP Status | Error Code | Message | +|----------|-------------|------------|----------| +| No API key | 401 | `unauthorized` | Access denied. A valid API key is required. | +| JWT required but missing | 401 | `jwt_required` | JWT Bearer token is required for this product. | +| Invalid JWT token | 401 | — | Access denied due to invalid or expired JWT bearer token. | +| JWT config missing | 503 | `jwt_not_configured` | JWT authentication is not configured properly on the gateway. | + +**Token Acquisition (Client Credentials Flow - Entra ID):** + +```http +POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token +Content-Type: application/x-www-form-urlencoded + +grant_type=client_credentials +&client_id={entra-app-client-id} +&client_secret={entra-app-client-secret} +&scope={audience}/.default +``` + +**Combining JWT with Other Policies:** + +JWT authentication works alongside all other access contract policies like model access control and capacity management. + +> **NOTE:** The `jwtRequired` variable must be set within the product policy inbound section. The `security-handler` fragment reads this variable during API-level policy execution. + +### App Role Authorization Policy + +App role authorization adds fine-grained access control on top of JWT authentication. When enabled for a product, the `security-handler` fragment checks that the JWT token contains at least one of the required app roles in the `roles` claim. This is enforced **after** JWT validation, so the token must first pass audience, issuer, and signature checks. + +The gateway's Entra ID app registration defines the following app roles (provisioned by `entra-id-setup/setup.ps1`): + +| App Role | Value | Description | +|----------|-------|-------------| +| ReadWrite | `Task.ReadWrite` | Full read and write access to all gateway capabilities | +| Models.Read | `Models.Read` | Access to LLM model endpoints (chat completions, embeddings) | +| MCP.Read | `MCP.Read` | Access to MCP tool endpoints | +| Agent.Read | `Agent.Read` | Access to agent endpoints | + +> **Full setup guides:** +> - [JWT Authentication Guide](../../../guides/entraid-auth-validation.md) — Gateway-level configuration +> - [JWT Client Identity and Permissions Guide](../../../guides/jwt-client-identity-permissions.md) — Assigning roles to client identities + +**Basic Usage — Require a Single Role:** + +```xml + + + + + + + + + + +``` + +**Multiple Roles (OR logic) — Any Matching Role Grants Access:** + +```xml + + + + + + + +``` + +**How It Works:** + +1. The `security-handler` fragment validates the JWT token (audience, issuer, signature, expiry) +2. After successful JWT validation, the fragment extracts the `roles` claim from the token +3. If `requiredRoles` is set by the product policy, the fragment checks if ANY of the required roles exist in the token's `roles` claim (case-insensitive OR match) +4. If no matching role is found, the request is rejected with HTTP 403 Forbidden + +**Error Response Format:** + +When a required role is missing, the policy returns: + +```json +{ + "error": { + "message": "Access denied. Required app role not found in token.", + "code": "insufficient_role", + "required_roles": "Models.Read", + "token_roles": "Agent.Read" + } +} +``` + +**Configuration Options:** + +| Variable | Description | Example | +|----------|-------------|---------| +| `requiredRoles` | Comma-separated list of accepted app roles (OR logic) | `"Models.Read"` or `"Models.Read,Agent.Read"` | + +> **NOTE:** The `requiredRoles` variable is opt-in. If not set or empty, no role check is performed — this ensures backward compatibility with existing access contracts that only use `jwtRequired`. + +**Output Variables Set by Security Handler:** + +| Variable | Description | Example Values | +|----------|-------------|----------------| +| `jwt-roles` | App roles extracted from the JWT token | `"Models.Read,Agent.Read"` or `""` | + +**Recommended Policy Ordering:** + +```xml + + + + + + + + + + + + + + + + + + + +``` + +### PII Handling Policy + +AI Citadel Gateway supports PII processing using built-in policy fragments that leverage Azure AI Language Service for detection and anonymization. This allows you to protect sensitive data when sending requests to LLM backends. + +#### Available PII Policy Fragments + +| Fragment | Purpose | Description | +|----------|---------|-------------| +| `pii-anonymization` | Inbound | Detects and replaces PII with placeholders before sending to backend | +| `pii-deanonymization` | Outbound | Restores original PII values in the response | +| `pii-state-saving` | Outbound | Logs PII processing activity to Event Hub for auditing | + +#### Configuration Variables + +The following variables can be set in your product policy to configure PII processing: + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `piiAnonymizationEnabled` | Yes | - | Set to `"true"` to enable PII anonymization | +| `piiConfidenceThreshold` | No | `"0.8"` | Minimum confidence score (0.0-1.0) for PII detection | +| `piiEntityCategoryExclusions` | No | `""` | Comma-separated list of PII categories to exclude (e.g., `"PersonType"`) | +| `piiDetectionLanguage` | No | `"en"` | Language code for detection. Use `"auto"` for multilingual content | +| `piiRegexPatterns` | No | `""` | JSON array of custom regex patterns for additional PII detection | +| `piiInputContent` | Yes | - | The content to be anonymized (typically the request body) | +| `piiStateSavingEnabled` | No | `"false"` | Set to `"true"` to enable Event Hub logging | + +>**NOTE:** For a complete list of PII entity categories, see [Azure AI Language PII Entity Categories](https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/concepts/entity-categories). + +#### PII Anonymization/Deanonymization Setup + +PII anonymization works in two phases: +1. **Inbound**: Detect and replace PII with placeholders (e.g., ``, ``) +2. **Outbound**: Restore original PII values in the LLM response + +##### Inbound Configuration + +```xml + + + + + + + + + + + + + + + + + + + + + + + @(context.Variables.GetValueOrDefault("piiAnonymizedContent")) + + + +``` + +##### Outbound Configuration + +```xml + + + + + + + + + + + + + + + + + + + + + @(context.Variables.GetValueOrDefault("piiDeanonymizedContentOutput")) + + + + @(context.Variables.GetValueOrDefault("responseBodyContent")) + + + +``` + +#### Custom Regex Patterns + +Extend Azure AI Language Service NLP detection with custom regex patterns for domain-specific PII: + +```xml + +``` + +>**TIP:** Regex patterns are processed before calling Azure AI Language Service, allowing you to catch domain-specific patterns that NLP might miss. + +#### Event Hub Logging + +When `piiStateSavingEnabled` is set to `"true"`, the `pii-state-saving` fragment logs detailed PII processing information to Event Hub for auditing and compliance purposes. The logged data includes: + +- Operation metadata (timestamp, API name, product, subscription) +- Processing configuration (confidence threshold, exclusions) +- Entity counts and categories detected +- PII mappings (for detailed audit trails) +- Content length metrics + +>**NOTE:** For detailed implementation information and advanced scenarios, see [PII Masking Guide](../../../guides/pii-masking-apim.md). + +## Examples of Applying Policies + +TBD + +### HR PII Support Agent Access Contract Policy + +TBD + +### Retail Shopping Assistant Access Contract Policy + +TBD + +## Extending default policies + +You can extend the out-of-the-box policies by leveraging APIM extensive policy expressions and capabilities. \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicep new file mode 100644 index 000000000..fa1d1736f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicep @@ -0,0 +1,206 @@ +targetScope = 'subscription' + +@description('APIM resource coordinates') +param apim object + +@description('Target Key Vault for storing endpoint and API key secrets') +param keyVault object + +@description('Whether to use Azure Key Vault for storing secrets. If false, secrets will be output instead.') +param useTargetAzureKeyVault bool = true + +@description('Use case descriptor used in naming: ---') +param useCase object + +@description('Map of service codes to their API names in APIM. Example: { OAI: ["azure-openai-service-api"], DOC: ["document-intelligence-api"] }') +param apiNameMapping object + +@description('Required AI services for this use case. Each item: { code: "OAI", endpointSecretName: "OAI_ENDPOINT", apiKeySecretName: "OAI_KEY", policyXmlPath?: "path/to/policy.xml" }') +param services array + +@description('Optional product terms shown to subscribers') +param productTerms string = '' + +// ============================================================================ +// AZURE AI FOUNDRY INTEGRATION PARAMETERS +// ============================================================================ + +@description('Whether to create Azure AI Foundry connection for this use case. If true, foundry parameter must be provided.') +param useTargetFoundry bool = false + +@description('Azure AI Foundry configuration for creating APIM connections. Required if useTargetFoundry is true.') +param foundry object = { + subscriptionId: '' + resourceGroupName: '' + accountName: '' + projectName: '' +} + +@description('Foundry connection configuration options') +param foundryConfig object = { + // Connection naming: if empty, uses useCase naming convention + connectionNamePrefix: '' + // Foundry connection category: ApiManagement or ModelGateway + connectionCategory: 'ApiManagement' + // Whether deployment name is in URL path (true) or request body (false) + deploymentInPath: 'false' + // Share connection to all project users + isSharedToAll: false + // API version for inference calls (empty = APIM defaults) + inferenceAPIVersion: '' + // API version for deployment discovery (empty = APIM defaults) + deploymentAPIVersion: '' + // Static model list (optional) + staticModels: [] + // Custom discovery endpoints (optional - leave empty for APIM defaults) + listModelsEndpoint: '' + getModelEndpoint: '' + deploymentProvider: '' + // Custom headers for requests + customHeaders: {} + // Custom auth configuration + authConfig: {} +} + +var productPostfix = '${useCase.businessUnit}-${useCase.useCaseName}-${useCase.environment}' + +// Default APIM product policy (applied when a service item does not provide policyXmlPath) +var defaultProductPolicyXml = loadTextContent('./policies/default-ai-product-policy.xml') + +resource apimRg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { + scope: subscription(apim.subscriptionId) + name: apim.resourceGroupName +} + +resource apimSvc 'Microsoft.ApiManagement/service@2024-05-01' existing = { + scope: apimRg + name: apim.name +} + +resource kvRg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = if (useTargetAzureKeyVault) { + scope: subscription(keyVault.subscriptionId) + name: keyVault.resourceGroupName +} + +resource kv 'Microsoft.KeyVault/vaults@2023-07-01' existing = if (useTargetAzureKeyVault) { + scope: kvRg + name: keyVault.name +} + +// Onboard each requested service into APIM +module onboard 'modules/apimOnboardService.bicep' = [for s in services: { + name: 'onboard-${s.code}-${productPostfix}' + scope: apimRg + params: { + apimName: apim.name + productId: '${s.code}-${productPostfix}' + productDisplayName: '${s.code} ${useCase.businessUnit} ${useCase.useCaseName} ${useCase.environment}' + productDescription: 'AI Gateway product for ${s.code} - ${useCase.useCaseName}' + productTerms: productTerms + apiNames: apiNameMapping[s.code] + // Use provided policy XML or default + productPolicyXml: contains(s, 'policyXml') && !empty(s.policyXml) ? s.policyXml : defaultProductPolicyXml + subscriptionName: '${s.code}-${productPostfix}-SUB-01' + subscriptionDisplayName: '${s.code}-${productPostfix}-SUB-01' + } +}] + +// Write Key Vault secrets per service (only if useTargetAzureKeyVault is true) +// Create/update KV secrets; normalize names (Key Vault does not allow underscores) +module kvWrites 'modules/kvSecrets.bicep' = [for (s, i) in services: if (useTargetAzureKeyVault) { + name: 'kv-${s.code}-${productPostfix}' + scope: kvRg + params: { + keyVaultName: kv.name + secretNames: [ toLower(replace(s.endpointSecretName, '_', '-')), toLower(replace(s.apiKeySecretName, '_', '-')) ] + secretValues: { + '${toLower(replace(s.endpointSecretName, '_', '-'))}': '${apimSvc.properties.gatewayUrl}/${onboard[i].outputs.apiPath}' + '${toLower(replace(s.apiKeySecretName, '_', '-'))}': onboard[i].outputs.subscriptionPrimaryKey + } + } +}] + +// ============================================================================ +// AZURE AI FOUNDRY CONNECTION DEPLOYMENT +// ============================================================================ +// Create Foundry APIM connections per service (only if useTargetFoundry is true) +// This enables Foundry agents to access AI models through the APIM gateway + +resource foundryRg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = if (useTargetFoundry) { + scope: subscription(foundry.subscriptionId) + name: foundry.resourceGroupName +} + +// Generate connection name based on config or use case naming +var foundryConnectionPrefix = !empty(foundryConfig.connectionNamePrefix) ? foundryConfig.connectionNamePrefix : 'Hub-${useCase.businessUnit}-${useCase.useCaseName}-${useCase.environment}' + +module foundryConnections 'modules/foundryConnection.bicep' = [for (s, i) in services: if (useTargetFoundry) { + name: 'foundry-${s.code}-${productPostfix}' + scope: foundryRg + params: { + aiFoundryAccountName: foundry.accountName + aiFoundryProjectName: foundry.projectName + connectionName: '${foundryConnectionPrefix}-${s.code}' + targetUrl: '${apimSvc.properties.gatewayUrl}/${onboard[i].outputs.apiPath}' + apimSubscriptionKey: onboard[i].outputs.subscriptionPrimaryKey + connectionCategory: foundryConfig.?connectionCategory ?? 'ApiManagement' + isSharedToAll: foundryConfig.?isSharedToAll ?? false + deploymentInPath: foundryConfig.?deploymentInPath ?? 'false' + inferenceAPIVersion: foundryConfig.?inferenceAPIVersion ?? '' + deploymentAPIVersion: foundryConfig.?deploymentAPIVersion ?? '' + staticModels: foundryConfig.?staticModels ?? [] + listModelsEndpoint: foundryConfig.?listModelsEndpoint ?? '' + getModelEndpoint: foundryConfig.?getModelEndpoint ?? '' + deploymentProvider: foundryConfig.?deploymentProvider ?? '' + customHeaders: foundryConfig.?customHeaders ?? {} + authConfig: foundryConfig.?authConfig ?? {} + } + dependsOn: [ + onboard + ] +}] + +output apimGatewayUrl string = apimSvc.properties.gatewayUrl +output useKeyVault bool = useTargetAzureKeyVault + +output products array = [for s in services: { + productId: '${s.code}-${productPostfix}' + displayName: '${s.code} ${useCase.businessUnit} ${useCase.useCaseName} ${useCase.environment}' +}] + +// When using Key Vault, output references to the secret names in Key Vault +output subscriptions array = [for s in services: { + name: '${s.code}-${productPostfix}-SUB-01' + productId: '${s.code}-${productPostfix}' + keyVaultApiKeySecretName: useTargetAzureKeyVault ? toLower(replace(s.apiKeySecretName, '_', '-')) : '' + keyVaultEndpointSecretName: useTargetAzureKeyVault ? toLower(replace(s.endpointSecretName, '_', '-')) : '' +}] + +// When NOT using Key Vault, output the actual endpoints and keys +// These outputs contain runtime values from the onboarding modules +// Note: When useTargetAzureKeyVault=false, the 'endpoints' output will contain sensitive API keys. +// Consumers should handle these values securely (e.g., store in environment variables, CI/CD secrets, etc.) +// The linter warning about secrets in outputs is intentional when Key Vault is disabled +#disable-next-line outputs-should-not-contain-secrets +output endpoints array = [for (s, i) in services: { + code: s.code + productId: '${s.code}-${productPostfix}' + subscriptionName: '${s.code}-${productPostfix}-SUB-01' + endpoint: useTargetAzureKeyVault ? '' : '${apimSvc.properties.gatewayUrl}/${onboard[i].outputs.apiPath}' + // API key is marked secure in the module output, consumers should treat this as sensitive when not using Key Vault + #disable-next-line outputs-should-not-contain-secrets + apiKey: useTargetAzureKeyVault ? '' : string(onboard[i].outputs.subscriptionPrimaryKey) +}] + +// ============================================================================ +// FOUNDRY CONNECTION OUTPUTS +// ============================================================================ +output useFoundry bool = useTargetFoundry + +output foundryConnections array = [for (s, i) in services: useTargetFoundry ? { + code: s.code + connectionName: '${foundryConnectionPrefix}-${s.code}' + targetUrl: '${apimSvc.properties.gatewayUrl}/${onboard[i].outputs.apiPath}' + foundryAccount: foundry.accountName + foundryProject: foundry.projectName +} : {}] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicepparam new file mode 100644 index 000000000..3568f0715 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/main.bicepparam @@ -0,0 +1,383 @@ +using 'main.bicep' + +// ============================================================================ +// Citadel Access Contracts - Use Case Onboarding Parameters +// ============================================================================ +// This parameter file is used to onboard a new use case to the AI Governance Hub. +// It creates APIM products, subscriptions, and optionally stores credentials +// in Azure Key Vault and/or creates Azure AI Foundry connections. +// +// REQUIRED PARAMETERS: apim, keyVault, useCase, apiNameMapping, services +// OPTIONAL PARAMETERS: useTargetAzureKeyVault, productTerms, useTargetFoundry, foundry, foundryConfig +// ============================================================================ + +// ============================================================================ +// REQUIRED: API Management (APIM) Configuration +// ============================================================================ +// Specifies the target APIM instance where the use case APIs will be published. +// This APIM instance should already exist and have the necessary APIs configured. +// +// Properties: +// - subscriptionId: Azure subscription ID where APIM is deployed +// - resourceGroupName: Resource group containing the APIM instance +// - name: Name of the APIM service instance +// +// Example: +// param apim = { +// subscriptionId: 'd287984f-2790-4baa-9520-59ae8169ed0d' +// resourceGroupName: 'rg-ai-hub-gateway-prod' +// name: 'apim-aihub-prod-001' +// } +// ============================================================================ +param apim = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-apim-resource-group' // Replace with your APIM resource group + name: 'apim-instance-name' // Replace with your APIM name +} + +// ============================================================================ +// OPTIONAL: Use Azure Key Vault for Secret Storage +// ============================================================================ +// Determines whether to store endpoint URLs and API keys in Azure Key Vault. +// +// - true (default): Secrets are stored in the specified Key Vault +// - More secure, recommended for production +// - Applications retrieve secrets from Key Vault at runtime +// - Secret names are normalized (underscores replaced with hyphens) +// +// - false: Secrets are output directly from the deployment +// - Useful for testing or CI/CD scenarios +// - Secrets appear in deployment outputs (handle with care!) +// - Consumer is responsible for securing the output values +// +// Default: true +// ============================================================================ +param useTargetAzureKeyVault = false + +// ============================================================================ +// OPTIONAL: Azure Key Vault Configuration (required if useTargetAzureKeyVault=true) +// ============================================================================ +// Specifies the target Key Vault for storing endpoint URLs and API keys. +// This Key Vault will store the generated APIM gateway endpoints and +// subscription keys for secure access by applications. +// +// Properties: +// - subscriptionId: Azure subscription ID where Key Vault is deployed +// - resourceGroupName: Resource group containing the Key Vault +// - name: Name of the Key Vault instance +// +// Note: This parameter is required even if useTargetAzureKeyVault is set to false. +// When useTargetAzureKeyVault=false, secrets will be output directly instead +// of being stored in Key Vault. +// +// Example: +// param keyVault = { +// subscriptionId: 'd287984f-2790-4baa-9520-59ae8169ed0d' +// resourceGroupName: 'rg-ai-hub-gateway-prod' +// name: 'kv-aihub-prod-001' +// } +// ============================================================================ +param keyVault = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-keyvault-resource-group' // Replace with your Key Vault resource group + name: 'kv-instance-name' // Replace with your Key Vault name +} + +// ============================================================================ +// REQUIRED: Use Case Identification +// ============================================================================ +// Defines the use case context used in naming conventions and organization. +// The naming pattern follows: --- +// +// Properties: +// - businessUnit: Your business unit, department, or team name +// Examples: 'Finance', 'Healthcare', 'Sales', 'Engineering' +// +// - useCaseName: Descriptive name for this specific use case +// Examples: 'CustomerSupport', 'DocumentProcessing', 'PatientAssistant' +// Keep it concise and avoid spaces (use PascalCase or kebab-case) +// +// - environment: Deployment environment identifier +// Examples: 'DEV', 'TEST', 'UAT', 'STAGING', 'PROD' +// +// These values are used to construct: +// - Product IDs: OAI-Finance-CustomerSupport-PROD +// - Subscription names: OAI-Finance-CustomerSupport-PROD-SUB-01 +// - Display names and descriptions +// +// Example: +// param useCase = { +// businessUnit: 'Finance' +// useCaseName: 'CustomerSupport' +// environment: 'PROD' +// } +// ============================================================================ +param useCase = { + businessUnit: 'YourBusinessUnit' // Replace with your business unit + useCaseName: 'YourUseCaseName' // Replace with your use case name + environment: 'DEV' // Replace with your environment (DEV, TEST, PROD, etc.) +} + +// ============================================================================ +// REQUIRED: API Name Mapping (bundled APIs) +// ============================================================================ +// Maps service codes to their corresponding API names in APIM. +// These APIs must already exist in your APIM instance before deployment. +// +// Structure: { : [, , ...] } +// +// Common service codes: +// - OAI: Azure OpenAI Service APIs +// - DOC: Document Intelligence APIs +// - CS: Content Safety APIs +// - CV: Computer Vision APIs +// - SEARCH: Azure AI Search APIs +// +// Each service code can map to one or more APIM API names. This allows: +// 1. Multiple APIs per service (e.g., different OpenAI deployments) +// 2. Grouping related APIs under a single product +// 3. Version management (e.g., 'api-v1', 'api-v2') +// +// Example: +// param apiNameMapping = { +// LLM: [ +// 'azure-openai-api', +// 'universal-llm-api' +// ] +// DOC: [ +// 'document-intelligence-api' +// ] +// SRCH: [ +// 'azure-ai-search-index-api' +// ] +// } +// +// Note: API names are case-sensitive and must match exactly with APIM. +// ============================================================================ +param apiNameMapping = { + LLM: ['universal-llm-api', 'azure-openai-api'] + DOC: ['document-intelligence-api', 'document-intelligence-api-legacy'] + SRCH: ['azure-ai-search-index-api'] + OAIRT: ['openai-realtime-ws-api'] + // Add more service codes and API mappings as needed +} + +// ============================================================================ +// REQUIRED: Services Configuration +// ============================================================================ +// Defines the AI services required for this use case. Each service creates: +// - An APIM product +// - A subscription with primary and secondary keys +// - Key Vault secrets (if useTargetAzureKeyVault=true) +// +// Each service object requires: +// - code: Service identifier (must match a key in apiNameMapping) +// Examples: 'LLM', 'DOC', 'SRCH', 'CV' +// +// - endpointSecretName: Name for the endpoint URL secret in Key Vault +// Convention: _ENDPOINT or -ENDPOINT +// Examples: 'OPENAI_ENDPOINT', 'DOCINTELL_ENDPOINT' +// Note: Underscores will be replaced with hyphens in Key Vault +// +// - apiKeySecretName: Name for the API key secret in Key Vault +// Convention: _API_KEY or -API-KEY +// Examples: 'LLM_API_KEY', 'DOCINTELL_API_KEY' +// Note: Underscores will be replaced with hyphens in Key Vault +// +// Optional properties: +// - policyXml: Custom APIM policy XML for this product +// - Can be inline XML string or loaded via loadTextContent() +// - If not provided or empty, default product policy is used +// - Use for service-specific rate limiting, transformation, etc. +// +// Example with inline policy: +// { +// code: 'LLM' +// endpointSecretName: 'OPENAI_ENDPOINT' +// apiKeySecretName: 'OPENAI_API_KEY' +// policyXml: '...' +// } +// +// Example loading policy from file: +// { +// code: 'LLM' +// endpointSecretName: 'OPENAI_ENDPOINT' +// apiKeySecretName: 'OPENAI_API_KEY' +// policyXml: loadTextContent('policies/openai-custom-policy.xml') +// } +// +// Example using default policy (empty or omitted policyXml): +// { +// code: 'DOC' +// endpointSecretName: 'DOCINTELL_ENDPOINT' +// apiKeySecretName: 'DOCINTELL_API_KEY' +// policyXml: '' // Uses default policy from ./policies/default-ai-product-policy.xml +// } +// +// ============================================================================ +param services = [ + { + code: 'LLM' // Service code (must exist in apiNameMapping) + endpointSecretName: 'OPENAI_ENDPOINT' // Key Vault secret name for endpoint URL + apiKeySecretName: 'OPENAI_API_KEY' // Key Vault secret name for API key + policyXml: '' // Optional: Custom policy XML (empty = use default) + } + // Add more services as needed + // { + // code: 'DOC' + // endpointSecretName: 'DOCINTELL_ENDPOINT' + // apiKeySecretName: 'DOCINTELL_API_KEY' + // policyXml: loadTextContent('policies/doc-intel-policy.xml') + // } +] + +// ============================================================================ +// OPTIONAL: Product Terms of Service +// ============================================================================ +// Defines the terms and conditions shown to API subscribers. +// This text is displayed when users subscribe to the APIM product. +// +// Use this to: +// - Define acceptable use policies +// - Specify compliance requirements (HIPAA, GDPR, etc.) +// - Set usage limitations or expectations +// - Include legal disclaimers +// - Reference organizational policies +// +// Examples: +// - 'This API is for internal use only. All usage is subject to company IT policies.' +// - 'By using this service, you agree to handle all data in compliance with GDPR regulations.' +// - 'This service is for healthcare professionals only. All usage must comply with HIPAA regulations.' +// +// Default: '' (empty string - no terms displayed) +// ============================================================================ +param productTerms = '' + +// ============================================================================ +// OPTIONAL: Azure AI Foundry Integration +// ============================================================================ +// Enable this section to automatically create Azure AI Foundry connections +// for each service being onboarded. This allows Foundry agents to access +// AI models through the APIM gateway using the created subscription keys. +// +// When to use: +// - Building AI agents in Azure AI Foundry that need APIM gateway access +// - Implementing the "Bring Your Own AI Gateway" pattern +// - Centralizing AI model access through governance policies +// +// Requirements: +// - Azure AI Foundry account and project must already exist +// - Deploying identity must have Contributor role on the Foundry resource group +// ============================================================================ + +// Set to true to create Foundry APIM connections for each service +param useTargetFoundry = false + +// Azure AI Foundry resource coordinates (required if useTargetFoundry=true) +// Example: +// param foundry = { +// subscriptionId: 'd287984f-2790-4baa-9520-59ae8169ed0d' +// resourceGroupName: 'rg-ai-foundry-prod' +// accountName: 'my-foundry-account' +// projectName: 'my-ai-project' +// } +param foundry = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with Foundry subscription ID + resourceGroupName: 'rg-foundry-resource-group' // Replace with Foundry resource group + accountName: 'foundry-account-name' // Replace with AI Foundry account name + projectName: 'foundry-project-name' // Replace with AI Foundry project name +} + +// ============================================================================ +// OPTIONAL: Foundry Connection Configuration +// ============================================================================ +// Advanced configuration for Foundry APIM connections. These settings control +// how Foundry agents interact with the APIM gateway. +// +// Configuration Options: +// - connectionNamePrefix: Custom prefix for connection names +// Default: Uses '--' from useCase +// Final name: '-' (e.g., 'HR-ChatBot-DEV-LLM') +// +// - deploymentInPath: Controls how model names are passed in requests +// 'true': Model name in URL path (/deployments/{model}/chat/completions) +// 'false': Model name in request body ({"model": "{model}"}) +// +// - isSharedToAll: Share connection with all project users +// true: All users in the Foundry project can use this connection +// false: Only the connection creator can use it initially +// +// - inferenceAPIVersion: API version for chat/embeddings calls +// Leave empty to use APIM defaults (recommended) +// +// - deploymentAPIVersion: API version for model discovery calls +// Leave empty to use APIM defaults (recommended) +// +// - staticModels: Define a fixed list of models (skips dynamic discovery) +// Format: [{ name: 'gpt-4o', properties: { model: { name: 'gpt-4o', version: '2024-11-20', format: 'OpenAI' } } }] +// +// - listModelsEndpoint: Custom endpoint for listing available models +// Default (empty): Uses APIM's /deployments endpoint +// +// - getModelEndpoint: Custom endpoint for getting model details +// Default (empty): Uses APIM's /deployments/{deployment-id} endpoint +// +// - deploymentProvider: Format for model discovery responses ('AzureOpenAI' or 'OpenAI') +// Default (empty): Uses AzureOpenAI format +// +// - customHeaders: Additional headers to include in all requests +// Useful for routing policies or custom metadata +// Example: { 'X-Environment': 'production', 'X-Route-Policy': 'premium' } +// +// - authConfig: Custom authentication header configuration +// Default: Uses 'api-key' header with the subscription key +// Example for Bearer: { type: 'api_key', name: 'Authorization', format: 'Bearer {api_key}' } +// ============================================================================ +param foundryConfig = { + connectionNamePrefix: '' // Empty = use useCase naming convention + deploymentInPath: 'false' // Model name in request body (false for Azure OpenAI /deployments/[deployment-id]) + isSharedToAll: false // Share with all project users + inferenceAPIVersion: '' // Empty = APIM defaults + deploymentAPIVersion: '' // Empty = APIM defaults + staticModels: [] // Empty = use dynamic discovery + listModelsEndpoint: '' // Empty = APIM defaults (/deployments) + getModelEndpoint: '' // Empty = APIM defaults (/deployments/{deployment-id}) + deploymentProvider: '' // Empty = AzureOpenAI format + customHeaders: {} // No custom headers + authConfig: {} // Default api-key header is used +} + +// ============================================================================ +// DEPLOYMENT NOTES +// ============================================================================ +// Prerequisites: +// 1. APIM instance must exist with APIs already configured +// 2. Key Vault must exist (if useTargetAzureKeyVault=true) +// 3. AI Foundry account and project must exist (if useTargetFoundry=true) +// 4. Deploying identity must have permissions: +// - APIM: Contributor or API Management Service Contributor +// - Key Vault: Key Vault Secrets Officer (if using Key Vault) +// - Foundry Resource Group: Contributor (if using Foundry) +// +// Deployment command: +// az deployment sub create \ +// --name \ +// --location \ +// --template-file main.bicep \ +// --parameters main.bicepparam +// +// Outputs: +// - apimGatewayUrl: Base URL of the APIM gateway +// - products: Array of created products with IDs and display names +// - subscriptions: Array of subscriptions with Key Vault secret references +// - endpoints: Array of endpoint URLs and API keys (only if useTargetAzureKeyVault=false) +// - useFoundry: Whether Foundry connections were created +// - foundryConnections: Array of Foundry connection details (if useTargetFoundry=true) +// +// Security considerations: +// - When useTargetAzureKeyVault=false, API keys appear in deployment outputs +// - Always secure deployment outputs in CI/CD pipelines +// - Rotate subscription keys periodically +// - Use managed identities when possible for Key Vault access +// - Foundry connections store API keys securely in the connection config +// ============================================================================ diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimOnboardService.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimOnboardService.bicep new file mode 100644 index 000000000..9243141fc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimOnboardService.bicep @@ -0,0 +1,79 @@ +@description('Existing APIM service name') +param apimName string + +@description('APIM product identifier (resource name). e.g., OAI-HR-InternalAssistant-DEV') +param productId string +@description('APIM product display name') +param productDisplayName string +@description('APIM product description') +param productDescription string = 'AI Gateway product for use case' +@description('APIM product terms') +param productTerms string = '' + +@description('List of API names to include in product') +param apiNames array + +@description('Optional product policy XML; if empty, a default minimal policy is applied') +param productPolicyXml string = '' + +@description('Subscription name (resource name) e.g., -SUB-01') +param subscriptionName string +@description('Subscription display name') +param subscriptionDisplayName string + +var defaultPolicy = '\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n' + +resource apim 'Microsoft.ApiManagement/service@2024-05-01' existing = { + name: apimName +} + +resource product 'Microsoft.ApiManagement/service/products@2024-05-01' = { + name: productId + parent: apim + properties: { + displayName: productDisplayName + description: productDescription + terms: productTerms + subscriptionRequired: true + approvalRequired: false + subscriptionsLimit: 10 + state: 'published' + } +} + +resource productApis 'Microsoft.ApiManagement/service/products/apis@2024-05-01' = [for (apiName, i) in apiNames: { + name: apiName + parent: product +}] + +resource policy 'Microsoft.ApiManagement/service/products/policies@2024-05-01' = { + name: 'policy' + parent: product + properties: { + format: 'rawxml' + value: empty(productPolicyXml) ? defaultPolicy : productPolicyXml + } +} + +resource sub 'Microsoft.ApiManagement/service/subscriptions@2024-05-01' = { + name: subscriptionName + parent: apim + properties: { + displayName: subscriptionDisplayName + scope: product.id + state: 'active' + } +} + +// first API path for endpoint URL construction +var firstApiName = apiNames[0] +resource api 'Microsoft.ApiManagement/service/apis@2024-05-01' existing = { + name: firstApiName + parent: apim +} + +output productName string = product.name +output subscriptionNameOut string = sub.name +@secure() +output subscriptionPrimaryKey string = sub.listSecrets().primaryKey +output apiPath string = api.properties.path diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimProduct.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimProduct.bicep new file mode 100644 index 000000000..d9626ec70 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimProduct.bicep @@ -0,0 +1,60 @@ +@description('Existing APIM service name') +param apimName string + +@description('Product display name') +param productDisplayName string +@description('Product description') +param productDescription string = 'AI Gateway product for a specific use case' +@description('Product identifier (resource name). No spaces. e.g., OAI-HR-InternalAssistant-DEV') +param productId string +@description('Product terms for subscription signup') +param productTerms string = '' +@description('Whether product requires subscription') +param subscriptionRequired bool = true +@description('Whether subscriptions are approval required') +param approvalRequired bool = false +@description('Number of subscriptions a user can have. -1 for unlimited') +param subscriptionsLimit int = 10 +@description('Array of API resourceIds to include in the product') +param apiResourceIds array + +@description('Optional policy XML content to attach to product. If empty, no policy will be set.') +@secure() +param productPolicyXml string = '' + +resource apim 'Microsoft.ApiManagement/service@2024-05-01' existing = { + name: apimName +} + +resource product 'Microsoft.ApiManagement/service/products@2024-05-01' = { + name: productId + parent: apim + properties: { + displayName: productDisplayName + description: productDescription + terms: productTerms + subscriptionRequired: subscriptionRequired + approvalRequired: approvalRequired + subscriptionsLimit: subscriptionsLimit + state: 'published' + } +} + +@batchSize(1) +resource productApis 'Microsoft.ApiManagement/service/products/apis@2024-05-01' = [for apiId in apiResourceIds: { + name: last(split(apiId, '/')) + parent: product +}] + +resource setPolicy 'Microsoft.ApiManagement/service/products/policies@2024-05-01' = if (!empty(productPolicyXml)) { + name: 'policy' + parent: product + properties: { + format: 'xml' + value: productPolicyXml + } +} + +output productName string = product.name +output productResourceId string = product.id +output productDisplay string = product.properties.displayName diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimSubscription.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimSubscription.bicep new file mode 100644 index 000000000..fe5d72ecc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/apimSubscription.bicep @@ -0,0 +1,35 @@ +@description('Existing APIM service name') +param apimName string +@description('Subscription display name') +param subscriptionDisplayName string +@description('Subscription name (resource name). e.g., OAI-HR-InternalAssistant-DEV-SUB-01') +param subscriptionName string +@description('Scope like /products/{productId}') +param scope string +@description('Optional owner userId path in APIM like /users/1') +param ownerId string = '' +@description('Initial subscription state') +@allowed([ 'active', 'submitted', 'rejected', 'cancelled', 'suspended', 'expired' ]) +param state string = 'active' + +resource apim 'Microsoft.ApiManagement/service@2024-05-01' existing = { + name: apimName +} + +resource sub 'Microsoft.ApiManagement/service/subscriptions@2024-05-01' = { + name: subscriptionName + parent: apim + properties: { + displayName: subscriptionDisplayName + scope: scope + ownerId: empty(ownerId) ? null : ownerId + state: state + } +} + +output subscriptionId string = sub.name +output subscriptionResourceId string = sub.id +@secure() +output primaryKey string = sub.listSecrets().primaryKey +@secure() +output secondaryKey string = sub.listSecrets().secondaryKey diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/foundryConnection.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/foundryConnection.bicep new file mode 100644 index 000000000..63856e023 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/foundryConnection.bicep @@ -0,0 +1,179 @@ +/* +=========================================== +Foundry APIM Connection Module +=========================================== + +Creates an APIM connection in an existing Azure AI Foundry project. +This enables Foundry agents to access AI models through the APIM gateway. + +REFERENCES: +- https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/ai-gateway +*/ + +// ============================================================================ +// REQUIRED PARAMETERS +// ============================================================================ + +@description('Name of the AI Foundry account (Cognitive Services account)') +param aiFoundryAccountName string + +@description('Name of the AI Foundry project') +param aiFoundryProjectName string + +@description('Name for the APIM connection') +param connectionName string + +@description('Target URL for the APIM connection (gateway + API path)') +param targetUrl string + +@description('APIM subscription key for API access') +@secure() +param apimSubscriptionKey string + +// ============================================================================ +// OPTIONAL PARAMETERS +// ============================================================================ + +@allowed(['ApiKey']) +@description('Authentication type (ApiKey is currently supported)') +param authType string = 'ApiKey' + +@allowed(['ApiManagement', 'ModelGateway']) +@description('Foundry connection category. Use ModelGateway for portal-compatible model gateway connections.') +param connectionCategory string = 'ApiManagement' + +@description('Share connection to all project users') +param isSharedToAll bool = false + +@allowed(['true', 'false']) +@description('Whether deployment name is in URL path (true) or request body (false)') +param deploymentInPath string = 'false' + +@description('API version for inference calls. Leave empty for APIM defaults.') +param inferenceAPIVersion string = '' + +@description('API version for deployment discovery calls. Leave empty for APIM defaults.') +param deploymentAPIVersion string = '' + +@description('Static model list (optional - use this OR dynamic discovery)') +param staticModels array = [] + +@description('Endpoint for listing models. Leave empty for APIM defaults.') +param listModelsEndpoint string = '' + +@description('Endpoint for getting model details. Leave empty for APIM defaults.') +param getModelEndpoint string = '' + +@allowed(['', 'AzureOpenAI', 'OpenAI']) +@description('Provider format for model discovery responses') +param deploymentProvider string = '' + +@description('Custom headers to include in requests') +param customHeaders object = {} + +@description('Custom authentication configuration') +param authConfig object = {} + +// ============================================================================ +// VARIABLES +// ============================================================================ + +// Validation flags +var hasStaticModels = length(staticModels) > 0 +var hasCustomDiscovery = !empty(listModelsEndpoint) && !empty(getModelEndpoint) && !empty(deploymentProvider) +var hasCustomHeaders = !empty(customHeaders) +var hasAuthConfig = !empty(authConfig) +var hasInferenceAPIVersion = !empty(inferenceAPIVersion) +var hasDeploymentAPIVersion = !empty(deploymentAPIVersion) + +// ============================================================================ +// METADATA CONSTRUCTION +// ============================================================================ + +var baseMetadata = { + deploymentInPath: deploymentInPath +} + +var inferenceVersionMetadata = hasInferenceAPIVersion ? { + inferenceAPIVersion: inferenceAPIVersion +} : {} + +var deploymentVersionMetadata = hasDeploymentAPIVersion ? { + deploymentAPIVersion: deploymentAPIVersion +} : {} + +var modelDiscoveryMetadata = hasCustomDiscovery ? { + modelDiscovery: string({ + listModelsEndpoint: listModelsEndpoint + getModelEndpoint: getModelEndpoint + deploymentProvider: deploymentProvider + }) +} : {} + +var staticModelsMetadata = hasStaticModels && !hasCustomDiscovery ? { + models: string(staticModels) +} : {} + +// Always emit customHeaders (portal requires this field even if empty) +var customHeadersMetadata = { + customHeaders: hasCustomHeaders ? string(customHeaders) : '{}' +} + +var authConfigMetadata = hasAuthConfig ? { + authConfig: string(authConfig) +} : {} + +var metadata = union( + baseMetadata, + inferenceVersionMetadata, + deploymentVersionMetadata, + modelDiscoveryMetadata, + staticModelsMetadata, + customHeadersMetadata, + authConfigMetadata +) + +// ============================================================================ +// EXISTING RESOURCES +// ============================================================================ + +resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiFoundryAccountName +} + +resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { + name: aiFoundryProjectName + parent: aiFoundry +} + +// ============================================================================ +// APIM CONNECTION RESOURCE +// ============================================================================ + +resource apimConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + name: connectionName + parent: aiProject + properties: { + category: connectionCategory + target: targetUrl + authType: authType + isSharedToAll: isSharedToAll + credentials: { + key: apimSubscriptionKey + } + metadata: metadata + } +} + +// ============================================================================ +// OUTPUTS +// ============================================================================ + +@description('Name of the created connection') +output connectionName string = apimConnection.name + +@description('ID of the created connection') +output connectionId string = apimConnection.id + +@description('Target URL for the APIM connection') +output targetUrl string = targetUrl diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/kvSecrets.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/kvSecrets.bicep new file mode 100644 index 000000000..72edbb7f7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/modules/kvSecrets.bicep @@ -0,0 +1,24 @@ +@description('Target Key Vault name where app secrets will be created/updated') +param keyVaultName string + +@description('Secret names to create/update') +param secretNames array + +@description('Secret values in same order as secretNames') +@secure() +param secretValues object + +resource kv 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: keyVaultName +} + +resource kvSecrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [for name in secretNames: { + name: name + parent: kv + properties: { + value: secretValues[name] + contentType: 'string' + } +}] + +output createdSecretNames array = secretNames diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/policies/default-ai-product-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/policies/default-ai-product-policy.xml new file mode 100644 index 000000000..c3f8339bc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/citadel-access-contracts/policies/default-ai-product-policy.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/README.md new file mode 100644 index 000000000..1497440d3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/README.md @@ -0,0 +1,163 @@ +# 🔐 Entra ID Setup for AI Hub Gateway + +## Overview + +Independent deployment module that creates a Microsoft Entra ID App Registration and **directly configures APIM JWT named values** for JWT authentication with the Citadel's AI Governance Hub. + +This is a **standalone operation** — no redeployment of the main landing zone (`azd up`) is required. After running the setup script, APIM is immediately configured for JWT authentication. + +## What Gets Created / Configured + +| Resource | Description | +|----------|-------------| +| **Entra ID App Registration** | App with OAuth2 scope (`access_as_user`), app role (`Task.ReadWrite`), sign-in audience `AzureADMyOrg` | +| **Application ID URI** | Set to `api://{appId}` — used as the JWT audience for token validation | +| **Service Principal** | Service principal for the app registration | +| **Client Secret** | 2-year client secret stored in Key Vault as `ENTRA-APP-CLIENT-SECRET` | +| **APIM Named Value: JWT-TenantId** | Entra ID tenant ID for JWT validation | +| **APIM Named Value: JWT-AppRegistrationId** | App registration client ID (audience) | +| **APIM Named Value: JWT-Issuer** | Token issuer URL (`https://login.microsoftonline.com/{tenantId}/v2.0`) | +| **APIM Named Value: JWT-OpenIdConfigUrl** | OpenID Connect configuration endpoint | +| **azd Environment Variables** | `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_AUDIENCE`, `ENTRA_CLIENT_SECRET` | + +## Prerequisites + +| Requirement | Description | +|-------------|-------------| +| **Azure CLI** | Authenticated (`az login`) | +| **Entra ID Permissions** | `Application.ReadWrite.All` or Application Developer role | +| **Key Vault** | Must exist (deployed by `azd up`) | +| **Key Vault Access** | Deployer needs `Key Vault Secrets Officer` role | +| **APIM Instance** | Must exist (deployed by `azd up`) | +| **APIM Access** | Deployer needs `API Management Service Contributor` role | +| **azd** | Azure Developer CLI installed (for environment variable storage) | + +>NOTE: For Key Vault Access, you can assign the `Key Vault Secrets Officer` role to your user account on the Key Vault. +>- The main deployment does not automatically assign this role. The following command show example execution ```az role assignment create --role "Key Vault Secrets Officer" --assignee "REPLACE@TENANT.onmicrosoft.com" --scope (az keyvault show --name kv-REPLACE --query id --output tsv)``` +>- Azure Key Vault public network access is disabled by default. Ensure you can access Key Vault from the machine that will run the setup script. You may need to enable public network access temporarily or run the script from a VM with access to the Key Vault. + +## Usage + +### Recommended Flow + +```bash +# 1. Deploy the gateway infrastructure first (creates Key Vault, APIM, etc.) +azd up + +# 2. Run the Entra ID setup (creates app registration + configures APIM directly) +cd bicep/infra/entra-id-setup +pwsh ./setup.ps1 + +# Done! APIM is now JWT-ready. No redeployment needed. +# Configure access contracts with jwtAuth.enabled=true to enforce JWT per product. +``` + +### With Explicit Parameters + +```bash +pwsh ./setup.ps1 -EnvironmentName "citadel-dev" -KeyVaultName "kv-abc123" -ApimResourceGroup "rg-citadel-dev" -ApimName "apim-abc123" +``` + +### Using azd Environment (Auto-Detected) + +```bash +# All values auto-detected from azd environment +pwsh ./setup.ps1 +``` + +## How It Works + +```mermaid +sequenceDiagram + participant User as Deployer + participant CLI as Azure CLI + participant Graph as Microsoft Graph API + participant KV as Azure Key Vault + participant APIM as API Management + participant AZD as azd Environment + + User->>CLI: pwsh ./setup.ps1 + CLI->>Graph: Check for existing app registration + alt App exists + Graph-->>CLI: Return existing app + else App doesn't exist + CLI->>Graph: Create app registration + Graph-->>CLI: Return new app (appId, objectId) + end + CLI->>Graph: Ensure service principal exists + CLI->>Graph: Generate client secret (addPassword) + Graph-->>CLI: Return secretText + CLI->>KV: Store secret as ENTRA-APP-CLIENT-SECRET + CLI->>APIM: Create/update JWT-TenantId named value + CLI->>APIM: Create/update JWT-AppRegistrationId named value + CLI->>APIM: Create/update JWT-Issuer named value + CLI->>APIM: Create/update JWT-OpenIdConfigUrl named value + CLI->>AZD: Set AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_AUDIENCE + Note over APIM: APIM is now JWT-ready! + Note over User: No redeployment needed +``` + +## Integration with Main Deployment + +The setup script configures APIM directly — **no redeployment is needed**. After running the script: + +1. **APIM named values** are live and used by the unified `security-handler` policy fragment across all three API endpoints (Azure OpenAI API, Universal LLM API, Unified AI API) +2. **Access contracts** can set `jwtRequired: true` in their product policy to enforce JWT per product +3. **azd env vars** are stored for future `azd up` runs (values flow through `main.bicepparam`) + +### Subsequent `azd up` Runs + +Future redeployments will not overwrite the APIM named values with `'not-configured'` because the azd environment variables (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_AUDIENCE`) are now populated. The Bicep deployment reads these and sets the named values to the correct values. + +## Idempotency + +The script is fully idempotent: +- **App Registration**: Searched by `displayName` — reuses existing if found +- **Service Principal**: Searched by `appId` — reuses existing if found +- **Client Secret**: Always generates a fresh secret and updates Key Vault +- **azd Variables**: Always overwritten with current values + +Re-running the script after a partial failure or for secret rotation is safe. + +## Bring Your Own App Registration + +If you already have an Entra ID App Registration, skip this script and set the values directly: + +```bash +azd env set AZURE_CLIENT_ID "your-client-id" +azd env set AZURE_TENANT_ID "your-tenant-id" +azd env set AZURE_AUDIENCE "api://your-client-id" +azd env set ENTRA_CLIENT_SECRET "your-client-secret" +``` + +Then run `azd up` — the main deployment will use your existing app registration values. + +## Files + +``` +entra-id-setup/ +├── setup.ps1 # PowerShell script for Entra ID provisioning +├── main.bicep # Placeholder Bicep (documentation only) +└── README.md # This file +``` + +## Troubleshooting + +### "Not logged in to Azure CLI" +Run `az login` and ensure you're connected to the correct tenant. + +### "Application.ReadWrite.All permission required" +The deploying user needs Entra ID permissions to create app registrations. Ask your tenant admin to grant `Application.ReadWrite.All` or assign the `Application Developer` role. + +### "Key Vault not found" +Run `azd up` first to create the infrastructure (Key Vault, APIM, etc.), then run this script. + +### "Access denied to Key Vault" +Ensure you have `Key Vault Secrets Officer` role on the Key Vault. The main deployment grants this to the APIM managed identity, but you also need it for your user account. + +## Related + +- [Full Deployment Guide](../../../guides/full-deployment-guide.md) — Complete deployment instructions +- [Entra ID Authentication Guide](../../../guides/entraid-auth-validation.md) — JWT validation configuration +- [Access Contracts](../citadel-access-contracts/README.md) — Per-product JWT enforcement +- [JWT Authentication Validation Notebook](../../../validation/citadel-jwt-authentication-tests.ipynb) — End-to-end JWT testing across all 3 API endpoints diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/setup.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/setup.ps1 new file mode 100644 index 000000000..906cb1fe2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/entra-id-setup/setup.ps1 @@ -0,0 +1,429 @@ +<# +.SYNOPSIS + Creates an Entra ID App Registration and configures APIM JWT named values + for the AI Hub Gateway. No redeployment of the landing zone required. + +.DESCRIPTION + This script is a self-contained Entra ID onboarding tool that: + 1. Creates (or reuses) an Entra ID App Registration with OAuth2 scopes and app roles + 2. Creates a service principal + 3. Generates a client secret and stores it in Key Vault + 4. Configures APIM named values for JWT authentication directly + 5. Stores values as azd environment variables for future deployments + + It is idempotent: re-running finds existing resources and updates configuration. + + After running this script, APIM is immediately configured for JWT authentication. + No 'azd up' redeployment is needed. + +.PARAMETER EnvironmentName + Name of the azd environment (used in app registration display name). + Defaults to AZURE_ENV_NAME from azd environment. + +.PARAMETER KeyVaultName + Name of the Key Vault to store the client secret. + Defaults to KEY_VAULT_NAME from azd environment. + +.PARAMETER ApimResourceGroup + Resource group containing the APIM instance. + Defaults to AZURE_RESOURCE_GROUP from azd environment, or auto-discovered. + +.PARAMETER ApimName + Name of the APIM instance to configure. + Defaults to APIM_NAME from azd environment, or auto-discovered. + +.EXAMPLE + # Using azd environment values (recommended after first azd up): + pwsh ./setup.ps1 + + # Explicit parameters: + pwsh ./setup.ps1 -EnvironmentName "citadel-dev" -KeyVaultName "kv-abc123" -ApimResourceGroup "rg-citadel-dev" -ApimName "apim-abc123" + +.NOTES + Prerequisites: + - Azure CLI authenticated (az login) with Application.ReadWrite.All permission + - Key Vault must exist and deployer must have Key Vault Secrets Officer role + - APIM instance must exist (deployed via azd up) + - azd environment configured (if not passing explicit parameters) +#> + +param( + [string]$EnvironmentName = "", + [string]$KeyVaultName = "", + [string]$ApimResourceGroup = "", + [string]$ApimName = "" +) + +$ErrorActionPreference = "Stop" + +# ── Resolve parameters from azd environment if not provided ── +if ([string]::IsNullOrWhiteSpace($EnvironmentName)) { + $EnvironmentName = azd env get-value AZURE_ENV_NAME 2>$null + if ([string]::IsNullOrWhiteSpace($EnvironmentName)) { + Write-Error "EnvironmentName not provided and AZURE_ENV_NAME not found in azd environment." + exit 1 + } +} + +if ([string]::IsNullOrWhiteSpace($KeyVaultName)) { + $KeyVaultName = azd env get-value KEY_VAULT_NAME 2>$null + if ([string]::IsNullOrWhiteSpace($KeyVaultName)) { + Write-Error "KeyVaultName not provided and KEY_VAULT_NAME not found in azd environment. Run 'azd up' first to create the Key Vault, then re-run this script." + exit 1 + } +} + +if ([string]::IsNullOrWhiteSpace($ApimResourceGroup)) { + $ApimResourceGroup = azd env get-value AZURE_RESOURCE_GROUP 2>$null + if ([string]::IsNullOrWhiteSpace($ApimResourceGroup)) { + # Auto-discover from the APIM resource if APIM_NAME is known + $knownApimName = azd env get-value APIM_NAME 2>$null + if (-not [string]::IsNullOrWhiteSpace($knownApimName)) { + Write-Host " Discovering resource group from APIM '$knownApimName'..." -ForegroundColor Gray + $apimResource = az resource list --name $knownApimName --resource-type "Microsoft.ApiManagement/service" --query "[0].resourceGroup" --output tsv 2>$null + if (-not [string]::IsNullOrWhiteSpace($apimResource)) { + $ApimResourceGroup = $apimResource + Write-Host " Discovered RG: $ApimResourceGroup" -ForegroundColor Gray + } + } + } + if ([string]::IsNullOrWhiteSpace($ApimResourceGroup)) { + Write-Error "ApimResourceGroup not provided and could not be auto-discovered. Provide -ApimResourceGroup or ensure AZURE_RESOURCE_GROUP is set (run 'azd up' with latest template)." + exit 1 + } +} + +if ([string]::IsNullOrWhiteSpace($ApimName)) { + $ApimName = azd env get-value APIM_NAME 2>$null + if ([string]::IsNullOrWhiteSpace($ApimName)) { + # Auto-discover APIM in the resource group + Write-Host " Discovering APIM instance in resource group '$ApimResourceGroup'..." -ForegroundColor Gray + $apimList = az apim list --resource-group $ApimResourceGroup --query "[0].name" --output tsv 2>$null + if (-not [string]::IsNullOrWhiteSpace($apimList)) { + $ApimName = $apimList + Write-Host " Discovered APIM: $ApimName" -ForegroundColor Gray + } else { + Write-Error "ApimName not provided and could not auto-discover APIM in resource group '$ApimResourceGroup'. Provide -ApimName or run 'azd up' first." + exit 1 + } + } +} + +$AppDisplayName = "ai-citadel-gateway-${EnvironmentName}" + +Write-Host "" +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan +Write-Host " Entra ID App Registration Setup" -ForegroundColor Cyan +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan +Write-Host " Environment: $EnvironmentName" -ForegroundColor Gray +Write-Host " App Name: $AppDisplayName" -ForegroundColor Gray +Write-Host " Key Vault: $KeyVaultName" -ForegroundColor Gray +Write-Host " APIM RG: $ApimResourceGroup" -ForegroundColor Gray +Write-Host " APIM Name: $ApimName" -ForegroundColor Gray +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan +Write-Host "" + +# ── Verify Azure CLI auth ── +$account = az account show --output json 2>$null | ConvertFrom-Json +if (-not $account) { + Write-Error "Not logged in to Azure CLI. Run 'az login' first." + exit 1 +} +$tenantId = $account.tenantId +Write-Host " Tenant: $tenantId" -ForegroundColor Gray +Write-Host " User: $($account.user.name)" -ForegroundColor Gray +Write-Host "" + +# ── 1. Create or get existing App Registration ── +Write-Host "[1/6] Checking for existing app registration..." -ForegroundColor Yellow +$existingApp = az ad app list --display-name $AppDisplayName --output json 2>$null | ConvertFrom-Json + +# Define the canonical set of app roles for the gateway +$gatewayAppRoles = @( + @{ + allowedMemberTypes = @("User", "Application") + description = "Full read and write access to all gateway capabilities" + displayName = "ReadWrite" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000002" + value = "Task.ReadWrite" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to LLM model endpoints (chat completions, embeddings)" + displayName = "Models.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000003" + value = "Models.Read" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to MCP tool endpoints" + displayName = "MCP.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000004" + value = "MCP.Read" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to agent endpoints" + displayName = "Agent.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000005" + value = "Agent.Read" + } +) + +if ($existingApp -and $existingApp.Count -gt 0) { + $app = $existingApp[0] + $appId = $app.appId + $appObjectId = $app.id + Write-Host " Found existing: $appId" -ForegroundColor Green + + # ── 1a. Ensure app roles are up-to-date on existing registration ── + Write-Host " Checking app roles..." -ForegroundColor Yellow + $currentRoleIds = @() + if ($app.appRoles) { + $currentRoleIds = $app.appRoles | ForEach-Object { $_.id } + } + $missingRoles = $gatewayAppRoles | Where-Object { $_.id -notin $currentRoleIds } + if ($missingRoles.Count -gt 0) { + Write-Host " Adding $($missingRoles.Count) missing app role(s)..." -ForegroundColor Yellow + $updatedRoles = @($app.appRoles) + @($missingRoles) + $patchPayload = @{ appRoles = $updatedRoles } + $patchTempFile = [System.IO.Path]::GetTempFileName() + try { + $patchPayload | ConvertTo-Json -Depth 10 | Set-Content -Path $patchTempFile -Encoding UTF8 + az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$appObjectId" --headers "Content-Type=application/json" --body "@$patchTempFile" --output none + if ($LASTEXITCODE -ne 0) { + Write-Host " Warning: Failed to update app roles. Continuing..." -ForegroundColor Yellow + } else { + Write-Host " App roles updated successfully" -ForegroundColor Green + } + } finally { + Remove-Item -Path $patchTempFile -Force -ErrorAction SilentlyContinue + } + } else { + Write-Host " App roles are up-to-date" -ForegroundColor Green + } +} else { + Write-Host " Creating new app registration..." -ForegroundColor Yellow + + # Write JSON body to temp file to avoid shell escaping issues with az rest --body + $appPayload = @{ + displayName = $AppDisplayName + signInAudience = "AzureADMyOrg" + isFallbackPublicClient = $true + api = @{ + requestedAccessTokenVersion = 2 + oauth2PermissionScopes = @( + @{ + adminConsentDescription = "Allow access to AI Hub Gateway API" + adminConsentDisplayName = "Access AI Hub Gateway API" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000001" + type = "User" + userConsentDescription = "Allow access to AI Hub Gateway API" + userConsentDisplayName = "Access AI Hub Gateway API" + value = "access_as_user" + } + ) + } + appRoles = @( + @{ + allowedMemberTypes = @("User", "Application") + description = "Full read and write access to all gateway capabilities" + displayName = "ReadWrite" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000002" + value = "Task.ReadWrite" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to LLM model endpoints (chat completions, embeddings)" + displayName = "Models.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000003" + value = "Models.Read" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to MCP tool endpoints" + displayName = "MCP.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000004" + value = "MCP.Read" + } + @{ + allowedMemberTypes = @("User", "Application") + description = "Access to agent endpoints" + displayName = "Agent.Read" + isEnabled = $true + id = "00000000-0000-0000-0000-000000000005" + value = "Agent.Read" + } + ) + requiredResourceAccess = @( + @{ + resourceAppId = "00000003-0000-0000-c000-000000000000" + resourceAccess = @( + @{ + id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" + type = "Scope" + } + ) + } + ) + } + + $tempFile = [System.IO.Path]::GetTempFileName() + try { + $appPayload | ConvertTo-Json -Depth 10 | Set-Content -Path $tempFile -Encoding UTF8 + + $appResult = az rest --method POST --uri "https://graph.microsoft.com/v1.0/applications" --headers "Content-Type=application/json" --body "@$tempFile" --output json | ConvertFrom-Json + + if (-not $appResult -or -not $appResult.appId) { + Write-Error "Failed to create app registration." + exit 1 + } + + $appId = $appResult.appId + $appObjectId = $appResult.id + Write-Host " Created: $appId" -ForegroundColor Green + } finally { + Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue + } +} + +if ([string]::IsNullOrWhiteSpace($appId)) { + Write-Error "App registration ID is empty. Cannot continue." + exit 1 +} + +# ── 1b. Set Application ID URI ── +Write-Host "[1b/6] Configuring Application ID URI..." -ForegroundColor Yellow +$identifierUri = "api://$appId" + +# Check current identifierUris +$currentApp = az rest --method GET --uri "https://graph.microsoft.com/v1.0/applications/$appObjectId" --query "identifierUris" --output json 2>$null | ConvertFrom-Json +if (-not $currentApp -or $currentApp.Count -eq 0 -or $currentApp -notcontains $identifierUri) { + $uriPayload = @{ identifierUris = @($identifierUri) } + $uriTempFile = [System.IO.Path]::GetTempFileName() + try { + $uriPayload | ConvertTo-Json -Depth 5 | Set-Content -Path $uriTempFile -Encoding UTF8 + az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$appObjectId" --headers "Content-Type=application/json" --body "@$uriTempFile" --output none + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to set Application ID URI: $identifierUri" + exit 1 + } + Write-Host " Set Application ID URI: $identifierUri" -ForegroundColor Green + } finally { + Remove-Item -Path $uriTempFile -Force -ErrorAction SilentlyContinue + } +} else { + Write-Host " Application ID URI already configured: $identifierUri" -ForegroundColor Green +} + +# ── 2. Ensure Service Principal exists ── +Write-Host "[2/6] Ensuring service principal exists..." -ForegroundColor Yellow +$spCheck = az ad sp list --filter "appId eq '$appId'" --output json 2>$null | ConvertFrom-Json +if (-not $spCheck -or $spCheck.Count -eq 0) { + az ad sp create --id $appId --output none 2>$null + Write-Host " Created service principal" -ForegroundColor Green +} else { + Write-Host " Service principal exists" -ForegroundColor Green +} + +# ── 3. Generate client secret ── +Write-Host "[3/6] Generating client secret..." -ForegroundColor Yellow +$secretResult = az ad app credential reset --id $appObjectId --display-name "Generated by Entra ID Setup" --years 2 --output json | ConvertFrom-Json +$clientSecret = $secretResult.password + +if ([string]::IsNullOrWhiteSpace($clientSecret)) { + Write-Error "Failed to create client secret." + exit 1 +} +Write-Host " Client secret generated" -ForegroundColor Green + +# ── 4. Store in Key Vault ── +Write-Host "[4/6] Storing client secret in Key Vault..." -ForegroundColor Yellow +$kvResult = az keyvault secret set --vault-name $KeyVaultName --name "ENTRA-APP-CLIENT-SECRET" --value $clientSecret --output json 2>&1 +if ($LASTEXITCODE -ne 0) { + Write-Host "" -ForegroundColor Red + Write-Host " Failed to store secret in Key Vault '$KeyVaultName'." -ForegroundColor Red + Write-Host " Error: $kvResult" -ForegroundColor Red + Write-Host "" -ForegroundColor Yellow + Write-Host " Possible causes:" -ForegroundColor Yellow + Write-Host " - You don't have 'Key Vault Secrets Officer' role on the Key Vault" -ForegroundColor Yellow + Write-Host " - The Key Vault has a firewall blocking access from your IP" -ForegroundColor Yellow + Write-Host " - The Key Vault name '$KeyVaultName' is incorrect" -ForegroundColor Yellow + Write-Host "" -ForegroundColor Yellow + Write-Host " To grant yourself access, run:" -ForegroundColor Cyan + Write-Host " az role assignment create --role `"Key Vault Secrets Officer`" --assignee `"$($account.user.name)`" --scope (az keyvault show --name $KeyVaultName --query id --output tsv)" -ForegroundColor Cyan + Write-Host "" -ForegroundColor Yellow + Write-Host " Continuing without Key Vault storage. The secret is still stored in azd environment." -ForegroundColor Yellow +} else { + Write-Host " Stored as ENTRA-APP-CLIENT-SECRET" -ForegroundColor Green +} + +# ── 5. Configure APIM JWT Named Values ── +Write-Host "[5/6] Configuring APIM JWT named values..." -ForegroundColor Yellow + +$loginEndpoint = "https://login.microsoftonline.com" +$jwtIssuer = "$loginEndpoint/$tenantId/v2.0" +$jwtOpenIdConfigUrl = "$loginEndpoint/$tenantId/v2.0/.well-known/openid-configuration" + +# Named values to create/update in APIM +$namedValues = @( + @{ Name = "JWT-TenantId"; DisplayName = "JWT-TenantId"; Value = $tenantId; Secret = $false } + @{ Name = "JWT-AppRegistrationId"; DisplayName = "JWT-AppRegistrationId"; Value = $appId; Secret = $false } + @{ Name = "JWT-Issuer"; DisplayName = "JWT-Issuer"; Value = $jwtIssuer; Secret = $false } + @{ Name = "JWT-OpenIdConfigUrl"; DisplayName = "JWT-OpenIdConfigUrl"; Value = $jwtOpenIdConfigUrl; Secret = $false } +) + +foreach ($nv in $namedValues) { + $nvName = $nv.Name + + # Check if named value already exists + $existing = az apim nv show --resource-group $ApimResourceGroup --service-name $ApimName --named-value-id $nvName --output json 2>$null | ConvertFrom-Json + + if ($existing) { + # Update existing named value + az apim nv update --resource-group $ApimResourceGroup --service-name $ApimName --named-value-id $nvName --value $nv.Value --output none 2>$null + Write-Host " Updated: $nvName" -ForegroundColor Green + } else { + # Create new named value + az apim nv create --resource-group $ApimResourceGroup --service-name $ApimName --named-value-id $nvName --display-name $nv.DisplayName --value $nv.Value --output none 2>$null + Write-Host " Created: $nvName" -ForegroundColor Green + } +} + +Write-Host " APIM JWT named values configured" -ForegroundColor Green + +# ── 6. Store values in azd environment ── +Write-Host "[6/6] Storing values in azd environment..." -ForegroundColor Yellow +azd env set AZURE_CLIENT_ID $appId 2>$null +azd env set AZURE_TENANT_ID $tenantId 2>$null +azd env set AZURE_AUDIENCE "api://$appId" 2>$null +azd env set ENTRA_CLIENT_SECRET $clientSecret 2>$null +Write-Host " Values stored in azd environment" -ForegroundColor Green + +# ── Summary ── +Write-Host "" +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green +Write-Host " Entra ID Setup Complete!" -ForegroundColor Green +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green +Write-Host " App Display Name: $AppDisplayName" -ForegroundColor Gray +Write-Host " Client ID: $appId" -ForegroundColor Gray +Write-Host " Tenant ID: $tenantId" -ForegroundColor Gray +Write-Host " App ID URI: api://$appId" -ForegroundColor Gray +Write-Host " Audience: api://$appId" -ForegroundColor Gray +Write-Host " KV Secret: ENTRA-APP-CLIENT-SECRET" -ForegroundColor Gray +Write-Host " APIM Named Values: JWT-TenantId, JWT-AppRegistrationId," -ForegroundColor Gray +Write-Host " JWT-Issuer, JWT-OpenIdConfigUrl" -ForegroundColor Gray +Write-Host "" +Write-Host " APIM is now configured for JWT authentication." -ForegroundColor Cyan +Write-Host " No redeployment needed. Configure access contracts" -ForegroundColor Cyan +Write-Host " with jwtAuth.enabled=true to enforce JWT per product." -ForegroundColor Cyan +Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Green +Write-Host "" diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/README.md new file mode 100644 index 000000000..71cbb154d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/README.md @@ -0,0 +1,320 @@ +# 🔌 Azure AI Foundry - APIM Connection Integration + +This module enables Azure AI Foundry projects to access AI models through your Azure API Management (APIM) gateway, supporting the **Bring Your Own AI Gateway** pattern for enterprise AI governance. + +> **💡 Tip**: For automated use case onboarding that includes Foundry connections, Key Vault secrets, and APIM products in a single deployment, see [Citadel Access Contracts](../citadel-access-contracts/README.md). The Access Contracts module integrates this Foundry functionality automatically. + +## 📋 Overview + +The APIM connection integration allows organizations to: + +- **Maintain control** over model endpoints behind your existing governance hub +- **Build agents** that leverage models without exposing them publicly +- **Apply governance** requirements to AI model access through APIM policies +- **Centralize access** through a single, governed AI gateway + +### Architecture + +```mermaid +flowchart LR + subgraph Foundry["Azure AI Foundry"] + Project[AI Project] + Agent[Foundry Agent] + end + + subgraph Connection["APIM Connection"] + Conn[Connection Config] + Creds[Credentials] + end + + subgraph Gateway["Citadel Governance Hub"] + APIM[Azure API Management] + Policy[Policies & Routing] + end + + subgraph Backend["AI Services"] + AOAI[Azure OpenAI] + Models[Other Models] + end + + Project --> Conn + Agent --> Conn + Conn --> APIM + APIM --> Policy + Policy --> AOAI + Policy --> Models +``` + +### Request Flow + +```mermaid +sequenceDiagram + participant Agent as Foundry Agent + participant Conn as APIM Connection + participant APIM as API Management + participant Model as AI Model + + Agent->>Conn: Chat Completion Request + Note over Conn: Add auth headers
Apply custom headers + Conn->>APIM: Forward Request + APIM->>APIM: Apply Policies + APIM->>Model: Route to Backend + Model-->>APIM: Response + APIM-->>Conn: Response + Conn-->>Agent: Response +``` + +--- + +## 📁 Folder Structure + +``` +foundry-integration/ +├── main.bicep # Main deployment template +├── main.bicepparam # Default parameters (APIM defaults) +├── README.md # This documentation +├── modules/ +│ └── apim-connection-common.bicep # Reusable connection module +└── samples/ + ├── static-models.bicepparam # Static model list configuration + ├── dynamic-discovery.bicepparam # Custom discovery endpoints + ├── custom-headers.bicepparam # Custom headers for policies + ├── custom-auth.bicepparam # Custom authentication config + └── full-config.bicepparam # Complete reference example +``` + +--- + +## ✅ Prerequisites + +| Requirement | Description | +|-------------|-------------| +| **Azure Subscription** | Access to subscription containing AI Foundry | +| **AI Foundry Project** | Existing Foundry account and project | +| **APIM Gateway** | Deployed Citadel Governance Hub or APIM instance | +| **APIM Subscription Key** | Valid subscription key for API access | +| **Azure CLI** | Latest version with Bicep support | +| **Permissions** | Contributor on Foundry resource group | + +--- + +## 🚀 Quick Start + +### Step 1: Configure Parameters + +Copy and edit the default parameter file: + +```bash +cd bicep/infra/foundry-integration +cp main.bicepparam hr-agent-citadel-hub-connection.bicepparam +``` + +Edit `hr-agent-citadel-hub-connection.bicepparam`: + +```bicep +using 'main.bicep' + +param aiFoundryAccountName = 'my-foundry-account' +param aiFoundryProjectName = 'my-project' +param connectionName = 'hr-agent-citadel-hub-connection' +param apimGatewayUrl = 'https://my-apim.azure-api.net' +param apiPath = 'models' +param apimSubscriptionKey = 'your-subscription-key' +param deploymentInPath = 'false' +param inferenceAPIVersion = '2024-02-01' +``` + +### Step 2: Deploy + +```bash +# Login and set subscription +az login +az account set --subscription + +# Deploy the connection +az deployment group create --name foundry-integration --resource-group --template-file main.bicep --parameters main-local.bicepparam +``` + +### Step 3: Verify + +Check the connection in Azure AI Foundry portal: +1. Navigate to your Foundry project +2. Go to **Operate → Admin → Connected resources** +3. Verify the connection appears and is active + +--- + +## 🔧 Configuration Options + +### Model Discovery Methods + +Choose **one** of these approaches: + +| Method | When to Use | Parameters | +|--------|-------------|------------| +| **APIM Defaults** | Standard APIM setup with `/deployments` endpoints | Leave `staticModels` empty and no custom discovery | +| **Static Models** | Fixed set of known models, no discovery needed | `staticModels = [...]` | +| **Custom Discovery** | Non-standard endpoints or OpenAI format | `listModelsEndpoint`, `getModelEndpoint`, `deploymentProvider` | + +#### Option 1: APIM Defaults (Recommended) + +APIM provisioned by Citadel Governance Hub supports dynamic model discovery out-of-the-box and it is the recommended approach. + +Uses APIM's standard discovery endpoints: +- List: `/deployments` +- Get: `/deployment/{deploymentName}` +- Provider: `AzureOpenAI` + +Simply don't provide `staticModels` or custom discovery parameters: + +```bicep +// No staticModels or discovery params = APIM defaults +param deploymentInPath = 'false' +param inferenceAPIVersion = '2024-02-01' +``` + +#### Option 2: Static Model List + +Define models explicitly when discovery isn't needed: + +```bicep +param staticModels = [ + { + name: 'gpt-4o' + properties: { + model: { + name: 'gpt-4o' + version: '2024-11-20' + format: 'OpenAI' + } + } + } +] +``` + +#### Option 3: Custom Discovery + +For non-standard endpoints or OpenAI format: + +```bicep +param listModelsEndpoint = '/models' +param getModelEndpoint = '/models/{deploymentName}' +param deploymentProvider = 'OpenAI' // or 'AzureOpenAI' +``` + +### Deployment Path Configuration + +Controls how model names are passed in requests: + +| Value | URL Format | Request Body | +|-------|------------|--------------| +| `'true'` | `/deployments/{model}/chat/completions` | N/A | +| `'false'` | `/chat/completions` | `{"model": "{model}"}` | + +### Custom Headers + +Add headers for APIM policy routing: + +```bicep +param customHeaders = { + 'X-Environment': 'production' + 'X-Route-Policy': 'premium' + 'X-Client-App': 'foundry-agents' +} +``` + +--- + +## 📋 Parameter Reference + +### Required Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `aiFoundryAccountName` | string | Name of the AI Foundry account | +| `aiFoundryProjectName` | string | Name of the project within Foundry | +| `connectionName` | string | Unique name for the connection | +| `apimGatewayUrl` | string | APIM gateway URL (e.g., `https://my-apim.azure-api.net`) | +| `apiPath` | string | API path in APIM (e.g., `openai`) | +| `apimSubscriptionKey` | string | APIM subscription key (secure) | + +### Optional Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `authType` | string | `'ApiKey'` | Authentication type | +| `isSharedToAll` | bool | `false` | Share with all project users | +| `deploymentInPath` | string | `'true'` | Deployment name in URL path | +| `inferenceAPIVersion` | string | `''` | API version for inference | +| `deploymentAPIVersion` | string | `''` | API version for discovery | +| `staticModels` | array | `[]` | Static model list | +| `listModelsEndpoint` | string | `''` | Custom list endpoint | +| `getModelEndpoint` | string | `''` | Custom get endpoint | +| `deploymentProvider` | string | `''` | Discovery provider format | +| `customHeaders` | object | `{}` | Custom request headers | +| `authConfig` | object | `{}` | Custom auth configuration | + +--- + +## 📦 Sample Configurations + +### Using APIM Defaults +```bash +az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters hr-agent-citadel-hub-connection.bicepparam +``` +--- + +## 🧪 Using the Connection in Agents + +After creating the connection, reference it in your Foundry agent: + +```python +import os +from azure.ai.projects import AIProjectClient +from azure.identity import DefaultAzureCredential + +# Configure model deployment name as: {connection-name}/{model-name} +model_deployment = "hr-agent-citadel-hub-connection/gpt-4o" +os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"] = model_deployment + +# Create agent client +client = AIProjectClient( + credential=DefaultAzureCredential(), + endpoint="https://your-foundry.cognitiveservices.azure.com/" +) + +# Create and run agent +agent = client.agents.create_agent( + model=model_deployment, + name="my-agent", + instructions="You are a helpful assistant." +) +``` + +--- + +--- + +## 📚 References + +- [Bring Your Own AI Gateway to Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/ai-gateway) +- [APIM Connection Objects](https://github.com/azure-ai-foundry/foundry-samples/blob/main/infrastructure/infrastructure-setup-bicep/01-connections/apim/APIM-Connection-Objects.md) +- [Foundry Samples Repository](https://github.com/azure-ai-foundry/foundry-samples) +- [Azure AI Projects Agent Samples](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/ai/azure-ai-projects/samples/agents) +- [Citadel Access Contracts](../citadel-access-contracts/README.md) - Automated use case onboarding with Foundry integration + +--- + +## ⚠️ Current Limitations + +| Limitation | Details | +|------------|---------| +| **Preview Status** | Feature is in preview with potential breaking changes | +| **UI Support** | Requires Azure CLI for connection management | +| **Agent Support** | Supports Prompt Agents in the Agent SDK | +| **APIM Tiers** | Only Standard v2 and Premium tiers supported if network isolation is required | +| **Auth Types** | Only ApiKey authentication currently (Entra ID coming soon) | + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/connection-apim.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/connection-apim.bicep new file mode 100644 index 000000000..33bca3be3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/connection-apim.bicep @@ -0,0 +1,207 @@ +/* +Connections enable your AI applications to access tools and objects managed elsewhere in or outside of Azure. + +This comprehensive template demonstrates how to add an APIM connection +with support for ALL APIM metadata parameters. It includes only non-empty parameters in the final configuration. + +This template can handle all APIM connection scenarios from the documentation: +1. Basic APIM with defaults (deploymentInPath + inferenceAPIVersion only) +2. APIM with Deployment API Version (adds deploymentAPIVersion) +3. APIM with Dynamic Discovery (adds modelDiscovery configuration) +4. APIM with Static Model List (adds models array) +5. APIM with Custom Headers (adds customHeaders) +6. Any combination of the above + +The template uses conditional logic to include only non-empty parameters, +making it flexible for any APIM scenario while avoiding empty metadata. + +IMPORTANT: Make sure you are logged into the subscription where the AI Foundry resource exists before deploying. +The connection will be created in the AI Foundry project, so you need to be in that subscription context. +Use: az account set --subscription +*/ + +// ======================================== +// REQUIRED PARAMETERS +// ======================================== + +@description('Resource ID of the AI Foundry project') +param projectResourceId string = '/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/rg-sample/providers/Microsoft.CognitiveServices/accounts/sample-foundry-account/projects/sample-project' + +@description('Resource ID of the APIM service') +param apimResourceId string = '/subscriptions/12345678-1234-1234-1234-123456789abc/resourceGroups/rg-sample/providers/Microsoft.ApiManagement/service/sample-apim' + +@description('Name of the API in APIM') +param apiName string = 'sample-api' + +// ======================================== +// BASIC CONFIGURATION +// ======================================== + +@description('Name for the connection (auto-generated if empty)') +param connectionName string = '' + +@description('APIM subscription name for API key auth') +param apimSubscriptionName string = 'master' + +@allowed(['ApiKey']) +@description('Authentication type') +param authType string = 'ApiKey' + +@description('Share connection to all project users') +param isSharedToAll bool = false + +// 1. REQUIRED - Basic APIM Configuration +@allowed(['true', 'false']) +@description('Whether deployment name is in URL path vs query parameter') +param deploymentInPath string = 'true' + +@description('API version for inference calls (chat completions, embeddings)') +param inferenceAPIVersion string = '' + +// 2. OPTIONAL - Deployment API Version +@description('API version for deployment management (optional)') +param deploymentAPIVersion string = '' + +// 3. OPTIONAL - Dynamic Discovery Configuration +@description('Endpoint for listing models (enables dynamic discovery)') +param listModelsEndpoint string = '' + +@description('Endpoint for getting model details (enables dynamic discovery)') +param getModelEndpoint string = '' + +@description('Provider for model discovery (AzureOpenAI, etc.)') +param deploymentProvider string = '' + +// 4. OPTIONAL - Static Model List +@description('Static model list (array of model objects, optional)') +param staticModels array = [] + +// 5. OPTIONAL - Custom Headers +@description('Custom headers (object with string key-value pairs, optional)') +param customHeaders object = {} + +// 6. OPTIONAL - Custom Authentication Configuration +@description('Authentication configuration (object for custom auth headers)') +param authConfig object = {} + +// Generate connection name if not provided +var apimServiceName = split(apimResourceId, '/')[8] +var generatedConnectionName = 'apim-${apimServiceName}-${apiName}' +var finalConnectionName = connectionName != '' ? connectionName : generatedConnectionName + +// ======================================== +// Conditional Metadata Construction Logic +// All complex objects (arrays, objects) must be serialized using string() function +// ======================================== + +// Helper variables for conditional logic +var hasDeploymentAPIVersion = deploymentAPIVersion != '' +var hasInferenceAPIVersion = inferenceAPIVersion != '' +var hasModelDiscovery = listModelsEndpoint != '' && getModelEndpoint != '' && deploymentProvider != '' +var hasStaticModels = length(staticModels) > 0 +var hasCustomHeaders = !empty(customHeaders) +var hasAuthConfig = !empty(authConfig) + +// Validation: Fail deployment if both static models and dynamic discovery are configured +var bothConfiguredError = hasModelDiscovery && hasStaticModels +var validationMessage = bothConfiguredError ? 'ERROR: Cannot configure both static models and dynamic discovery. Use either staticModels array OR modelDiscovery parameters, not both.' : '' + +// Validation: Fail deployment if neither static models nor dynamic discovery is configured +var neitherConfiguredError = !hasModelDiscovery && !hasStaticModels +var neitherConfiguredMessage = 'ERROR: Must configure either static models (staticModels array) OR dynamic discovery (listModelsEndpoint, getModelEndpoint, deploymentProvider). Cannot have neither.' + +// Force deployment failure if both are configured +resource deploymentValidation 'Microsoft.Resources/deploymentScripts@2023-08-01' = if (bothConfiguredError) { + name: 'validation-error-${uniqueString(resourceGroup().id)}' + location: resourceGroup().location + kind: 'AzurePowerShell' + properties: { + azPowerShellVersion: '8.0' + scriptContent: 'throw "${validationMessage}"' + retentionInterval: 'PT1H' + } +} + +// Force deployment failure if neither are configured +resource configValidation 'Microsoft.Resources/deploymentScripts@2023-08-01' = if (neitherConfiguredError) { + name: 'config-validation-error-${uniqueString(resourceGroup().id)}' + location: resourceGroup().location + kind: 'AzurePowerShell' + properties: { + azPowerShellVersion: '8.0' + scriptContent: 'throw "${neitherConfiguredMessage}"' + retentionInterval: 'PT1H' + } +} + +// Build metadata using conditional union - includes only non-empty parameters +var metadata = union( + // Always include basic configuration + { + deploymentInPath: deploymentInPath + }, + // Conditionally include inference API version + hasInferenceAPIVersion ? { + inferenceAPIVersion: inferenceAPIVersion + } : {}, + // Conditionally include deployment API version + hasDeploymentAPIVersion ? { + deploymentAPIVersion: deploymentAPIVersion + } : {}, + // Conditionally include model discovery configuration + hasModelDiscovery ? { + modelDiscovery: string({ + listModelsEndpoint: listModelsEndpoint + getModelEndpoint: getModelEndpoint + deploymentProvider: deploymentProvider + }) + } : {}, + // Conditionally include static models (only if no dynamic discovery) + hasStaticModels && !hasModelDiscovery ? { + models: string(staticModels) + } : {}, + // Conditionally include custom headers + hasCustomHeaders ? { + customHeaders: string(customHeaders) + } : {}, + // Conditionally include custom auth configuration + hasAuthConfig ? { + authConfig: string(authConfig) + } : {} +) + +// ======================================== +// DEPLOYMENT +// ======================================== + +module apimConnection 'modules/apim-connection-common.bicep' = if (!bothConfiguredError && !neitherConfiguredError) { + name: 'unified-apim-connection' + params: { + projectResourceId: projectResourceId + connectionName: finalConnectionName + apimResourceId: apimResourceId + apiName: apiName + apimSubscriptionName: apimSubscriptionName + authType: authType + isSharedToAll: isSharedToAll + metadata: metadata + } +} + +// ======================================== +// OUTPUTS +// ======================================== + +@description('Name of the created connection') +output connectionName string = finalConnectionName + +@description('Final metadata configuration') +output metadata object = metadata + +@description('Authentication type used') +output authType string = authType + +// Outputs for verification +output hasStaticModels bool = hasStaticModels +output hasModelDiscovery bool = hasModelDiscovery +output hasCustomHeaders bool = hasCustomHeaders diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicep new file mode 100644 index 000000000..0bb365c38 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicep @@ -0,0 +1,282 @@ +/* +=========================================== +Azure AI Foundry - APIM Connection Integration +=========================================== + +This Bicep template creates an APIM connection in an existing Azure AI Foundry project. +It enables Foundry agents to access AI models through your Azure API Management gateway. + +SUPPORTED SCENARIOS: +1. Basic APIM connection with APIM defaults (dynamic discovery using /deployments endpoints) +2. APIM with Static Model List (predefined models, no dynamic discovery needed) +3. APIM with Custom Dynamic Discovery endpoints +4. APIM with Custom Headers for policy routing +5. APIM with Custom Authentication configuration + +DEPLOYMENT: +Make sure you are logged into the subscription where the AI Foundry resource exists. + az account set --subscription + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters main.bicepparam + +REFERENCES: +- https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/ai-gateway +- https://github.com/azure-ai-foundry/foundry-samples/blob/main/infrastructure/infrastructure-setup-bicep/01-connections/apim-and-modelgateway-integration-guide.md +*/ + +// ============================================================================ +// TARGET SCOPE +// ============================================================================ +targetScope = 'resourceGroup' + +// ============================================================================ +// REQUIRED PARAMETERS +// ============================================================================ + +@description('Name of the AI Foundry account (Cognitive Services account)') +param aiFoundryAccountName string + +@description('Name of the AI Foundry project') +param aiFoundryProjectName string + +@description('Name for the APIM connection') +param connectionName string + +@description('APIM gateway URL (e.g., https://my-apim.azure-api.net)') +param apimGatewayUrl string + +@description('API path in APIM (e.g., openai, azure-openai-service-api)') +param apiPath string + +// ============================================================================ +// AUTHENTICATION PARAMETERS +// ============================================================================ + +@description('APIM subscription key for API access') +@secure() +param apimSubscriptionKey string + +@allowed(['ApiKey']) +@description('Authentication type (ApiKey is currently supported)') +param authType string = 'ApiKey' + +@description('Share connection to all project users') +param isSharedToAll bool = false + +// ============================================================================ +// APIM CONFIGURATION PARAMETERS +// ============================================================================ + +@allowed(['true', 'false']) +@description('Whether deployment name is in URL path (true) or request body (false). REQUIRED.') +param deploymentInPath string = 'false' + +@description('API version for inference calls (chat completions, embeddings). Leave empty for APIM defaults.') +param inferenceAPIVersion string = '' + +@description('API version for deployment management/discovery calls. Leave empty for APIM defaults.') +param deploymentAPIVersion string = '' + +// ============================================================================ +// MODEL DISCOVERY CONFIGURATION +// Choose EITHER static models OR dynamic discovery, not both. +// If neither is specified, APIM defaults will be used (/deployments endpoints). +// ============================================================================ + +// Note: When useApimDefaults is true and no static models or custom discovery is configured, +// APIM will automatically use its default discovery endpoints (/deployments). +// This parameter is for documentation purposes - the actual behavior is determined by +// whether staticModels or custom discovery parameters are provided. + +// OPTION 1: Static Model List +@description('Static model list (use this OR dynamic discovery, not both)') +param staticModels array = [] +/* +Example staticModels format: +[ + { + name: 'gpt-4o-deployment' // Deployment name in APIM + properties: { + model: { + name: 'gpt-4o' // Model name + version: '2024-11-20' // Model version + format: 'OpenAI' // Provider format + } + } + } +] +*/ + +// OPTION 2: Dynamic Discovery Configuration +@description('Endpoint for listing models (e.g., /deployments, /models). Required for custom dynamic discovery.') +param listModelsEndpoint string = '/deployments' + +@description('Endpoint for getting model details (e.g., /deployments/{deploymentName}). Required for custom dynamic discovery.') +param getModelEndpoint string = '/deployments/{deployment-id}' + +@allowed(['', 'AzureOpenAI', 'OpenAI']) +@description('Provider format for model discovery responses') +param deploymentProvider string = 'AzureOpenAI' + +// ============================================================================ +// OPTIONAL: CUSTOM HEADERS +// ============================================================================ + +@description('Custom headers to include in requests (key-value pairs)') +param customHeaders object = {} +/* +Example customHeaders format: +{ + 'X-Environment': 'production' + 'X-Route-Policy': 'premium' + 'X-Client-App': 'foundry-agents' +} +*/ + +// ============================================================================ +// OPTIONAL: CUSTOM AUTHENTICATION CONFIGURATION +// ============================================================================ + +@description('Custom authentication configuration for API key header') +param authConfig object = {} +/* +Example authConfig format: +{ + type: 'api_key' + name: 'x-api-key' // Custom header name (default: api-key) + format: '{api_key}' // Format template (default: just the key) +} + +Common patterns: +- Bearer token: { type: 'api_key', name: 'Authorization', format: 'Bearer {api_key}' } +- Custom header: { type: 'api_key', name: 'X-API-Token', format: 'Token {api_key}' } +*/ + +// ============================================================================ +// VARIABLES +// ============================================================================ + +// Validation flags +var hasStaticModels = length(staticModels) > 0 +var hasCustomDiscovery = listModelsEndpoint != '' && getModelEndpoint != '' && deploymentProvider != '' +var hasCustomHeaders = !empty(customHeaders) +var hasAuthConfig = !empty(authConfig) +var hasInferenceAPIVersion = inferenceAPIVersion != '' +var hasDeploymentAPIVersion = deploymentAPIVersion != '' + +// Build target URL +var targetUrl = '${apimGatewayUrl}/${apiPath}' + +// ============================================================================ +// METADATA CONSTRUCTION +// Build metadata object based on provided parameters +// ============================================================================ + +var baseMetadata = { + deploymentInPath: deploymentInPath +} + +var inferenceVersionMetadata = hasInferenceAPIVersion ? { + inferenceAPIVersion: inferenceAPIVersion +} : {} + +var deploymentVersionMetadata = hasDeploymentAPIVersion ? { + deploymentAPIVersion: deploymentAPIVersion +} : {} + +// Model discovery metadata - only include if custom endpoints provided +var modelDiscoveryMetadata = hasCustomDiscovery ? { + modelDiscovery: string({ + listModelsEndpoint: listModelsEndpoint + getModelEndpoint: getModelEndpoint + deploymentProvider: deploymentProvider + }) +} : {} + +// Static models metadata - only include if models provided and no custom discovery +var staticModelsMetadata = hasStaticModels && !hasCustomDiscovery ? { + models: string(staticModels) +} : {} + +// Custom headers metadata +var customHeadersMetadata = hasCustomHeaders ? { + customHeaders: string(customHeaders) +} : {} + +// Auth config metadata +var authConfigMetadata = hasAuthConfig ? { + authConfig: string(authConfig) +} : {} + +// Final metadata combining all configurations +var metadata = union( + baseMetadata, + inferenceVersionMetadata, + deploymentVersionMetadata, + modelDiscoveryMetadata, + staticModelsMetadata, + customHeadersMetadata, + authConfigMetadata +) + +// ============================================================================ +// EXISTING RESOURCES +// ============================================================================ + +resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiFoundryAccountName +} + +resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { + name: aiFoundryProjectName + parent: aiFoundry +} + +// ============================================================================ +// APIM CONNECTION RESOURCE +// ============================================================================ + +resource apimConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + name: connectionName + parent: aiProject + properties: { + category: 'ApiManagement' + target: targetUrl + authType: authType + isSharedToAll: isSharedToAll + credentials: { + key: apimSubscriptionKey + } + metadata: metadata + } +} + +// ============================================================================ +// OUTPUTS +// ============================================================================ + +@description('Name of the created connection') +output connectionName string = apimConnection.name + +@description('ID of the created connection') +output connectionId string = apimConnection.id + +@description('Target URL for the APIM connection') +output targetUrl string = targetUrl + +@description('Authentication type used') +output authType string = authType + +@description('Whether static models are configured') +output hasStaticModels bool = hasStaticModels + +@description('Whether custom discovery is configured') +output hasCustomDiscovery bool = hasCustomDiscovery + +@description('Whether using APIM default discovery (no static models, no custom discovery)') +output usingApimDefaults bool = !hasStaticModels && !hasCustomDiscovery + +@description('Final metadata configuration') +output metadata object = metadata diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicepparam new file mode 100644 index 000000000..7db9fdaf3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/main.bicepparam @@ -0,0 +1,57 @@ +/* +=========================================== +APIM Connection - Using APIM Defaults +=========================================== + +This parameter file creates an APIM connection using APIM's default +dynamic discovery endpoints: +- List Deployments: /deployments +- Get Deployment: /deployments/{deploymentName} +- Provider: AzureOpenAI + +This is the simplest configuration - APIM defaults handle model discovery automatically. + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters main.bicepparam +*/ + +using 'main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ +param connectionName = 'citadel-hub-connection' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' +param apiPath = 'models' // e.g., 'openai', 'models' are supported by default + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' // Use Key Vault reference in production! + +// ============================================================================ +// APIM Configuration +// ============================================================================ +param deploymentInPath = 'false' +param inferenceAPIVersion = '2024-02-01' + +// ============================================================================ +// OPTIONAL: Configurations +// ============================================================================ +param customHeaders = {} + +// Using APIM defaults - no static models or custom discovery parameters provided +// APIM will automatically use /deployments endpoints for model discovery diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/modules/apim-connection-common.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/modules/apim-connection-common.bicep new file mode 100644 index 000000000..190988f23 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/modules/apim-connection-common.bicep @@ -0,0 +1,96 @@ +/* +Common module for creating APIM connections to Azure AI Foundry projects. +This module handles the core connection logic and can be reused across different APIM connection samples. +*/ + +// Project resource parameters +param projectResourceId string +param connectionName string + +// APIM resource parameters +param apimResourceId string +param apiName string +param apimSubscriptionName string = 'master' + +// Connection configuration +param authType string = 'ApiKey' +param isSharedToAll bool = false + +// APIM-specific metadata (passed through from parent template) +param metadata object + +// Extract project information from resource ID +var aiFoundryName = split(projectResourceId, '/')[8] +var projectName = split(projectResourceId, '/')[10] + +// Extract APIM information from resource ID +var apimSubscriptionId = split(apimResourceId, '/')[2] +var apimResourceGroupName = split(apimResourceId, '/')[4] +var apimServiceName = split(apimResourceId, '/')[8] + +// Reference the AI Foundry account +resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiFoundryName + scope: resourceGroup() +} + +// Reference the project within the AI Foundry account +resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { + name: projectName + parent: aiFoundry +} + +// Reference the APIM service (can be in different resource group/subscription) +resource existingApim 'Microsoft.ApiManagement/service@2021-08-01' existing = { + name: apimServiceName + scope: resourceGroup(apimSubscriptionId, apimResourceGroupName) +} + +// Reference the specific API within APIM +resource apimApi 'Microsoft.ApiManagement/service/apis@2021-08-01' existing = { + name: apiName + parent: existingApim +} + +// Reference the APIM subscription to get keys (only for ApiKey auth) +resource apimSubscription 'Microsoft.ApiManagement/service/subscriptions@2021-08-01' existing = { + name: apimSubscriptionName + parent: existingApim +} + +// Create the connection with ApiKey authentication +resource connectionApiKey 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = if (authType == 'ApiKey') { + name: connectionName + parent: aiProject + properties: { + category: 'ApiManagement' + target: '${existingApim.properties.gatewayUrl}/${apimApi.properties.path}' + authType: 'ApiKey' + isSharedToAll: isSharedToAll + credentials: { + key: apimSubscription.listSecrets(apimSubscription.apiVersion).primaryKey + } + metadata: metadata + } +} + +// TODO: Future AAD connection (when role assignments are implemented) +// resource connectionAAD 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = if (authType == 'AAD') { +// name: connectionName +// parent: aiProject +// properties: { +// category: 'ApiManagement' +// target: '${existingApim.properties.gatewayUrl}/${apimApi.properties.path}' +// authType: 'AAD' +// isSharedToAll: isSharedToAll +// credentials: {} +// metadata: metadata +// } +// } + +// Outputs (only from the created connection) +output connectionName string = authType == 'ApiKey' ? connectionApiKey.name : '' +output connectionId string = authType == 'ApiKey' ? connectionApiKey.id : '' +output targetUrl string = '${existingApim.properties.gatewayUrl}/${apimApi.properties.path}' +output authType string = authType +output metadata object = metadata diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-auth.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-auth.bicepparam new file mode 100644 index 000000000..7c907eb43 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-auth.bicepparam @@ -0,0 +1,92 @@ +/* +=========================================== +APIM Connection - Custom Authentication Configuration +=========================================== + +This parameter file creates an APIM connection with custom +authentication header configuration. Use this when: +- Your APIM expects a different header name than 'api-key' +- You need Bearer token format authentication +- You have custom authentication header requirements + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters samples/custom-auth.bicepparam +*/ + +using '../main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ +param connectionName = 'citadel-hub-custom-auth' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' +param apiPath = 'openai' + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' + +// ============================================================================ +// APIM Configuration +// ============================================================================ +param deploymentInPath = 'true' +param inferenceAPIVersion = '2024-02-01' + +// ============================================================================ +// Static Models +// ============================================================================ +param staticModels = [ + { + name: 'gpt-4o' + properties: { + model: { + name: 'gpt-4o' + version: '2024-11-20' + format: 'OpenAI' + } + } + } +] + +// ============================================================================ +// Custom Authentication Configuration +// Customize how the API key is sent in request headers +// ============================================================================ +param authConfig = { + type: 'api_key' + name: 'Ocp-Apim-Subscription-Key' // Custom header name (default: api-key) + format: '{api_key}' // Format template with {api_key} placeholder +} + +/* +Common authConfig patterns: + +1. Standard APIM Subscription Key (default): + { type: 'api_key', name: 'api-key', format: '{api_key}' } + +2. APIM Standard Header: + { type: 'api_key', name: 'Ocp-Apim-Subscription-Key', format: '{api_key}' } + +3. Bearer Token Format: + { type: 'api_key', name: 'Authorization', format: 'Bearer {api_key}' } + +4. Custom X-API-Key Header: + { type: 'api_key', name: 'X-API-Key', format: '{api_key}' } + +5. Custom Token Format: + { type: 'api_key', name: 'X-API-Token', format: 'Token {api_key}' } +*/ diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-headers.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-headers.bicepparam new file mode 100644 index 000000000..d768ee058 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/custom-headers.bicepparam @@ -0,0 +1,74 @@ +/* +=========================================== +APIM Connection - Custom Headers Configuration +=========================================== + +This parameter file creates an APIM connection with custom headers. +Use this when: +- Your APIM policies require custom headers for routing +- You need to include environment or client identification headers +- You have rate limiting or policy headers to include + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters samples/custom-headers.bicepparam +*/ + +using '../main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ +param connectionName = 'citadel-hub-headers' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' +param apiPath = 'openai' + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' + +// ============================================================================ +// APIM Configuration +// ============================================================================ +param deploymentInPath = 'true' +param inferenceAPIVersion = '2024-02-01' + +// ============================================================================ +// Static Models (required when using custom headers without discovery) +// ============================================================================ +param staticModels = [ + { + name: 'gpt-4o' + properties: { + model: { + name: 'gpt-4o' + version: '2024-11-20' + format: 'OpenAI' + } + } + } +] + +// ============================================================================ +// Custom Headers +// These headers are included in all inference requests to APIM +// ============================================================================ +param customHeaders = { + 'X-Environment': 'production' // Environment identifier + 'X-Route-Policy': 'premium' // Routing policy header + 'X-Client-App': 'foundry-agents' // Client application identifier + 'X-Business-Unit': 'finance' // Business unit tracking +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/dynamic-discovery.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/dynamic-discovery.bicepparam new file mode 100644 index 000000000..b5164a0e9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/dynamic-discovery.bicepparam @@ -0,0 +1,67 @@ +/* +=========================================== +APIM Connection - Dynamic Discovery Configuration +=========================================== + +This parameter file creates an APIM connection with custom +dynamic discovery endpoints. Use this when: +- Your APIM uses non-standard endpoints for model discovery +- You need to specify custom endpoints different from APIM defaults +- You're using OpenAI-format APIs instead of AzureOpenAI + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters samples/dynamic-discovery.bicepparam +*/ + +using '../main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ +param connectionName = 'citadel-hub-dynamic' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' +param apiPath = 'openai' + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' + +// ============================================================================ +// APIM Configuration +// ============================================================================ +param deploymentInPath = 'true' +param inferenceAPIVersion = '2024-02-01' +param deploymentAPIVersion = '2024-02-01' // API version for discovery calls + +// ============================================================================ +// Custom Dynamic Discovery Configuration +// Specify custom endpoints if different from APIM defaults +// ============================================================================ + +// Endpoint to list all available models/deployments +// Default APIM uses: /deployments +param listModelsEndpoint = '/deployments' + +// Endpoint to get details for a specific model/deployment +// Use {deploymentName} as placeholder for the model name +// Default APIM uses: /deployments/{deploymentName} +param getModelEndpoint = '/deployments/{deploymentName}' + +// Provider format for parsing discovery responses +// - 'AzureOpenAI': Uses Azure ARM format with value array and properties.model structure +// - 'OpenAI': Uses OpenAI format with data array and id field +param deploymentProvider = 'AzureOpenAI' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/full-config.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/full-config.bicepparam new file mode 100644 index 000000000..78f96c5db --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/full-config.bicepparam @@ -0,0 +1,144 @@ +/* +=========================================== +APIM Connection - Full Configuration Example +=========================================== + +This parameter file demonstrates ALL available configuration options +for an APIM connection. Use this as a reference for understanding +all possible settings. + +NOTE: This is a comprehensive example. In practice, you would only +include the parameters relevant to your specific use case. + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters samples/full-config.bicepparam +*/ + +using '../main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ + +// Name of your AI Foundry account (Cognitive Services resource) +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' + +// Name of the specific project within the AI Foundry account +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ + +// Unique name for this connection (will appear in Foundry portal) +param connectionName = 'citadel-hub-full-config' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ + +// Full URL to your APIM gateway (without trailing slash) +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' + +// API path as configured in APIM (without leading slash) +param apiPath = 'openai' + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ + +// APIM subscription key - use Key Vault reference in production! +// Example Key Vault reference: @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/apim-key) +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' + +// Authentication type (currently only ApiKey is supported) +param authType = 'ApiKey' + +// Whether to share this connection with all users in the project +param isSharedToAll = true + +// ============================================================================ +// APIM Request Configuration +// ============================================================================ + +// How deployment name is passed in requests: +// 'true' - In URL path: /deployments/{name}/chat/completions +// 'false' - In request body: { "model": "{name}" } +param deploymentInPath = 'true' + +// API version for inference calls (chat completions, embeddings) +param inferenceAPIVersion = '2024-02-01' + +// API version for deployment management/discovery calls +param deploymentAPIVersion = '2024-02-01' + +// ============================================================================ +// MODEL CONFIGURATION +// Choose ONE of the following approaches: +// 1. Leave staticModels and discovery params empty (APIM defaults) +// 2. staticModels (define models explicitly) +// 3. Custom discovery endpoints (listModelsEndpoint, getModelEndpoint, deploymentProvider) +// ============================================================================ + +// Option 2: Static model list (comment out if using discovery) +param staticModels = [ + { + name: 'gpt-4o' + properties: { + model: { + name: 'gpt-4o' + version: '2024-11-20' + format: 'OpenAI' + } + } + } + { + name: 'gpt-4o-mini' + properties: { + model: { + name: 'gpt-4o-mini' + version: '2024-07-18' + format: 'OpenAI' + } + } + } + { + name: 'text-embedding-3-large' + properties: { + model: { + name: 'text-embedding-3-large' + version: '1' + format: 'OpenAI' + } + } + } +] + +// Option 3: Custom discovery (uncomment and configure if not using static models) +// param listModelsEndpoint = '/deployments' +// param getModelEndpoint = '/deployments/{deploymentName}' +// param deploymentProvider = 'AzureOpenAI' + +// ============================================================================ +// OPTIONAL: Custom Headers +// Additional headers to include in all inference requests +// ============================================================================ +param customHeaders = { + 'X-Environment': 'production' + 'X-Route-Policy': 'enterprise' + 'X-Client-App': 'foundry-agents' + 'X-Correlation-ID': 'citadel-integration' +} + +// ============================================================================ +// OPTIONAL: Custom Authentication Configuration +// Customize how the API key is sent in request headers +// ============================================================================ +param authConfig = { + type: 'api_key' + name: 'api-key' + format: '{api_key}' +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-auth-config.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-auth-config.json new file mode 100644 index 000000000..675da0ad7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-auth-config.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-RG/providers/Microsoft.CognitiveServices/accounts/YOUR-AI-FOUNDRY-ACCOUNT/projects/YOUR-PROJECT" + }, + "apimResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-APIM-RG/providers/Microsoft.ApiManagement/service/YOUR-APIM-SERVICE" + }, + "apiName": { + "value": "YOUR-API-NAME" + }, + "connectionName": { + "value": "YOUR-CONNECTION-NAME" + }, + "authType": { + "value": "ApiKey" + }, + "isSharedToAll": { + "value": false + }, + "deploymentInPath": { + "value": "true" + }, + "inferenceAPIVersion": { + "value": "2025-03-01-preview" + }, + "deploymentAPIVersion": { + "value": "" + }, + "authConfig": { + "value": { + "type": "api_key", + "name": "YOUR-HEADER-NAME", + "format": "{api_key}" + } + }, + "staticModels": { + "value": [ + { + "name": "YOUR-MODEL-DEPLOYMENT-NAME", + "properties": { + "model": { + "name": "YOUR-MODEL-NAME", + "version": "YOUR-MODEL-VERSION", + "format": "OpenAI" + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-headers.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-headers.json new file mode 100644 index 000000000..ce6df6528 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-custom-headers.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-RG/providers/Microsoft.CognitiveServices/accounts/YOUR-AI-FOUNDRY-ACCOUNT/projects/YOUR-PROJECT" + }, + "apimResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-APIM-RG/providers/Microsoft.ApiManagement/service/YOUR-APIM-SERVICE" + }, + "apiName": { + "value": "YOUR-API-NAME" + }, + "connectionName": { + "value": "YOUR-CONNECTION-NAME" + }, + "authType": { + "value": "ApiKey" + }, + "isSharedToAll": { + "value": false + }, + "deploymentInPath": { + "value": "true" + }, + "inferenceAPIVersion": { + "value": "2025-03-01-preview" + }, + "deploymentAPIVersion": { + "value": "" + }, + "customHeaders": { + "value": { + "YOUR-HEADER-NAME-1": "YOUR-HEADER-VALUE-1", + "YOUR-HEADER-NAME-2": "YOUR-HEADER-VALUE-2" + } + }, + "staticModels": { + "value": [ + { + "name": "YOUR-MODEL-DEPLOYMENT-NAME", + "properties": { + "model": { + "name": "YOUR-MODEL-NAME", + "version": "YOUR-MODEL-VERSION", + "format": "OpenAI" + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-dynamic-discovery.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-dynamic-discovery.json new file mode 100644 index 000000000..58421d285 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-dynamic-discovery.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-RG/providers/Microsoft.CognitiveServices/accounts/YOUR-AI-FOUNDRY-ACCOUNT/projects/YOUR-PROJECT" + }, + "apimResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-APIM-RG/providers/Microsoft.ApiManagement/service/YOUR-APIM-SERVICE" + }, + "apiName": { + "value": "YOUR-API-NAME" + }, + "connectionName": { + "value": "YOUR-CONNECTION-NAME" + }, + "authType": { + "value": "ApiKey" + }, + "isSharedToAll": { + "value": false + }, + "deploymentInPath": { + "value": "true" + }, + "inferenceAPIVersion": { + "value": "2025-03-01-preview" + }, + "deploymentAPIVersion": { + "value": "" + }, + "listModelsEndpoint": { + "value": "/deployments" + }, + "getModelEndpoint": { + "value": "/deployments/{deploymentName}" + }, + "deploymentProvider": { + "value": "AzureOpenAI" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-static-models.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-static-models.json new file mode 100644 index 000000000..692cf04a5 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/parameters-static-models.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "projectResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-RG/providers/Microsoft.CognitiveServices/accounts/YOUR-AI-FOUNDRY-ACCOUNT/projects/YOUR-PROJECT" + }, + "apimResourceId": { + "value": "/subscriptions/YOUR-SUBSCRIPTION-ID/resourceGroups/YOUR-APIM-RG/providers/Microsoft.ApiManagement/service/YOUR-APIM-SERVICE" + }, + "apiName": { + "value": "YOUR-API-NAME" + }, + "connectionName": { + "value": "YOUR-CONNECTION-NAME" + }, + "authType": { + "value": "ApiKey" + }, + "isSharedToAll": { + "value": false + }, + "deploymentInPath": { + "value": "true" + }, + "inferenceAPIVersion": { + "value": "2025-03-01-preview" + }, + "deploymentAPIVersion": { + "value": "" + }, + "staticModels": { + "value": [ + { + "name": "YOUR-MODEL-DEPLOYMENT-NAME-1", + "properties": { + "model": { + "name": "YOUR-MODEL-NAME-1", + "version": "YOUR-MODEL-VERSION-1", + "format": "OpenAI" + } + } + }, + { + "name": "YOUR-MODEL-DEPLOYMENT-NAME-2", + "properties": { + "model": { + "name": "YOUR-MODEL-NAME-2", + "version": "YOUR-MODEL-VERSION-2", + "format": "OpenAI" + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/static-models.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/static-models.bicepparam new file mode 100644 index 000000000..4d171d4b2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/foundry-integration/samples/static-models.bicepparam @@ -0,0 +1,84 @@ +/* +=========================================== +APIM Connection - Static Models Configuration +=========================================== + +This parameter file creates an APIM connection with a predefined +static list of models. Use this when: +- You have a fixed set of known models +- Dynamic discovery is not available/needed +- You want explicit control over available models + +USAGE: + az deployment group create \ + --resource-group \ + --template-file main.bicep \ + --parameters samples/static-models.bicepparam +*/ + +using '../main.bicep' + +// ============================================================================ +// REQUIRED: AI Foundry Configuration +// ============================================================================ +param aiFoundryAccountName = 'YOUR-AI-FOUNDRY-ACCOUNT-NAME' +param aiFoundryProjectName = 'YOUR-AI-FOUNDRY-PROJECT-NAME' + +// ============================================================================ +// REQUIRED: Connection Configuration +// ============================================================================ +param connectionName = 'citadel-hub-static' + +// ============================================================================ +// REQUIRED: APIM Gateway Configuration +// ============================================================================ +param apimGatewayUrl = 'https://YOUR-APIM-NAME.azure-api.net' +param apiPath = 'openai' + +// ============================================================================ +// REQUIRED: Authentication +// ============================================================================ +param apimSubscriptionKey = 'YOUR-APIM-SUBSCRIPTION-KEY' + +// ============================================================================ +// APIM Configuration +// ============================================================================ +param deploymentInPath = 'true' +param inferenceAPIVersion = '2024-02-01' + +// ============================================================================ +// Static Model List +// Define all models that should be available through this connection +// ============================================================================ +param staticModels = [ + { + name: 'gpt-4o' // Deployment name (how you reference it in API calls) + properties: { + model: { + name: 'gpt-4o' // Actual model name + version: '2024-11-20' // Model version + format: 'OpenAI' // Provider format + } + } + } + { + name: 'gpt-4o-mini' + properties: { + model: { + name: 'gpt-4o-mini' + version: '2024-07-18' + format: 'OpenAI' + } + } + } + { + name: 'text-embedding-ada-002' + properties: { + model: { + name: 'text-embedding-ada-002' + version: '2' + format: 'OpenAI' + } + } + } +] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/README.md new file mode 100644 index 000000000..cb79f58cc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/README.md @@ -0,0 +1,465 @@ +# 🚀 Citadel Backend Contract + +## Overview + +Automate the onboarding of LLM backends to your APIM-based AI Gateway with a streamlined, infrastructure-as-code approach using **Bicep parameter files** (`.bicepparam`). + +This package enables dynamic LLM backend routing without modifying APIM policies: + +- 📦 **Automatic Backend Creation**: Create APIM backends from configuration +- ⚖️ **Load Balancing**: Distribute requests across multiple backends for the same model +- 🔄 **Automatic Failover**: Route to healthy backends when others are unavailable +- 🔌 **Multi-Provider Support**: Microsoft Foundry, Azure OpenAI, Amazon Bedrock, and external LLM providers +- 📝 **Declarative Configuration**: Simple `.bicepparam` files for version control + +## What Gets Created + +| Resource | Description | +|----------|-------------| +| **APIM Backends** | Individual backend resources for each LLM endpoint | +| **Backend Pools** | Load-balanced pools for models with multiple backends | +| **Policy Fragments** | Dynamic routing logic for model-based routing | +| **Get Available Models Fragment** | Returns available model deployments with capabilities (similar to Azure Cognitive Services API) | +| **Metadata Config Fragment** | Centralized model routing config for the Unified AI API — always deployed with backend onboarding to stay in sync | + +## Prerequisites + +- Existing deployment of AI Citadel Governance Hub with: + - User-assigned managed identity configured + - APIs for Universal LLM API and Azure OpenAI API +- LLM backends deployed and accessible: + - Microsoft Foundry with model deployments + - Azure OpenAI resources with model deployments + - Amazon Bedrock with foundation model access + - APIM can reach the target backends from network perspective +- Verify APIM's user assigned managed identity has required roles: + - `Cognitive Services OpenAI User` for Azure OpenAI + - `Cognitive Services User` for Microsoft Foundry +- For Amazon Bedrock: + - AWS IAM user with Bedrock access and access keys generated + - Provide `awsAccessKey`, `awsSecretKey`, and `awsRegion` parameters when deploying — these are stored as secret APIM named values (`aws-access-key`, `aws-secret-key`, `aws-region`) + - If these parameters are not provided, the named values are created with a `NOT_CONFIGURED` placeholder and the gateway returns a `500 AWSCredentialsNotConfigured` error at runtime when a Bedrock backend is invoked + +## Quick Start + +### 1. Copy the Parameter Template + +```bash +cp main.bicepparam llm-backends-dev-local.bicepparam +``` + +### 2. Configure Your Backends + +Edit `llm-backends-dev-local.bicepparam`: + +```bicep +using 'main.bicep' + +param apim = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-citadel-governance-hub' // Replace with your APIM resource group + name: 'apim-citadel-governance-hub' // Replace with your APIM name +} + +param apimManagedIdentity = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-citadel-governance-hub' // Replace with your identity resource group + name: 'id-apim-citadel' // Replace with your managed identity name +} + +param llmBackendConfig = [ + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-4o-mini", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-07-18", "retirementDate": "2026-09-30" }, + { "name": "gpt-4o", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-11-20", "retirementDate": "2026-09-30" }, + { "name": "gpt-4.1", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2025-04-14", "retirementDate": "2026-10-14", "apiVersion": "2025-04-01-preview", "timeout": 180 }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "Phi-4", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "Microsoft", "modelVersion": "3", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "text-embedding-3-large", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-04-14" } + ] + priority: 1 + weight: 100 + } +] +``` + +### 3. Deploy + +```bash +az deployment sub create --name llm-backend-onboarding --location swedencentral --template-file main.bicep --parameters llm-backends-generated-local.bicepparam +``` + +## Configuration Reference + +### Backend Configuration Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `backendId` | string | Yes | Unique identifier for the backend (usually the name of the backend resource) | +| `backendType` | string | Yes | `ai-foundry`, `azure-openai`, `aws-bedrock`, or `external` | +| `endpoint` | string | Yes | Base URL of the LLM service | +| `authScheme` | string | Yes | `managedIdentity`, `apiKey`, or `token` | +| `supportedModels` | array | Yes | Array of model objects (see Model Object Properties below) | +| `priority` | number | No | 1-5, default 1 (lower = higher priority) | +| `weight` | number | No | 1-1000, default 100 (load balancing weight) | + +### Model Object Properties + +Each model in the `supportedModels` array has these properties: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `name` | string | Yes | Model name (e.g., `gpt-4o`, `DeepSeek-R1`, `Phi-4`) | +| `sku` | string | No | SKU name for the deployment (default: `Standard`). Used in `get-available-models` response | +| `capacity` | number | No | Capacity/TPM quota (default: 100). Used in `get-available-models` response | +| `modelFormat` | string | No | Model format identifier, e.g., `OpenAI`, `DeepSeek`, `Microsoft` (default: `OpenAI`). Used in `get-available-models` response | +| `modelVersion` | string | No | Version of the model (default: `1`). Used in `get-available-models` response | +| `retirementDate` | string (date) | No | Optional retirement date for the model. Used in `get-available-models` response | +| `apiVersion` | string | No | API version for OpenAI-type requests (default: `2024-02-15-preview`). Used by Unified AI API for backend routing | +| `timeout` | number | No | Request timeout in seconds (default: `120`). Used by Unified AI API for per-model timeout configuration | +| `inferenceApiVersion` | string | No | API version for inference-type requests (e.g., `2024-05-01-preview`). Used by Unified AI API for non-OpenAI models | + +### Backend Types + +#### AI Foundry (`ai-foundry`) +- Uses Azure AI Foundry project endpoints +- Endpoint format: `https://.cognitiveservices.azure.com/` +- Authentication: Managed identity with Cognitive Services scope +- No URL rewriting required + +#### Azure OpenAI (`azure-openai`) +- Uses Azure OpenAI Service endpoints +- Endpoint format: `https://.openai.azure.com/` +- Authentication: Managed identity with Cognitive Services scope +- Automatic URL rewriting to include `/deployments/{model}/` + +#### External (`external`) +- Uses external LLM provider endpoints +- Authentication: API key or backend credentials +- No URL rewriting + +#### Amazon Bedrock (`aws-bedrock`) +- Uses Amazon Bedrock runtime endpoints +- Endpoint format: `https://bedrock-runtime..amazonaws.com` +- Authentication: AWS Signature Version 4 (SigV4) using IAM access keys stored as APIM named values +- Path construction: `/model/{model-id}/converse` +- Requires additional parameters: `awsAccessKey`, `awsSecretKey`, `awsRegion` +- See [Microsoft Learn: Import Amazon Bedrock API](https://learn.microsoft.com/en-us/azure/api-management/amazon-bedrock-passthrough-llm-api) for detailed APIM integration guidance + +## Example Configurations + +### Single AI Foundry Backend + +```bicep +param llmBackendConfig = [ + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-4o-mini", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-07-18", "retirementDate": "2026-09-30" }, + { "name": "gpt-4o", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-11-20", "retirementDate": "2026-09-30" }, + { "name": "gpt-4.1", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2025-04-14", "retirementDate": "2026-10-14", "apiVersion": "2025-04-01-preview", "timeout": 180 }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "Phi-4", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "Microsoft", "modelVersion": "3", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "text-embedding-3-large", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-04-14" } + ] + priority: 1 + weight: 100 + } +] +``` + +### Load Balancing Across Regions + +As `DeepSeek-R1` is available in 2 different backends, the onboarding script will automatically create a backend pool for `DeepSeek-R1` and distribute traffic based on the specified priority/weights. + +```bicep +param llmBackendConfig = [ + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-4o-mini", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-07-18", "retirementDate": "2026-09-30" }, + { "name": "gpt-4o", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-11-20", "retirementDate": "2026-09-30" }, + { "name": "gpt-4.1", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2025-04-14", "retirementDate": "2026-10-14", "apiVersion": "2025-04-01-preview", "timeout": 180 }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "Phi-4", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "Microsoft", "modelVersion": "3", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "text-embedding-3-large", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-04-14" } + ] + priority: 1 + weight: 100 + } + { + backendId: 'aif-citadel-secondary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-1.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-5", "sku": "GlobalStandard", "capacity": 50, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-02-05" }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" } + ] + priority: 2 + weight: 50 + } +] +``` + +### Mixed Providers + +This is mixing Azure OpenAI and Microsoft Foundry backends. Common models across providers will be automatically load balanced (like `DeepSeek-R1` and `text-embedding-3-large` in the below example), while unique models will be routed to their specific backend. + +```bicep +param llmBackendConfig = [ + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-4o-mini", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-07-18", "retirementDate": "2026-09-30" }, + { "name": "gpt-4o", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-11-20", "retirementDate": "2026-09-30" }, + { "name": "gpt-4.1", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2025-04-14", "retirementDate": "2026-10-14", "apiVersion": "2025-04-01-preview", "timeout": 180 }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "Phi-4", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "Microsoft", "modelVersion": "3", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "text-embedding-3-large", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-04-14" } + ] + priority: 1 + weight: 100 + } + { + backendId: 'aoai-eastus-gpt4' + backendType: 'azure-openai' + endpoint: 'https://YOUR-AOAI-RESOURCE.openai.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-5", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2025-08-07", "retirementDate": "2027-02-05" }, + { "name": "DeepSeek-R1", "sku": "GlobalStandard", "capacity": 1, "modelFormat": "DeepSeek", "modelVersion": "1", "retirementDate": "2099-12-30", "inferenceApiVersion": "2024-05-01-preview" }, + { "name": "text-embedding-3-large", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "1", "retirementDate": "2027-04-14" } + ] + priority: 1 + weight: 100 + } +] +``` + +### Amazon Bedrock Backend + +This example adds an Amazon Bedrock backend alongside Azure backends. The `aws-bedrock` backend type uses AWS SigV4 authentication via IAM access keys stored as APIM named values. + +```bicep +param llmBackendConfig = [ + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { "name": "gpt-4o", "sku": "GlobalStandard", "capacity": 100, "modelFormat": "OpenAI", "modelVersion": "2024-11-20", "retirementDate": "2026-09-30" } + ] + priority: 1 + weight: 100 + } + { + backendId: 'bedrock-us-east-1' + backendType: 'aws-bedrock' + endpoint: 'https://bedrock-runtime.us-east-1.amazonaws.com' + authScheme: 'awsSigV4' + supportedModels: [ + { "name": "us.anthropic.claude-3-5-haiku-20241022-v1:0", "sku": "OnDemand", "capacity": 1, "modelFormat": "Anthropic", "modelVersion": "1", "retirementDate": "2099-12-30" } + { "name": "us.anthropic.claude-3-5-sonnet-20241022-v2:0", "sku": "OnDemand", "capacity": 1, "modelFormat": "Anthropic", "modelVersion": "2", "retirementDate": "2099-12-30" } + { "name": "us.amazon.nova-pro-v1:0", "sku": "OnDemand", "capacity": 1, "modelFormat": "Amazon", "modelVersion": "1", "retirementDate": "2099-12-30" } + ] + priority: 1 + weight: 100 + } +] + +// AWS credentials for Bedrock authentication +param awsAccessKey = '' +param awsSecretKey = '' +param awsRegion = 'us-east-1' +``` + +> **Important**: Store AWS access keys securely. Consider using Azure Key Vault references for the APIM named values in production. See [Create IAM user access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-key-self-managed.html#Using_CreateAccessKey) for generating AWS access keys. + +## Request Flow + +``` +1. Client → APIM Gateway + POST /models/chat/completions + Body: { "model": "gpt-4o", "messages": [...] } + +2. Extract Model + → requestedModel = "gpt-4o" + +3. Find Backend Pool + → matches "gpt-4o-backend-pool" (load balanced) + or direct backend if single provider + +4. Authenticate + → Get managed identity token + → Set Authorization header + +5. Route to Backend + → Forward to healthy backend in pool + +6. Return Response + → Client receives response with usage headers +``` + +## Get Available Models API + +The `get-available-models` policy fragment enables an API endpoint that returns all available model deployments with their capabilities, similar to the Azure Cognitive Services deployment list API. + +This policy fragment is designed to support Microsoft Foundry integration with Citadel Governance Hub, allowing clients to query available models dynamically from Foundry portal experience. + +>NOTE: This policy fragment is included in the `/deployments` get operation of the Universal LLM API by default. Currently this Microsoft Foundry feature is in `preview` and may change in future releases. + +### Usage + +Include the policy fragment in any API operation to return available models: + +```xml + + + +``` + +### Response Format + +```json +{ + "value": [ + { + "id": "aif-citadel-primary", + "type": "ai-foundry", + "name": "gpt-4o", + "sku": { "name": "GlobalStandard", "capacity": 100 }, + "properties": { + "model": { "format": "OpenAI", "name": "gpt-4o", "version": "2024-11-20" }, + "capabilities": { "chatCompletion": "true" }, + "provisioningState": "Succeeded" + } + }, + { + "id": "aif-citadel-primary", + "type": "ai-foundry", + "name": "gpt-4o-mini", + "sku": { "name": "GlobalStandard", "capacity": 100 }, + "properties": { + "model": { "format": "OpenAI", "name": "gpt-4o-mini", "version": "2024-11-20" }, + "capabilities": { "chatCompletion": "true" }, + "provisioningState": "Succeeded" + } + } + ] +} +``` + +The response is dynamically generated based on the `llmBackendConfig` parameter, using the optional metadata fields (`sku`, `capacity`, `modelFormat`, `modelVersion`). + +## Monitoring + +### Key Metrics + +Connected Application Insights to APIM provides insights into backend performance: + +| Metric | Description | +|--------|-------------| +| Application Map | Visual representation of dependencies performance | +| Performance | For both operations and dependencies | +| Failures | Failures by backend | +| Latency | Response time per backend | + +### Application Insights Query + +```kusto +// this query calculates LLM backend duration percentiles and count by target +let start=ago(24h); +let end=now(); +let timeGrain=5m; + +let dataset=dependencies +// additional filters can be applied here +| where timestamp > start and timestamp < end +| where client_Type != "Browser" +; +// calculate duration percentiles and count for all dependencies (overall) +dataset +| summarize avg_duration=sum(itemCount * duration)/sum(itemCount), percentiles(duration, 50, 95, 99), count_=sum(itemCount) +| project operation_Name="Overall", avg_duration, percentile_duration_50, percentile_duration_95, percentile_duration_99, count_ +| union(dataset +// change 'target' on the below line to segment by a different property +| summarize avg_duration=sum(itemCount * duration)/sum(itemCount), percentiles(duration, 50, 95, 99), count_=sum(itemCount) by target +| sort by avg_duration desc, count_ desc +) +``` + +## Troubleshooting + +### "Model not supported" Error + +1. Check model name in `supportedModels` array (case-insensitive) +2. Verify backend pool was created in APIM +3. Review policy fragment deployment + +### "403 Forbidden" Error + +1. Check `allowedBackendPools` policy variable +2. Verify RBAC configuration +3. Review product/subscription access + +### "401 Unauthorized" Error + +1. Verify APIM's managed identity has required roles: + - `Cognitive Services OpenAI User` for Azure OpenAI + - `Cognitive Services User` for AI Foundry +2. For Amazon Bedrock: Verify AWS IAM access keys are valid and stored as named values (`aws-access-key`, `aws-secret-key`, `aws-region`) +3. `Unauthorized model access` indicates used access contract product is restricted for the model +4. Check named value `uami-client-id` is set correctly to APIM's managed identity client ID + +### "500 AWSCredentialsNotConfigured" Error + +This error means an `aws-bedrock` backend was matched but the AWS credentials named values are still set to the `NOT_CONFIGURED` placeholder. To fix: + +1. Redeploy with the `awsAccessKey`, `awsSecretKey`, and `awsRegion` parameters set to valid values, **or** +2. Manually update the APIM named values `aws-access-key`, `aws-secret-key`, and `aws-region` in the Azure Portal + +## Files + +``` +llm-backend-onboarding/ +├── main.bicep # Main deployment template +├── main.bicepparam # Parameter file template +├── README.md # This file +└── modules/ + ├── llm-backends.bicep # Creates APIM backend resources + ├── llm-backend-pools.bicep # Creates load-balanced pools + ├── llm-policy-fragments.bicep # Generates routing policy fragments + ├── universal-llm-api.bicep # Creates Universal LLM API + └── policies/ + ├── frag-set-backend-pools.xml + ├── frag-set-backend-authorization.xml + ├── frag-set-target-backend-pool.xml + ├── frag-set-llm-requested-model.xml + ├── frag-set-llm-usage.xml + ├── frag-get-available-models.xml + ├── frag-metadata-config.xml # Unified AI API metadata (always deployed) + ├── universal-llm-api-policy.xml + ├── universal-llm-openapi.json + └── models-inference-openapi.json +``` + +## Related Guides + +- [Citadel Access Contracts](../citadel-access-contracts/README.md) - Configure use case access to governance hub +- [Gateway Routing Guide](../../../guides/llm-routing-architecture.md) - Detailed routing architecture guide +- [Full Deployment Guide](../../../guides/full-deployment-guide.md) - Complete Citadel deployment guide diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicep new file mode 100644 index 000000000..aea6db88d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicep @@ -0,0 +1,233 @@ +targetScope = 'subscription' + +/** + * @module LLM Backend Onboarding + * @description Deploys LLM backends, backend pools, and policy fragments to an existing APIM instance + * + * This deployment enables dynamic LLM backend routing in Azure API Management (APIM), allowing: + * - Route requests to multiple LLM providers (Azure OpenAI, AI Foundry, external providers) + * - Load balance across multiple backends for the same model + * - Automatic failover when backends are unavailable + * - Flexible authentication schemes per backend + * + * Usage: + * az deployment sub create \ + * --location \ + * --template-file main.bicep \ + * --parameters main.bicepparam + */ + +// ============================================================================ +// PARAMETERS +// ============================================================================ + +@description('APIM resource coordinates for the existing API Management instance') +param apim object + +@description('User-assigned managed identity resource coordinates for APIM authentication') +param apimManagedIdentity object + +@description('Configuration array for LLM backends') +@metadata({ + description: ''' + Each backend object should have: + - backendId: Unique identifier (used in APIM backend resource name) + - backendType: 'ai-foundry' | 'azure-openai' | 'aws-bedrock' | 'external' + - endpoint: Base URL of the LLM service (e.g., https://xxx.services.ai.azure.com/models) + - authScheme: 'managedIdentity' | 'apiKey' | 'token' (legacy, replaced by authType) + - authType: (Optional) 'managed-identity' | 'aws-sigv4' | 'api-key-bearer' | 'api-key-header' | 'none' + - authConfig: (Optional) { namedValueKey: 'apim-named-value-name' } — credential source for api-key auth types + - supportedModels: Array of model objects, each with: + - name: Model name (required) + - sku: (Optional) SKU name for deployment, default 'Standard' + - capacity: (Optional) Capacity/TPM quota, default 100 + - modelFormat: (Optional) Model format identifier, default 'OpenAI' + - modelVersion: (Optional) Version of the model, default '1' + - retirementDate: (Optional) Retirement date for the model in YYYY-MM-DD format + - apiVersion: (Optional) API version for OpenAI-type requests, default '2024-02-15-preview' + - timeout: (Optional) Request timeout in seconds, default 120 + - inferenceApiVersion: (Optional) API version for inference-type requests (e.g., '2024-05-01-preview') + - priority: (Optional) 1-5, default 1 (lower = higher priority) + - weight: (Optional) 1-1000, default 100 (higher = more traffic) + ''' + example: [ + { + backendId: 'ai-foundry-eastus-gpt4' + backendType: 'ai-foundry' + endpoint: 'https://my-foundry.services.ai.azure.com/' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-11-20', retirementDate: '2026-09-30' } + { name: 'gpt-4o-mini', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-07-18', retirementDate: '2026-09-30' } + { name: 'Phi-4', sku: 'GlobalStandard', capacity: 1, modelFormat: 'Microsoft', modelVersion: '3', inferenceApiVersion: '2024-05-01-preview' } + ] + priority: 1 + weight: 100 + } + ] +}) +param llmBackendConfig array + +@description('Whether to configure circuit breaker for backends (recommended for production)') +param configureCircuitBreaker bool = true + +@description('AWS access key ID for Amazon Bedrock authentication (required when using aws-bedrock backends)') +@secure() +param awsAccessKey string = '' + +@description('AWS secret access key for Amazon Bedrock authentication (required when using aws-bedrock backends)') +@secure() +param awsSecretKey string = '' + +@description('AWS region for Amazon Bedrock (e.g., us-east-1)') +param awsRegion string = '' + +@description('Model alias definitions for grouping models under a single alias name') +@metadata({ + description: ''' + Each alias object should have: + - name: Alias name that clients use (e.g., "gpt-advanced") + - models: Array of model names included in the alias (must exist in llmBackendConfig) + - strategy: (Optional) "priority" (default, first available) or "weighted" (round-robin) + - weights: (Optional) Array of weights matching models array (required when strategy is "weighted") + ''' +}) +param modelAliases array = [] + +@description('Key Vault name for storing backend credentials (required when backends use api-key auth with Key Vault references)') +param keyVaultName string = '' + +// @description('Whether to deploy the Universal LLM API (set to false if API already exists)') +// param deployUniversalLlmApi bool = true + +// @description('API path for the Universal LLM API (default: llm)') +// param universalLlmApiPath string = 'llm' + +// ============================================================================ +// EXISTING RESOURCES +// ============================================================================ + +resource apimRg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { + scope: subscription(apim.subscriptionId) + name: apim.resourceGroupName +} + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + scope: apimRg + name: apim.name +} + +resource identityRg 'Microsoft.Resources/resourceGroups@2022-09-01' existing = { + scope: subscription(apimManagedIdentity.subscriptionId) + name: apimManagedIdentity.resourceGroupName +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = { + scope: identityRg + name: apimManagedIdentity.name +} + +// ============================================================================ +// MODULES +// ============================================================================ + +/** + * Step 1: Create LLM Backends + * Creates individual APIM backend resources for each LLM endpoint + */ +module llmBackends 'modules/llm-backends.bicep' = { + name: 'llm-backends-deployment-${uniqueString(deployment().name)}' + scope: apimRg + params: { + apimServiceName: apim.name + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + configureCircuitBreaker: configureCircuitBreaker + } +} + +/** + * Step 2: Create Backend Pools + * Groups backends by supported models for load balancing and failover + */ +module llmBackendPools 'modules/llm-backend-pools.bicep' = { + name: 'llm-backend-pools-deployment-${uniqueString(deployment().name)}' + scope: apimRg + params: { + apimServiceName: apim.name + backendDetails: llmBackends.outputs.backendDetails + } +} + +/** + * Step 3: Generate Policy Fragments + * Creates dynamic policy fragments with backend pool configurations + */ +module llmPolicyFragments 'modules/llm-policy-fragments.bicep' = { + name: 'llm-policy-fragments-deployment-${uniqueString(deployment().name)}' + scope: apimRg + params: { + apimServiceName: apim.name + policyFragmentConfig: llmBackendPools.outputs.policyFragmentConfig + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + awsAccessKey: awsAccessKey + awsSecretKey: awsSecretKey + awsRegion: awsRegion + modelAliases: modelAliases + keyVaultName: keyVaultName + } +} + +// /** +// * Step 4: Deploy Universal LLM API (Optional) +// * Creates the unified API endpoint that uses the dynamic backends +// */ +// module universalLlmApi 'modules/universal-llm-api.bicep' = if (deployUniversalLlmApi) { +// name: 'universal-llm-api-deployment-${uniqueString(deployment().name)}' +// scope: apimRg +// params: { +// apimServiceName: apim.name +// apiPath: universalLlmApiPath +// } +// } + +// ============================================================================ +// OUTPUTS +// ============================================================================ + +@description('Name of the APIM service') +output apimServiceName string = apim.name + +@description('Gateway URL for the APIM service') +output apimGatewayUrl string = apimService.properties.gatewayUrl + +@description('Array of created backend IDs') +output backendIds array = llmBackends.outputs.backendIds + +@description('Array of created backend pool names') +output poolNames array = llmBackendPools.outputs.poolNames + +@description('Mapping of models to their backend pools') +output modelToPoolMap object = llmBackendPools.outputs.modelToPoolMap + +@description('Mapping of models to direct backends (single-backend models)') +output modelToBackendMap object = llmBackendPools.outputs.modelToBackendMap + +@description('All supported models across all backends') +output supportedModels array = union([], map(llmBackendConfig, config => config.supportedModels)) + +// @description('Universal LLM API endpoint (if deployed)') +// output universalLlmApiEndpoint string = deployUniversalLlmApi ? '${apimService.properties.gatewayUrl}/${universalLlmApiPath}' : '' + +@description('Policy fragment names') +output policyFragments object = { + setBackendPools: llmPolicyFragments.outputs.setBackendPoolsFragmentName + setBackendAuthorization: llmPolicyFragments.outputs.setBackendAuthorizationFragmentName + setTargetBackendPool: llmPolicyFragments.outputs.setTargetBackendPoolFragmentName + getAvailableModels: llmPolicyFragments.outputs.getAvailableModelsFragmentName + validateModelAccess: llmPolicyFragments.outputs.validateModelAccessFragmentName + metadataConfig: llmPolicyFragments.outputs.metadataConfigFragmentName + responsesIdSecurity: llmPolicyFragments.outputs.responsesIdSecurityFragmentName + responsesIdCacheStore: llmPolicyFragments.outputs.responsesIdCacheStoreFragmentName +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicepparam new file mode 100644 index 000000000..1af75bae5 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/main.bicepparam @@ -0,0 +1,239 @@ +using 'main.bicep' + +// ============================================================================ +// LLM Backend Onboarding - Parameter File +// ============================================================================ +// This parameter file configures LLM backends for an existing APIM instance. +// It creates backend resources, backend pools, and policy fragments for +// dynamic model-based routing. +// +// REQUIRED PARAMETERS: apim, apimManagedIdentity, llmBackendConfig +// OPTIONAL PARAMETERS: configureCircuitBreaker, deployUniversalLlmApi, universalLlmApiPath, tags +// ============================================================================ + +// ============================================================================ +// REQUIRED: API Management (APIM) Configuration +// ============================================================================ +// Specifies the target APIM instance where LLM backends will be onboarded. +// This APIM instance should already exist and have the necessary networking +// and security configurations in place. +// +// Properties: +// - subscriptionId: Azure subscription ID where APIM is deployed +// - resourceGroupName: Resource group containing the APIM instance +// - name: Name of the APIM service instance +// ============================================================================ +param apim = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-citadel-governance-hub' // Replace with your APIM resource group + name: 'apim-citadel-governance-hub' // Replace with your APIM name +} + +// ============================================================================ +// REQUIRED: APIM Managed Identity Configuration +// ============================================================================ +// Specifies the user-assigned managed identity used by APIM for backend +// authentication. This identity must have the appropriate RBAC roles: +// - Cognitive Services OpenAI User (for Azure OpenAI backends) +// - Cognitive Services User (for AI Foundry backends) +// +// Properties: +// - subscriptionId: Azure subscription ID where the identity is deployed +// - resourceGroupName: Resource group containing the managed identity +// - name: Name of the user-assigned managed identity +// ============================================================================ +param apimManagedIdentity = { + subscriptionId: '00000000-0000-0000-0000-000000000000' // Replace with your subscription ID + resourceGroupName: 'rg-citadel-governance-hub' // Replace with your identity resource group + name: 'id-apim-citadel' // Replace with your managed identity name +} + +// ============================================================================ +// REQUIRED: LLM Backend Configuration Array +// ============================================================================ +// Defines all LLM backends that APIM will route requests to. Each backend +// object should have: +// +// Required Properties: +// - backendId: Unique identifier (used in APIM backend resource name) +// - backendType: 'ai-foundry' | 'azure-openai' | 'external' +// - endpoint: Base URL of the LLM service +// - authType: 'managed-identity' | 'apiKey' | 'token' +// - supportedModels: Array of model objects (see below) +// +// Optional Properties (for load balancing): +// - priority: 1-5, default 1 (lower = higher priority for load balancing) +// - weight: 1-1000, default 100 (higher = more traffic share) +// +// Model Object Properties (in supportedModels array): +// - name: Model name (required) - e.g., 'gpt-4o', 'DeepSeek-R1' +// - sku: SKU name for the deployment (default: 'Standard') +// - capacity: Capacity/TPM quota (default: 100) +// - modelFormat: Model format identifier, e.g., 'OpenAI', 'DeepSeek', 'Microsoft' (default: 'OpenAI') +// - modelVersion: Version of the model (default: '1') +// - retirementDate: Retirement date in YYYY-MM-DD format (optional) +// - apiVersion: API version for OpenAI-type requests (default: '2024-02-15-preview') +// - timeout: Request timeout in seconds (default: 120) +// - inferenceApiVersion: API version for inference-type requests, e.g., '2024-05-01-preview' (optional, for non-OpenAI models) +// +// Example configurations for different scenarios are shown below. +// ============================================================================ +param llmBackendConfig = [ + // ---------------------------------- + // AI Foundry Backend - Primary + // ---------------------------------- + // This backend connects to an Azure AI Foundry project endpoint + // Models deployed in AI Foundry use the OpenAI-compatible inference API + { + backendId: 'aif-citadel-primary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-0.cognitiveservices.azure.com/' // Replace with your AI Foundry endpoint + authType: 'managed-identity' + // Each model has its own metadata for get-available-models response + supportedModels: [ + { name: 'gpt-4o-mini', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-07-18', retirementDate: '2026-09-30' } + { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-11-20', retirementDate: '2026-09-30' } + { name: 'gpt-4.1', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2025-04-14', retirementDate: '2026-10-14', apiVersion: '2025-04-01-preview', timeout: 180 } + { name: 'DeepSeek-R1', sku: 'GlobalStandard', capacity: 1, modelFormat: 'DeepSeek', modelVersion: '1', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + { name: 'Phi-4', sku: 'GlobalStandard', capacity: 1, modelFormat: 'Microsoft', modelVersion: '3', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + { name: 'text-embedding-3-large', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '1', retirementDate: '2027-04-14' } + ] + priority: 1 + weight: 100 + } + + // ---------------------------------- + // AI Foundry Backend - Secondary (Different Region) + // ---------------------------------- + // For load balancing and failover, add backends in different regions + // Models shared with primary backend will be load balanced + { + backendId: 'aif-citadel-secondary' + backendType: 'ai-foundry' + endpoint: 'https://aif-RESOURCE_TOKEN-1.cognitiveservices.azure.com/' // Replace with your secondary AI Foundry endpoint + authType: 'managed-identity' + supportedModels: [ + { name: 'gpt-5', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2025-08-07', retirementDate: '2027-02-05' } + { name: 'DeepSeek-R1', sku: 'GlobalStandard', capacity: 1, modelFormat: 'DeepSeek', modelVersion: '1', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + { name: 'text-embedding-3-large', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '1', retirementDate: '2027-04-14' } + ] + priority: 2 + weight: 50 + } + + // ---------------------------------- + // Azure OpenAI Backend (Optional) + // ---------------------------------- + // Uncomment to add Azure OpenAI Service endpoints + // { + // backendId: 'aoai-eastus-gpt4' + // backendType: 'azure-openai' + // endpoint: 'https://YOUR-AOAI-RESOURCE.openai.azure.com/' + // authType: 'managed-identity' // replaces legacy authScheme + // supportedModels: [ + // { name: 'gpt-4', sku: 'Standard', capacity: 120, modelFormat: 'OpenAI', modelVersion: '0613' } + // { name: 'gpt-35-turbo', sku: 'Standard', capacity: 120, modelFormat: 'OpenAI', modelVersion: '0613' } + // { name: 'text-embedding-ada-002', sku: 'Standard', capacity: 120, modelFormat: 'OpenAI', modelVersion: '2' } + // ] + // priority: 1 + // weight: 100 + // } + + // ---------------------------------- + // AWS Bedrock Mantle Backend (Optional - OpenAI-Compatible) + // ---------------------------------- + // Uncomment to add AWS Bedrock Mantle OpenAI-compatible endpoints + // { + // backendId: 'bedrock-mantle-us-east-1' + // backendType: 'aws-bedrock-mantle' + // endpoint: 'https://bedrock-mantle.us-east-1.api.aws' + // authType: 'api-key-bearer' + // authConfig: { + // namedValueKey: 'bedrock-mantle-api-key' + // keyVaultSecretUri: 'https://YOUR-KEYVAULT.vault.azure.net/secrets/bedrock-mantle-api-key' // Key Vault reference (recommended) + // // secretValue: 'your-api-key-here' // Explicit value (testing only — do NOT use in production) + // } + // supportedModels: [ + // { name: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0', sku: 'OnDemand', capacity: 1, modelFormat: 'Anthropic', modelVersion: '2' } + // ] + // priority: 1 + // weight: 100 + // } + + // ---------------------------------- + // Gemini OpenAI-Compatible Backend (Optional) + // ---------------------------------- + // Uncomment to add Google Gemini OpenAI-compatible endpoints + // { + // backendId: 'gemini-openai' + // backendType: 'gemini-openai' + // endpoint: 'https://generativelanguage.googleapis.com' + // authType: 'api-key-bearer' + // authConfig: { + // namedValueKey: 'gemini-api-key' + // keyVaultSecretUri: 'https://YOUR-KEYVAULT.vault.azure.net/secrets/gemini-api-key' // Key Vault reference (recommended) + // // secretValue: 'your-api-key-here' // Explicit value (testing only — do NOT use in production) + // } + // supportedModels: [ + // { name: 'gemini-2.5-flash', sku: 'OnDemand', capacity: 1, modelFormat: 'Google', modelVersion: '1' } + // ] + // priority: 1 + // weight: 100 + // } + + // ---------------------------------- + // AI Foundry with API Key Auth (Optional — alternative to managed identity) + // ---------------------------------- + // Example: Same backend type (ai-foundry) with different auth type + // { + // backendId: 'aif-external-partner' + // backendType: 'ai-foundry' + // endpoint: 'https://partner-foundry.cognitiveservices.azure.com/' + // authType: 'api-key-header' // Uses api-key header instead of managed identity + // authConfig: { + // namedValueKey: 'partner-foundry-api-key' + // keyVaultSecretUri: 'https://YOUR-KEYVAULT.vault.azure.net/secrets/partner-foundry-key' + // } + // supportedModels: [ + // { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-11-20' } + // ] + // priority: 2 + // weight: 50 + // } +] + +// ============================================================================ +// OPTIONAL: Circuit Breaker Configuration +// ============================================================================ +// Enable circuit breaker for backend resilience. When enabled, APIM will +// temporarily stop routing to backends that are experiencing failures. +// +// Recommended: true for production environments +// ============================================================================ +param configureCircuitBreaker = true + +// ============================================================================ +// OPTIONAL: Model Aliases +// ============================================================================ +// Define model aliases that group multiple models under a single client-facing name. +// Clients use the alias name in requests, and the gateway resolves to an actual model. +// +// Strategy options: +// - 'priority': Use models in order (first available wins). Default. +// - 'weighted': Distribute traffic based on weights (round-robin with weights). +// +// Examples: +// param modelAliases = [ +// { +// name: 'gpt-advanced' +// models: ['gpt-5', 'gpt-4.1', 'gpt-4o'] +// strategy: 'priority' +// } +// { +// name: 'embeddings-default' +// models: ['text-embedding-3-large'] +// strategy: 'priority' +// } +// ] +// ============================================================================ +param modelAliases = [] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backend-pools.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backend-pools.bicep new file mode 100644 index 000000000..f1ae86e30 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backend-pools.bicep @@ -0,0 +1,142 @@ +/** + * @module llm-backend-pools + * @description Creates APIM backend pools that group backends by supported models + * + * This module analyzes the LLM backend configuration and creates backend pools. + * Each pool groups backends that support the same model, enabling: + * - Load balancing across multiple backends for the same model + * - Automatic failover if one backend becomes unavailable + * - Priority-based and weighted routing strategies + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('Array of backend details from llm-backends module output') +param backendDetails array + +// ------------------ +// VARIABLES +// ------------------ + +// Extract model names from supportedModels objects for each backend +// supportedModels can be either array of strings (legacy) or array of objects with 'name' property (new) +var normalizedBackendDetails = [for backend in backendDetails: { + backendId: backend.backendId + backendType: backend.backendType + authType: backend.?authType ?? '' + authConfigNamedValue: backend.?authConfigNamedValue ?? '' + resourceId: backend.resourceId + priority: backend.priority + weight: backend.weight + // Extract model names - handle both string arrays and object arrays + modelNames: map(backend.supportedModels, m => m.name) +}] + +// Group backends by supported models to create backend pools +var modelToBackendsMap = reduce(normalizedBackendDetails, {}, (acc, backend) => union(acc, reduce(backend.modelNames, {}, (modelAcc, model) => union(modelAcc, { + '${model}': union( + contains(acc, model) ? acc[model] : [], + [{ + backendId: backend.backendId + backendType: backend.backendType + authType: backend.authType + authConfigNamedValue: backend.authConfigNamedValue + resourceId: backend.resourceId + priority: backend.priority + weight: backend.weight + }] + ) +})))) + +// Create pool configurations only for models supported by multiple backends +// Note: Pool names must only contain letters, numbers, and hyphens (dots are stripped from model names) +var poolConfigs = map( + filter(items(modelToBackendsMap), (item) => length(item.value) > 1), + (item) => { + modelName: item.key + poolName: '${replace(item.key, '.', '')}-backend-pool' + backends: item.value + } +) + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName + // Note: We don't use this directly as pools are created as children of the service +} + +// Create backend pools for models with multiple backend options +resource backendPools 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = [for config in poolConfigs: { + name: config.poolName + parent: apimService + properties: { + description: 'Backend pool for model: ${config.modelName}' + type: 'Pool' + #disable-next-line BCP035 + pool: { + services: [for backend in config.backends: { + id: '/backends/${backend.backendId}' + priority: backend.priority + weight: backend.weight + }] + } + } +}] + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Array of created backend pool names') +output poolNames array = [for (config, i) in poolConfigs: backendPools[i].name] + +@description('Mapping of models to their backend pool names') +output modelToPoolMap object = reduce(poolConfigs, {}, (acc, config) => union(acc, { + '${config.modelName}': config.poolName +})) + +@description('Mapping of models to backend IDs (for models with single backend)') +output modelToBackendMap object = reduce( + filter(items(modelToBackendsMap), (item) => length(item.value) == 1), + {}, + (acc, item) => union(acc, { + '${item.key}': item.value[0].backendId + }) +) + +@description('Complete pool configurations including backend details') +output poolDetails array = [for (config, i) in poolConfigs: { + modelName: config.modelName + poolName: config.poolName + poolType: 'pool' + backends: config.backends +}] + +@description('Configuration for policy fragment generation') +output policyFragmentConfig object = { + backendPools: map(poolConfigs, config => { + poolName: config.poolName + poolType: length(config.backends) > 0 ? config.backends[0].backendType : 'mixed' + authType: length(config.backends) > 0 ? config.backends[0].authType : '' + authConfigNamedValue: length(config.backends) > 0 ? config.backends[0].authConfigNamedValue : '' + supportedModels: [config.modelName] + }) + directBackends: map( + filter(items(modelToBackendsMap), (item) => length(item.value) == 1), + (item) => { + poolName: item.value[0].backendId + poolType: item.value[0].backendType + authType: item.value[0].authType + authConfigNamedValue: item.value[0].authConfigNamedValue + supportedModels: [item.key] + } + ) +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backends.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backends.bicep new file mode 100644 index 000000000..d0d1f3d21 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-backends.bicep @@ -0,0 +1,122 @@ +/** + * @module llm-backends + * @description Creates APIM backends for LLM services (AI Foundry, Azure OpenAI, Amazon Bedrock, and other providers) + * + * This module dynamically creates backend resources based on the provided configuration array. + * Each backend represents an LLM endpoint that can serve one or more model deployments. + * + * Supported backend types: + * - ai-foundry: Azure AI Foundry projects with model deployments + * - azure-openai: Azure OpenAI Service endpoints + * - aws-bedrock: Amazon Bedrock endpoints with AWS SigV4 authentication + * - external: Other LLM providers (OpenAI, Anthropic, etc.) + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('User-assigned managed identity client ID for authentication') +param managedIdentityClientId string + +@description('Configuration array for LLM backends') +param llmBackendConfig array + +@description('Whether to configure circuit breaker for backends') +param configureCircuitBreaker bool = true + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +// Create individual backends for each LLM endpoint +resource llmBackends 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = [for (config, i) in llmBackendConfig: { + name: config.backendId + parent: apimService + properties: { + description: 'LLM Backend: ${config.backendType} - ${config.backendId} - Supports models: ${join(map(config.supportedModels, m => m.name), ', ')}' + url: config.endpoint + protocol: 'http' + + // Circuit breaker configuration for resilience + circuitBreaker: configureCircuitBreaker ? { + rules: [ + { + failureCondition: { + count: 3 + errorReasons: [ + 'Server errors' + ] + interval: 'PT5M' + statusCodeRanges: [ + { + min: 429 + max: 429 + } + { + min: 500 + max: 503 + } + ] + } + name: '${config.backendId}-breaker-rule' + tripDuration: 'PT1M' + acceptRetryAfter: true + } + ] + } : null + + // Backend authorization configuration + // Determines the effective authType: explicit authType > default from backendType + // managed-identity: configure credentials.managedIdentity on the backend resource (native APIM backend auth) + // api-key-bearer/api-key-header: credentials handled in policy fragments via named values + // aws-sigv4: credentials handled in policy fragments + // none: no auth configured + credentials: { + // Managed Identity authorization (native backend auth — no policy expression needed) + #disable-next-line BCP037 + managedIdentity: (config.?authType ?? (config.backendType == 'aws-bedrock' ? 'aws-sigv4' : (config.backendType == 'external' ? 'none' : (config.backendType == 'aws-bedrock-mantle' || config.backendType == 'gemini-openai' ? 'api-key-bearer' : 'managed-identity')))) == 'managed-identity' ? { + clientId: managedIdentityClientId + resource: 'https://cognitiveservices.azure.com' + } : null + // Header-based credentials (managed identity client ID hint for Azure backends) + header: (config.?authType ?? (config.backendType == 'aws-bedrock' ? 'aws-sigv4' : (config.backendType == 'external' ? 'none' : 'managed-identity'))) == 'managed-identity' ? { + 'x-ms-client-id': [ + managedIdentityClientId + ] + } : {} + } + + // TLS configuration for secure communication + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +}] + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Array of created backend IDs') +output backendIds array = [for (config, i) in llmBackendConfig: llmBackends[i].name] + +@description('Array of backend configurations with resource IDs') +output backendDetails array = [for (config, i) in llmBackendConfig: { + backendId: config.backendId + backendType: config.backendType + authType: config.?authType ?? '' + authConfigNamedValue: config.?authConfig.?namedValueKey ?? '' + resourceId: llmBackends[i].id + supportedModels: config.supportedModels + priority: config.?priority ?? 1 + weight: config.?weight ?? 100 +}] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep new file mode 100644 index 000000000..fda22544c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/llm-policy-fragments.bicep @@ -0,0 +1,323 @@ +/* + * @module llm-policy-fragments + * @description Generates APIM policy fragments with backend pool configurations + * + * This module creates policy fragments that contain the dynamically generated + * backend pool configurations based on the LLM backend setup. These fragments + * are used by the Universal LLM API policy to route requests appropriately. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('Policy fragment configuration from backend pools module') +param policyFragmentConfig object + +@description('User-assigned managed identity client ID for authentication') +param managedIdentityClientId string + +@description('LLM backend configuration with model metadata for available models response') +param llmBackendConfig array = [] + +@description('AWS access key ID for Amazon Bedrock authentication (required when using aws-bedrock backends)') +@secure() +param awsAccessKey string = 'NA' + +@description('AWS secret access key for Amazon Bedrock authentication (required when using aws-bedrock backends)') +@secure() +param awsSecretKey string = 'NA' + +@description('AWS region for Amazon Bedrock (e.g., us-east-1)') +param awsRegion string = 'NA' + +@description('Model alias definitions for grouping models under a single alias name') +param modelAliases array = [] + +@description('Key Vault name for storing backend credentials (reserved for future use)') +#disable-next-line no-unused-params +param keyVaultName string = '' + +// ------------------ +// VARIABLES +// ------------------ + +// Combine backend pools and direct backends for unified routing +var allPools = union(policyFragmentConfig.backendPools, policyFragmentConfig.directBackends) + +// Generate C# code for each backend pool with unique variable names (includes authType and authConfigNamedValue) +var backendPoolsArray = [for (pool, index) in allPools: replace(replace(replace(replace(replace(replace('// Pool: POOLNAME (Type: POOLTYPE, Auth: AUTHTYPE)\nvar pool_INDEX = new JObject()\n{\n { "poolName", "POOLNAME" },\n { "poolType", "POOLTYPE" },\n { "authType", "AUTHTYPE" },\n { "authConfigNamedValue", "AUTHCONFIGNAMEDVALUE" },\n { "supportedModels", new JArray(MODELS) }\n};\nbackendPools.Add(pool_INDEX);', 'POOLNAME', pool.poolName), 'POOLTYPE', pool.poolType), 'AUTHTYPE', pool.?authType ?? ''), 'AUTHCONFIGNAMEDVALUE', pool.?authConfigNamedValue ?? ''), 'INDEX', string(index)), 'MODELS', join(map(pool.supportedModels, (model) => '"${model}"'), ', '))] + +var backendPoolsCode = join(backendPoolsArray, '\n') + +// Load policy fragment templates +var setBackendPoolsFragmentTemplate = loadTextContent('./policies/frag-set-backend-pools.xml') +var setBackendAuthorizationFragmentXml = loadTextContent('./policies/frag-set-backend-authorization.xml') +var setTargetBackendPoolFragmentXml = loadTextContent('./policies/frag-set-target-backend-pool.xml') +var setLlmRequestedModelFragmentXml = loadTextContent('./policies/frag-set-llm-requested-model.xml') +var setLlmUsageFragmentXml = loadTextContent('./policies/frag-set-llm-usage.xml') +var getAvailableModelsFragmentTemplate = loadTextContent('./policies/frag-get-available-models.xml') + +// Inject generated backend pools code into template +var updatedSetBackendPoolsFragmentXml = replace(setBackendPoolsFragmentTemplate, '//{backendPoolsCode}', backendPoolsCode) + +// Generate model deployments code using reduce to flatten models from all backends +// Each backend generates code for all its supported models (now with per-model metadata) +// supportedModels is now an array of objects: { name, sku?, capacity?, modelFormat?, modelVersion?, retirementDate? } +var modelDeploymentsCodeResult = reduce(llmBackendConfig, { code: '', index: 0 }, (acc, config) => + reduce(config.supportedModels, acc, (modelAcc, model) => { + code: '${modelAcc.code}\n// Model: ${model.name} from backend: ${config.backendId}\nvar deployment_${modelAcc.index} = new JObject()\n{\n { "id", "${config.backendId}" },\n { "type", "${config.backendType}" },\n { "name", "${model.name}" },\n { "sku", new JObject() { { "name", "${model.?sku ?? 'Standard'}" }, { "capacity", ${model.?capacity ?? 100} } } },\n { "properties", new JObject() {\n { "model", new JObject() { { "format", "${model.?modelFormat ?? 'OpenAI'}" }, { "name", "${model.name}" }, { "version", "${model.?modelVersion ?? '1'}" } } },\n { "capabilities", new JObject() { { "chatCompletion", "true" } } },\n { "provisioningState", "Succeeded" }${!empty(model.?retirementDate) ? ',\n { "retirementDate", "${model.retirementDate}" }' : ''}\n }}\n};\nmodelDeployments.Add(deployment_${modelAcc.index});' + index: modelAcc.index + 1 + }) +) + +var modelDeploymentsCode = modelDeploymentsCodeResult.code + +// Inject generated model deployments code into available models template +var updatedGetAvailableModelsFragmentXml = replace(getAvailableModelsFragmentTemplate, '//{modelDeploymentsCode}', modelDeploymentsCode) + +// Generate metadata-config fragment for the Unified AI API +// Maps each model to its backend pool/direct backend + apiVersion + timeout + inferenceApiVersion +var metadataModelsResult = reduce(llmBackendConfig, { code: '', seenModels: [] }, (acc, config) => + reduce(config.supportedModels, acc, (modelAcc, model) => { + code: contains(modelAcc.seenModels, model.name) ? modelAcc.code : '${modelAcc.code}${length(modelAcc.seenModels) > 0 ? ',\n' : ''}\t\t\t\'${model.name}\': {\n\t\t\t\t\'backend\': \'${reduce(allPools, '', (poolAcc, pool) => contains(pool.supportedModels, model.name) ? pool.poolName : poolAcc)}\',\n\t\t\t\t\'apiVersion\': \'${model.?apiVersion ?? '2024-02-15-preview'}\',\n\t\t\t\t\'timeout\': ${model.?timeout ?? 120}${!empty(model.?inferenceApiVersion) ? ',\n\t\t\t\t\'inferenceApiVersion\': \'${model.inferenceApiVersion}\'' : ''}\n\t\t\t}' + seenModels: contains(modelAcc.seenModels, model.name) ? modelAcc.seenModels : union(modelAcc.seenModels, [model.name]) + }) +) +var metadataModelsCode = metadataModelsResult.code +var metadataConfigFragmentXml = loadTextContent('./policies/frag-metadata-config.xml') +var updatedMetadataConfigStep1 = replace(metadataConfigFragmentXml, '//{modelsConfigCode}', metadataModelsCode) + +// Generate model aliases code from modelAliases parameter using reduce pattern +var modelAliasesResult = reduce(modelAliases, { code: '', count: 0 }, (acc, alias) => { + code: '${acc.code}${acc.count > 0 ? ',\n' : ''}\t\t\t\'${alias.name}\': {\n\t\t\t\t\'models\': [${join(map(alias.models, m => '\'${m}\''), ', ')}],\n\t\t\t\t\'strategy\': \'${alias.?strategy ?? 'priority'}\'\n\t\t\t}' + count: acc.count + 1 +}) +var modelAliasesCode = modelAliasesResult.code +var updatedMetadataConfigFragmentXml = replace(updatedMetadataConfigStep1, '//{modelAliasesCode}', modelAliasesCode) + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +// Named value for managed identity client ID +resource uamiClientIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'uami-client-id' + parent: apimService + properties: { + displayName: 'uami-client-id' + value: managedIdentityClientId + secret: false + } +} + +// Named values for AWS Bedrock authentication +// Always created with safe defaults so the policy fragment compiles even when no aws-bedrock backends are configured. +// When aws-bedrock backends are present, the caller must supply real credentials via parameters. +resource awsAccessKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-access-key' + parent: apimService + properties: { + displayName: 'aws-access-key' + value: !empty(awsAccessKey) ? awsAccessKey : 'NOT_CONFIGURED' + secret: true + } +} + +resource awsSecretKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-secret-key' + parent: apimService + properties: { + displayName: 'aws-secret-key' + value: !empty(awsSecretKey) ? awsSecretKey : 'NOT_CONFIGURED' + secret: true + } +} + +resource awsRegionNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-region' + parent: apimService + properties: { + displayName: 'aws-region' + value: !empty(awsRegion) ? awsRegion : 'NOT_CONFIGURED' + secret: false + } +} + +// Dynamic named values for backend API key credentials +// Backends with authConfig.namedValueKey and authConfig.keyVaultSecretUri use Key Vault references +// Backends with authConfig.namedValueKey and authConfig.secretValue use explicit values (testing only) +var backendAuthConfigs = filter(llmBackendConfig, config => !empty(config.?authConfig.?namedValueKey ?? '')) + +resource backendApiKeyNamedValues 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = [for config in backendAuthConfigs: { + name: config.authConfig.namedValueKey + parent: apimService + properties: { + displayName: config.authConfig.namedValueKey + secret: true + // Use Key Vault reference if keyVaultSecretUri is provided, otherwise use explicit value + keyVault: !empty(config.?authConfig.?keyVaultSecretUri ?? '') ? { + secretIdentifier: config.authConfig.keyVaultSecretUri + } : null + value: empty(config.?authConfig.?keyVaultSecretUri ?? '') ? (config.?authConfig.?secretValue ?? 'NOT_CONFIGURED') : null + } +}] + +// Policy Fragment: Set Backend Pools +resource setBackendPoolsFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-backend-pools' + parent: apimService + properties: { + description: 'Dynamically generated backend pool configurations for LLM routing' + format: 'rawxml' + value: updatedSetBackendPoolsFragmentXml + } +} + +// Policy Fragment: Set Backend Authorization +resource setBackendAuthorizationFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-backend-authorization' + parent: apimService + dependsOn: [ + awsAccessKeyNamedValue + awsSecretKeyNamedValue + awsRegionNamedValue + ] + properties: { + description: 'Authentication and routing configuration for different LLM backend types' + format: 'rawxml' + value: setBackendAuthorizationFragmentXml + } +} + +// Policy Fragment: Set Target Backend Pool +resource setTargetBackendPoolFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-target-backend-pool' + parent: apimService + properties: { + description: 'Determines the target backend pool for LLM requests' + format: 'rawxml' + value: setTargetBackendPoolFragmentXml + } +} + +// Policy Fragment: Set LLM Requested Model +resource setLlmRequestedModelFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-llm-requested-model' + parent: apimService + properties: { + description: 'Extracts the requested model from deployment-id (Azure OpenAI) or request body (Inference)' + format: 'rawxml' + value: setLlmRequestedModelFragmentXml + } +} + +// Policy Fragment: Set LLM Usage +resource setLlmUsageFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-llm-usage' + parent: apimService + properties: { + description: 'Collects usage metrics for LLM requests' + format: 'rawxml' + value: setLlmUsageFragmentXml + } +} + +// Policy Fragment: Get Available Models +resource getAvailableModelsFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'get-available-models' + parent: apimService + properties: { + description: 'Returns a JSON response listing all available model deployments with their capabilities' + format: 'rawxml' + value: updatedGetAvailableModelsFragmentXml + } +} + +// Policy Fragment: Validate Model Access +// Restricts access to specific models based on the allowedModels variable +resource validateModelAccessFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'validate-model-access' + parent: apimService + properties: { + description: 'Validates that the requested model is in the allowed models list for the product' + format: 'rawxml' + value: loadTextContent('./policies/frag-validate-model-access.xml') + } +} + +// Policy Fragment: Responses API ID Security (inbound) +// Enforces per-subscription ownership of OpenAI Responses API response_id values +// and hydrates routing for GET/DELETE operations on /responses/{id}. +resource responsesIdSecurityFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'responses-id-security' + parent: apimService + properties: { + description: 'Inbound: validates response_id ownership and hydrates routing for /responses operations' + format: 'rawxml' + value: loadTextContent('./policies/frag-responses-id-security.xml') + } +} + +// Policy Fragment: Responses API ID Cache Store (outbound) +// Records response_id → "||" in APIM cache +// after a successful POST /responses, enabling subsequent ownership checks. +resource responsesIdCacheStoreFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'responses-id-cache-store' + parent: apimService + properties: { + description: 'Outbound: caches response_id ownership for newly created Responses API objects' + format: 'rawxml' + value: loadTextContent('./policies/frag-responses-id-cache-store.xml') + } +} + +// Policy Fragment: Metadata Configuration +// Provides centralized configuration for the Unified AI API with dynamically generated model mappings +resource metadataConfigFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'metadata-config' + parent: apimService + properties: { + description: 'Dynamically generated metadata configuration for Unified AI API routing' + format: 'rawxml' + value: updatedMetadataConfigFragmentXml + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Name of the set-backend-pools fragment') +output setBackendPoolsFragmentName string = setBackendPoolsFragment.name + +@description('Name of the set-backend-authorization fragment') +output setBackendAuthorizationFragmentName string = setBackendAuthorizationFragment.name + +@description('Name of the set-target-backend-pool fragment') +output setTargetBackendPoolFragmentName string = setTargetBackendPoolFragment.name + +@description('Name of the get-available-models fragment') +output getAvailableModelsFragmentName string = getAvailableModelsFragment.name + +@description('Name of the validate-model-access fragment') +output validateModelAccessFragmentName string = validateModelAccessFragment.name + +@description('Name of the metadata-config fragment') +output metadataConfigFragmentName string = metadataConfigFragment.name + +@description('Name of the responses-id-security fragment') +output responsesIdSecurityFragmentName string = responsesIdSecurityFragment.name + +@description('Name of the responses-id-cache-store fragment') +output responsesIdCacheStoreFragmentName string = responsesIdCacheStoreFragment.name + +@description('Generated backend pools configuration code') +output backendPoolsCode string = backendPoolsCode diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-get-available-models.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-get-available-models.xml new file mode 100644 index 000000000..382ba57fa --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-get-available-models.xml @@ -0,0 +1,72 @@ + + + + (StringComparer.OrdinalIgnoreCase); + JArray uniqueModels = new JArray(); + foreach (var model in allModels) + { + string modelName = model["name"]?.ToString() ?? ""; + if (!seenNames.Contains(modelName)) + { + seenNames.Add(modelName); + uniqueModels.Add(model); + } + } + + // Get allowedModels filter (comma-separated string) + string allowedModelsStr = context.Variables.ContainsKey("allowedModels") + ? (string)context.Variables["allowedModels"] + : ""; + + JArray filteredModels; + if (string.IsNullOrWhiteSpace(allowedModelsStr)) + { + // No filter - return all unique models + filteredModels = uniqueModels; + } + else + { + // Parse allowed models and filter (case-insensitive) + var allowedSet = new HashSet( + allowedModelsStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(m => m.Trim()), + StringComparer.OrdinalIgnoreCase + ); + + filteredModels = new JArray(); + foreach (var model in uniqueModels) + { + string modelName = model["name"]?.ToString() ?? ""; + if (allowedSet.Contains(modelName)) + { + filteredModels.Add(model); + } + } + } + + return filteredModels.ToString(); + }" /> + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-metadata-config.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-metadata-config.xml new file mode 100644 index 000000000..ea9a01b6f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-metadata-config.xml @@ -0,0 +1,107 @@ + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-cache-store.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-cache-store.xml new file mode 100644 index 000000000..6fa62743f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-cache-store.xml @@ -0,0 +1,75 @@ + + + + + = 200 + && context.Response.StatusCode < 300 + )"> + + + + + ("user-id", subId); + return string.Concat(subId, "|", model, "|", userId); + }" + duration="86400" + caching-type="internal" /> + + + @((string)context.Variables["newResponseId"]) + + + + @($"Cached response-id ownership: id={context.Variables.GetValueOrDefault("newResponseId", string.Empty)} sub={context.Subscription?.Id ?? string.Empty} model={context.Variables.GetValueOrDefault("requestedModel", string.Empty)}") + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-security.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-security.xml new file mode 100644 index 000000000..285d3aa07 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-responses-id-security.xml @@ -0,0 +1,224 @@ + + + + + + + + + + + ("parsed-request-body"); + if (bodyObj == null) + { + var raw = context.Request.Body.As(preserveContent: true); + if (string.IsNullOrEmpty(raw)) { return string.Empty; } + bodyObj = JObject.Parse(raw); + } + var prev = bodyObj["previous_response_id"]?.ToString(); + return string.IsNullOrEmpty(prev) ? string.Empty : prev.Trim(); + } + catch { return string.Empty; } + }" /> + + + + + + + + + 0 ? parts[0] : string.Empty; + var currentSubId = context.Subscription?.Id ?? string.Empty; + return !string.IsNullOrEmpty(cachedSubId) + && !cachedSubId.Equals(currentSubId, StringComparison.Ordinal); + }"> + + @($"Response-id ownership mismatch. responseId={context.Variables.GetValueOrDefault("responseId", string.Empty)} requestSub={context.Subscription?.Id ?? string.Empty}") + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. The supplied response_id was created by a different subscription and cannot be accessed."), + new JProperty("type", "access_error"), + new JProperty("code", "response_id_forbidden") + )) + ).ToString(); + } + + + + + + + ("cachedResponseOwner", string.Empty); + if (string.IsNullOrEmpty(cached)) { return false; } + var parts = cached.Split('|'); + return parts.Length >= 2 && !string.IsNullOrEmpty(parts[1]); + }"> + + + @($"Hydrated requestedModel='{context.Variables.GetValueOrDefault("requestedModel", string.Empty)}' from response-id cache for {context.Request.Method} {context.Request.Url.Path}") + + + + + + + ("cachedResponseOwner", string.Empty); + return string.IsNullOrEmpty(cached); + }"> + + @($"Response-id cache miss on {context.Request.Method} {context.Request.Url.Path}. Returning 404.") + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"The supplied response_id '{context.Variables.GetValueOrDefault("responseId", string.Empty)}' is not known to the gateway. It was either never created here or the ownership cache entry has expired."), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "response_id_not_found") + )) + ).ToString(); + } + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-authorization.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-authorization.xml new file mode 100644 index 000000000..eff3db26d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-authorization.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + application/json + + {"error": {"code": "AWSCredentialsNotConfigured", "message": "Amazon Bedrock credentials are not configured. Set the APIM named values: aws-access-key, aws-secret-key, and aws-region."}} + + + + + + @(((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ")) + + + @{ + var body = context.Request.Body.As(preserveContent: true); + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + + + @{ + var accessKey = "{{aws-access-key}}"; + var secretKey = "{{aws-secret-key}}"; + var region = "{{aws-region}}"; + var service = "bedrock"; + + var method = context.Request.Method; + var uri = context.Request.Url; + var host = uri.Host; + + // Create canonical path + var path = uri.Path; + var modelSplit = path.Split(new[] { "model/" }, 2, StringSplitOptions.None); + var afterModel = modelSplit.Length > 1 ? modelSplit[1] : ""; + var parts = afterModel.Split(new[] { '/' }, 2); + var model = System.Uri.EscapeDataString(parts[0]); + var remainder = parts.Length > 1 ? parts[1] : ""; + var canonicalPath = $"/model/{model}/{remainder}"; + + var amzDate = ((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ"); + var dateStamp = ((DateTime)context.Variables["now"]).ToString("yyyyMMdd"); + + // Hash the payload + var body = context.Request.Body.As(preserveContent: true); + string hashedPayload; + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); + hashedPayload = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + // Create canonical query string + var queryDict = context.Request.Url.Query; + var canonicalQueryString = ""; + if (queryDict != null && queryDict.Count > 0) + { + var encodedParams = new List(); + foreach (var kvp in queryDict) + { + var encodedKey = System.Uri.EscapeDataString(kvp.Key); + var encodedValue = System.Uri.EscapeDataString(kvp.Value.First() ?? ""); + encodedParams.Add($"{encodedKey}={encodedValue}"); + } + canonicalQueryString = string.Join("&", encodedParams.OrderBy(p => p)); + } + + // Create signed headers and canonical headers + var headers = context.Request.Headers; + var canonicalHeaderList = new List(); + + var contentType = headers.GetValueOrDefault("Content-Type", "").ToLowerInvariant(); + if (!string.IsNullOrEmpty(contentType)) + { + canonicalHeaderList.Add(new[] { "content-type", contentType }); + } + + canonicalHeaderList.Add(new[] { "host", host }); + + foreach (var header in headers) + { + var name = header.Key.ToLowerInvariant(); + if (string.Equals(name, "x-amz-content-sha256", StringComparison.OrdinalIgnoreCase) || + string.Equals(name, "x-amz-date", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + if (name.StartsWith("x-amz-")) + { + var value = header.Value.First()?.Trim(); + canonicalHeaderList.Add(new[] { name, value }); + } + } + canonicalHeaderList.Add(new[] { "x-amz-content-sha256", hashedPayload }); + canonicalHeaderList.Add(new[] { "x-amz-date", amzDate }); + var canonicalHeadersOrdered = canonicalHeaderList.OrderBy(h => h[0]); + var canonicalHeaders = string.Join("\n", canonicalHeadersOrdered.Select(h => $"{h[0]}:{h[1].Trim()}")) + "\n"; + var signedHeaders = string.Join(";", canonicalHeadersOrdered.Select(h => h[0])); + + // Create and hash the canonical request + var canonicalRequest = $"{method}\n{canonicalPath}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedPayload}"; + string hashedCanonicalRequest = ""; + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(canonicalRequest)); + hashedCanonicalRequest = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + // Build string to sign + var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request"; + var stringToSign = $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n{hashedCanonicalRequest}"; + + // Derive signing key + byte[] kSecret = System.Text.Encoding.UTF8.GetBytes("AWS4" + secretKey); + byte[] kDate, kRegion, kService, kSigning; + using (var h1 = new System.Security.Cryptography.HMACSHA256(kSecret)) + { + kDate = h1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(dateStamp)); + } + using (var h2 = new System.Security.Cryptography.HMACSHA256(kDate)) + { + kRegion = h2.ComputeHash(System.Text.Encoding.UTF8.GetBytes(region)); + } + using (var h3 = new System.Security.Cryptography.HMACSHA256(kRegion)) + { + kService = h3.ComputeHash(System.Text.Encoding.UTF8.GetBytes(service)); + } + using (var h4 = new System.Security.Cryptography.HMACSHA256(kService)) + { + kSigning = h4.ComputeHash(System.Text.Encoding.UTF8.GetBytes("aws4_request")); + } + + // Create signature + string signature; + using (var hmac = new System.Security.Cryptography.HMACSHA256(kSigning)) + { + var sigBytes = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)); + signature = BitConverter.ToString(sigBytes).Replace("-", "").ToLowerInvariant(); + } + + return $"AWS4-HMAC-SHA256 Credential={accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}"; + } + + + @(context.Request.Url.Host) + + + + + + @{ + var namedValue = context.Variables.GetValueOrDefault("targetAuthConfigNamedValue", ""); + // Named value is resolved at deploy-time by APIM; at runtime we just read the variable + // The actual key lookup happens via the pool's authConfigNamedValue → APIM named value + return "Bearer " + namedValue; + } + + + + + + @(context.Variables.GetValueOrDefault("targetAuthConfigNamedValue", "")) + + + + + + + + + + + + + ("targetPoolType", "") == "non-llm-request")"> + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-pools.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-pools.xml new file mode 100644 index 000000000..1ccfbf817 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-backend-pools.xml @@ -0,0 +1,33 @@ + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-requested-model.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-requested-model.xml new file mode 100644 index 000000000..ca42100f6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-requested-model.xml @@ -0,0 +1,107 @@ + + + + = 0) + { + var afterSegment = path.Substring(idx + deploymentsSegment.Length); + var slashPos = afterSegment.IndexOf('/'); + var modelName = slashPos >= 0 ? afterSegment.Substring(0, slashPos) : afterSegment; + if (!string.IsNullOrEmpty(modelName)) + { + return modelName; + } + } + + // 3. Try request body model field (Inference endpoint pattern) + var bodyText = context.Request.Body.As(preserveContent: true); + if (string.IsNullOrEmpty(bodyText)) + { + return string.Empty; + } + + var body = JObject.Parse(bodyText); + return body["model"] != null ? body["model"].ToString() : string.Empty; + } + catch + { + return string.Empty; + } + }" /> + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Model could not be detected from request body or path"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "missing_model_parameter"), + new JProperty("param", "model") + )) + ).ToString(); + } + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-usage.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-usage.xml new file mode 100644 index 000000000..e96d22a01 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-llm-usage.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-target-backend-pool.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-target-backend-pool.xml new file mode 100644 index 000000000..f24e166d4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-set-target-backend-pool.xml @@ -0,0 +1,271 @@ + + + + + ("apiTypeOverrideBackend", ""); + if (!string.IsNullOrEmpty(apiTypeOverride)) + { + return apiTypeOverride; + } + + string defaultBackendPool = context.Variables.GetValueOrDefault("defaultBackendPool", ""); + string allowedBackendPools = context.Variables.GetValueOrDefault("allowedBackendPools", ""); + JArray backendPools = (JArray)context.Variables["backendPools"]; + + var allowedPools = string.IsNullOrWhiteSpace(allowedBackendPools) + ? null + : allowedBackendPools.Split(',').Select(s => s.Trim()).Where(s => s.Length > 0).ToArray(); + + // Find all backend pools that support the requested model + List matchingPools = new List(); + foreach (JObject pool in backendPools) + { + JArray supportedModels = (JArray)pool["supportedModels"]; + if (supportedModels.Any(model => model.ToString().Equals(requestedModel, StringComparison.OrdinalIgnoreCase))) + { + matchingPools.Add(pool); + } + } + + // If we have matching pools, filter by allowed pools if necessary + if (matchingPools.Count > 0) + { + // If there are no restrictions, use the first matching pool + if (allowedPools == null || allowedPools.Length == 0) + { + return matchingPools[0]["poolName"].ToString(); + } + + // Otherwise, find the first matching pool that is also in allowed pools + foreach (var pool in matchingPools) + { + string poolName = pool["poolName"].ToString(); + if (allowedPools.Contains(poolName)) + { + return poolName; + } + } + + // If no allowed pools match, check if default backend pool should be used + if (!string.IsNullOrWhiteSpace(defaultBackendPool)) + { + return defaultBackendPool; + } + + // If no default is specified, return error that no allowed pools match + return "ERROR_NO_ALLOWED_POOLS"; + } + + // If no matching pools at all, check for default backend pool + if (!string.IsNullOrWhiteSpace(defaultBackendPool)) + { + return defaultBackendPool; + } + + // No matching pools and no default - error + return "ERROR_NO_MODEL"; + }" /> + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Model parameter is required in the request payload"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "missing_model_parameter") + )) + ).ToString(); + } + + + + + + + application/json + + @{ + string requestedModel = (string)context.Variables["requestedModel"]; + JArray backendPools = (JArray)context.Variables["backendPools"]; + JArray supportedModels = new JArray(); + + foreach (JObject pool in backendPools) + { + foreach (var model in (JArray)pool["supportedModels"]) + { + if (!supportedModels.Contains(model)) + { + supportedModels.Add(model); + } + } + } + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Model '{requestedModel}' is not supported. Supported models: {string.Join(", ", supportedModels.Select(m => m.ToString()))}"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "unsupported_model"), + new JProperty("param", "model"), + new JProperty("supported_models", supportedModels) + )) + ).ToString(); + } + + + + + + + application/json + + @{ + string requestedModel = (string)context.Variables["requestedModel"]; + string allowedBackendPools = (string)context.Variables["allowedBackendPools"]; + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Access to model '{requestedModel}' is not allowed for this client. You do not have access to any of the backend pools that support this model."), + new JProperty("type", "access_error"), + new JProperty("code", "backend_pool_access_forbidden"), + new JProperty("allowed_backend_pools", allowedBackendPools) + )) + ).ToString(); + } + + + + + + + + ("apiTypeOverrideBackend", ""); + if (!string.IsNullOrEmpty(apiTypeOverride) && targetBackendPool == apiTypeOverride) + { + // Look up poolType from backendPools if possible, otherwise default to api-type-override + JArray backendPools = context.Variables.GetValueOrDefault("backendPools"); + if (backendPools != null) + { + foreach (JObject pool in backendPools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["poolType"].ToString(); + } + } + } + return "api-type-override"; + } + + JArray pools = context.Variables.GetValueOrDefault("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["poolType"].ToString(); + } + } + } + + return string.Empty; + }" /> + + + + ("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + // Use explicit authType if present, otherwise derive from poolType + var authType = pool["authType"]?.ToString(); + if (!string.IsNullOrEmpty(authType)) { return authType; } + + var poolType = pool["poolType"]?.ToString() ?? ""; + if (poolType == "ai-foundry" || poolType == "azure-openai") { return "native-backend-authorization"; } + if (poolType == "aws-bedrock") { return "aws-sigv4"; } + if (poolType == "aws-bedrock-mantle" || poolType == "gemini-openai") { return "api-key-bearer"; } + if (poolType == "external") { return "none"; } + return "native-backend-authorization"; + } + } + } + return "native-backend-authorization"; + }" /> + + + ("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["authConfigNamedValue"]?.ToString() ?? ""; + } + } + } + return ""; + }" /> + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-validate-model-access.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-validate-model-access.xml new file mode 100644 index 000000000..47697907e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/frag-validate-model-access.xml @@ -0,0 +1,63 @@ + + + + + + + + + m.Trim().ToLowerInvariant()).ToArray(); + var requestedModel = (context.Variables.GetValueOrDefault("requestedModel") ?? string.Empty).ToLowerInvariant(); + return !allowedModels.Any(m => m == requestedModel); + }"> + + + + application/json + + @{ + string requestedModel = context.Variables.GetValueOrDefault("requestedModel") ?? string.Empty; + string allowedModels = context.Variables.GetValueOrDefault("allowedModels") ?? string.Empty; + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Access to model '{requestedModel}' is not allowed for this product."), + new JProperty("type", "access_error"), + new JProperty("code", "unauthorized_model_access"), + new JProperty("allowed_models", allowedModels) + )) + ).ToString(); + } + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/models-inference-openapi.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/models-inference-openapi.json new file mode 100644 index 000000000..de627d04d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/models-inference-openapi.json @@ -0,0 +1,243 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Models Inference API", + "description": "AI Models Inference API endpoint for unified model access following the Azure AI model inference specification", + "version": "2024-05-01-preview" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/chat/completions": { + "post": { + "operationId": "createModelChatCompletion", + "summary": "Creates a model response for the given chat conversation", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChatCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChatCompletionResponse" + } + } + } + } + } + } + }, + "/completions": { + "post": { + "operationId": "createModelCompletion", + "summary": "Creates a completion for the provided prompt", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/embeddings": { + "post": { + "operationId": "createModelEmbedding", + "summary": "Creates an embedding vector representing the input text", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmbeddingRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/info": { + "get": { + "operationId": "getModelInfo", + "summary": "Returns information about the deployed model", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "CreateChatCompletionRequest": { + "type": "object", + "required": ["model", "messages"], + "properties": { + "model": { + "type": "string", + "description": "ID of the model to use" + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "required": ["role", "content"], + "properties": { + "role": { + "type": "string", + "enum": ["system", "user", "assistant", "tool"] + }, + "content": { + "type": "string" + } + } + } + }, + "temperature": { + "type": "number" + }, + "max_tokens": { + "type": "integer" + }, + "stream": { + "type": "boolean" + } + } + }, + "CreateChatCompletionResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "object": { + "type": "string" + }, + "created": { + "type": "integer" + }, + "model": { + "type": "string" + }, + "choices": { + "type": "array", + "items": { + "type": "object" + } + }, + "usage": { + "type": "object" + } + } + }, + "CreateCompletionRequest": { + "type": "object", + "required": ["model", "prompt"], + "properties": { + "model": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "max_tokens": { + "type": "integer" + }, + "temperature": { + "type": "number" + } + } + }, + "CreateEmbeddingRequest": { + "type": "object", + "required": ["model", "input"], + "properties": { + "model": { + "type": "string" + }, + "input": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "in": "header", + "name": "api-key" + } + } + }, + "security": [ + { + "apiKey": [] + } + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-api-policy.xml new file mode 100644 index 000000000..9545c6505 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-api-policy.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-openapi.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-openapi.json new file mode 100644 index 000000000..cbecc5439 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/policies/universal-llm-openapi.json @@ -0,0 +1,330 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Universal LLM API", + "description": "OpenAI-compatible API endpoint for unified access to all LLM models with dynamic routing and load balancing", + "version": "2024-05-01-preview" + }, + "servers": [ + { + "url": "/" + } + ], + "paths": { + "/chat/completions": { + "post": { + "operationId": "createChatCompletion", + "summary": "Creates a model response for the given chat conversation", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChatCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChatCompletionResponse" + } + } + } + } + } + } + }, + "/completions": { + "post": { + "operationId": "createCompletion", + "summary": "Creates a completion for the provided prompt", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/embeddings": { + "post": { + "operationId": "createEmbedding", + "summary": "Creates an embedding vector representing the input text", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmbeddingRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/deployments/{deployment-id}/chat/completions": { + "post": { + "operationId": "createDeploymentChatCompletion", + "summary": "Creates a chat completion for the specified deployment (Azure OpenAI compatible)", + "parameters": [ + { + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChatCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/deployments/{deployment-id}/completions": { + "post": { + "operationId": "createDeploymentCompletion", + "summary": "Creates a completion for the specified deployment (Azure OpenAI compatible)", + "parameters": [ + { + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCompletionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/deployments/{deployment-id}/embeddings": { + "post": { + "operationId": "createDeploymentEmbedding", + "summary": "Creates an embedding for the specified deployment (Azure OpenAI compatible)", + "parameters": [ + { + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "api-version", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmbeddingRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "CreateChatCompletionRequest": { + "type": "object", + "required": ["messages"], + "properties": { + "model": { + "type": "string", + "description": "ID of the model to use" + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "required": ["role", "content"], + "properties": { + "role": { + "type": "string", + "enum": ["system", "user", "assistant", "tool"] + }, + "content": { + "type": "string" + } + } + } + }, + "temperature": { + "type": "number" + }, + "max_tokens": { + "type": "integer" + }, + "stream": { + "type": "boolean" + } + } + }, + "CreateChatCompletionResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "object": { + "type": "string" + }, + "created": { + "type": "integer" + }, + "model": { + "type": "string" + }, + "choices": { + "type": "array", + "items": { + "type": "object" + } + }, + "usage": { + "type": "object", + "properties": { + "prompt_tokens": { + "type": "integer" + }, + "completion_tokens": { + "type": "integer" + }, + "total_tokens": { + "type": "integer" + } + } + } + } + }, + "CreateCompletionRequest": { + "type": "object", + "required": ["prompt"], + "properties": { + "model": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "max_tokens": { + "type": "integer" + }, + "temperature": { + "type": "number" + } + } + }, + "CreateEmbeddingRequest": { + "type": "object", + "required": ["input"], + "properties": { + "model": { + "type": "string" + }, + "input": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "in": "header", + "name": "api-key" + } + } + }, + "security": [ + { + "apiKey": [] + } + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep new file mode 100644 index 000000000..7739727a7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/llm-backend-onboarding/modules/universal-llm-api.bicep @@ -0,0 +1,118 @@ +// @module universal-llm-api +// @description Creates the Universal LLM API that uses dynamic backend routing +// +// This module creates an API in APIM that routes requests to the appropriate +// backend pool based on the model specified in the request. + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('Path for the Universal LLM API') +param apiPath string = 'llm' + +@description('Display name for the API') +param apiDisplayName string = 'Universal LLM API' + +@description('Description for the API') +param apiDescription string = 'Unified API endpoint for all LLM models with dynamic routing and load balancing' + +@description('Enable subscription key authentication') +param enableSubscriptionKey bool = true + +// ------------------ +// VARIABLES +// ------------------ + +var policyXml = loadTextContent('./policies/universal-llm-api-policy.xml') + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +// Universal LLM API - OpenAI-compatible endpoint +resource universalLlmApi 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + name: 'universal-llm-api' + parent: apimService + properties: { + apiType: 'http' + description: apiDescription + displayName: apiDisplayName + format: 'openapi+json' + path: '${apiPath}/openai' + protocols: [ + 'https' + ] + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + subscriptionRequired: enableSubscriptionKey + value: loadTextContent('./policies/universal-llm-openapi.json') + } +} + +// API Policy +resource universalLlmApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-06-01-preview' = { + name: 'policy' + parent: universalLlmApi + properties: { + format: 'rawxml' + value: policyXml + } +} + +// AI Models Inference API - Alternative endpoint for inference +resource modelsInferenceApi 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + name: 'models-inference-api' + parent: apimService + properties: { + apiType: 'http' + description: 'AI Models Inference API endpoint for unified model access' + displayName: 'Models Inference API' + format: 'openapi+json' + path: '${apiPath}/models' + protocols: [ + 'https' + ] + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + subscriptionRequired: enableSubscriptionKey + value: loadTextContent('./policies/models-inference-openapi.json') + } +} + +// Models Inference API Policy +resource modelsInferenceApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-06-01-preview' = { + name: 'policy' + parent: modelsInferenceApi + properties: { + format: 'rawxml' + value: policyXml + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Universal LLM API name') +output universalLlmApiName string = universalLlmApi.name + +@description('Universal LLM API path') +output universalLlmApiPath string = universalLlmApi.properties.path + +@description('Models Inference API name') +output modelsInferenceApiName string = modelsInferenceApi.name + +@description('Models Inference API path') +output modelsInferenceApiPath string = modelsInferenceApi.properties.path diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicep new file mode 100644 index 000000000..98685f656 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicep @@ -0,0 +1,1159 @@ +targetScope = 'subscription' + +// +// BASIC PARAMETERS +// +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@minLength(1) +@description('Primary location for all resources (filtered on available regions for Azure Open AI Service).') +@allowed([ 'uaenorth', 'southafricanorth', 'westeurope', 'southcentralus', 'australiaeast', 'canadaeast', 'eastus', 'eastus2', 'francecentral', 'japaneast', 'northcentralus', 'swedencentral', 'switzerlandnorth', 'uksouth' ]) +param location string + +@description('Location of the API Center service. Leave blank to use primary location, where API Center is available in that region.') +@allowed(['', 'australiaeast', 'canadacentral', 'centralindia', 'eastus', 'francecentral', 'swedencentral', 'uksouth', 'westeurope' ]) +param apicLocation string = '' + +@description('Tags to be applied to resources.') +param tags object = { 'azd-env-name': environmentName, 'SecurityControl': 'Ignore' } + +// +// RESOURCE NAMES - Assign custom names to different provisioned services +// +@description('Name of the resource group. Leave blank to use default naming conventions.') +param resourceGroupName string + +@description('Name of the APIM managed identity. Leave blank to use default naming conventions.') +param apimIdentityName string = '' + +@description('Name of the Usage Logic App managed identity. Leave blank to use default naming conventions.') +param usageLogicAppIdentityName string = '' + +@description('Name of the API Management service. Leave blank to use default naming conventions.') +param apimServiceName string = '' + +@description('Name of the Log Analytics workspace. Leave blank to use default naming conventions.') +param logAnalyticsName string = '' + +@description('Use an existing Log Analytics workspace instead of creating a new one.') +param useExistingLogAnalytics bool = false + +@description('Name of the existing Log Analytics workspace (only used when useExistingLogAnalytics is true).') +param existingLogAnalyticsName string = '' + +@description('Resource group containing the existing Log Analytics workspace (only used when useExistingLogAnalytics is true).') +param existingLogAnalyticsRG string = '' + +@description('Subscription ID containing the existing Log Analytics workspace (only used when useExistingLogAnalytics is true). Leave blank to use the current subscription.') +param existingLogAnalyticsSubscriptionId string = '' + +@description('Name of the Application Insights dashboard for APIM. Leave blank to use default naming conventions.') +param apimApplicationInsightsDashboardName string = '' + +@description('Name of the Application Insights dashboard for Function/Logic App. Leave blank to use default naming conventions.') +param funcAplicationInsightsDashboardName string = '' + +@description('Name of the Application Insights dashboard for Function/Logic App. Leave blank to use default naming conventions.') +param foundryApplicationInsightsDashboardName string = '' + +@description('Name of the Application Insights for APIM resource. Leave blank to use default naming conventions.') +param apimApplicationInsightsName string = '' + +@description('Name of the Application Insights for Function/Logic App resource. Leave blank to use default naming conventions.') +param funcApplicationInsightsName string = '' + +@description('Name of the Application Insights for Function/Logic App resource. Leave blank to use default naming conventions.') +param foundryApplicationInsightsName string = '' + +@description('Name of the Event Hub Namespace resource. Leave blank to use default naming conventions.') +param eventHubNamespaceName string = '' + +@description('Name of the Cosmos DB account resource. Leave blank to use default naming conventions.') +param cosmosDbAccountName string = '' + +@description('Name of the Logic App resource for usage processing. Leave blank to use default naming conventions.') +param usageProcessingLogicAppName string = '' + +@description('Name of the Storage Account. Leave blank to use default naming conventions.') +param storageAccountName string = '' + +@description('Name of the API Center service. Leave blank to use default naming conventions.') +param apicServiceName string = '' + +@description('Name of the AI Foundry resource. Leave blank to use default naming conventions.') +param aiFoundryResourceName string = '' + +@description('Name of the Azure Key Vault. Leave blank to use default naming conventions.') +param keyVaultName string = '' + +@description('Name of the Azure Managed Redis resource. Leave blank to use default naming conventions.') +param redisCacheName string = '' + +// +// NETWORKING PARAMETERS - Network configuration and access controls +// + +@description('Name of the Virtual Network. Leave blank to use default naming conventions.') +param vnetName string = '' + +@description('Use an existing Virtual Network instead of creating a new one.') +param useExistingVnet bool = false + +@description('Resource group containing the existing VNet (only used when useExistingVnet is true).') +param existingVnetRG string = '' + +// Subnet names +@description('Subnet name for API Management in the VNet. Leave blank to use default naming conventions.') +param apimSubnetName string = '' + +@description('Subnet name for Private Endpoints in the VNet. Leave blank to use default naming conventions.') +param privateEndpointSubnetName string = '' + +@description('Subnet name for Function/Logic App in the VNet. Leave blank to use default naming conventions.') +param functionAppSubnetName string = '' + +@description('Subnet name for AI Foundry agent (network injection) workloads in the VNet. Leave blank to use default naming conventions. Required when foundryNetworkInjectionEnabled is true and useExistingVnet is true.') +param agentSubnetName string = '' + + +// NSG & route table names +@description('NSG name for API Management subnet. Leave blank to use default naming conventions.') +param apimNsgName string = '' + +@description('NSG name for Private Endpoint subnet. Leave blank to use default naming conventions.') +param privateEndpointNsgName string = '' + +@description('NSG name for Function App subnet. Leave blank to use default naming conventions.') +param functionAppNsgName string = '' + +@description('NSG name for AI Foundry agent (network injection) subnet. Leave blank to use default naming conventions.') +param agentSubnetNsgName string = '' + +@description('Route Table name for API Management subnet. Leave blank to use default naming conventions.') +param apimRouteTableName string = '' + +// VNet address space and subnet prefixes +@description('Virtual Network address space.') +param vnetAddressPrefix string = '10.170.0.0/24' + +@description('API Management subnet address range.') +param apimSubnetPrefix string = '10.170.0.0/26' + +@description('Private Endpoint subnet address range.') +param privateEndpointSubnetPrefix string = '10.170.0.64/26' + +@description('Function App subnet address range.') +param functionAppSubnetPrefix string = '10.170.0.128/26' + +@description('AI Foundry agent (network injection) subnet address range. Used only when a new VNet is provisioned and foundryNetworkInjectionEnabled is true. Subnet is delegated to Microsoft.App/environments.') +param agentSubnetPrefix string = '10.170.0.192/26' + +@description('Enable AI Foundry network injection by attaching the Foundry account to the agent subnet (delegated to Microsoft.App/environments). Defaults to true. When useExistingVnet is true the agentSubnetName must reference an existing subnet with the required delegation.') +param foundryNetworkInjectionEnabled bool = true + +// DNS ZONE PARAMETERS - DNS zone configuration for private endpoints (for use with existing VNet) +@description('Resource group containing the DNS zones (only used with existing VNet when existingPrivateDnsZones is not provided - LEGACY).') +param dnsZoneRG string = '' + +@description('Subscription ID containing the DNS zones (only used with existing VNet when existingPrivateDnsZones is not provided - LEGACY).') +param dnsSubscriptionId string = '' + +@description('Existing Private DNS Zone resource IDs for BYO network scenarios. Each property should contain the full resource ID of the DNS zone. When provided, these take precedence over dnsZoneRG/dnsSubscriptionId.') +param existingPrivateDnsZones object = { + // Example format: + // openai: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.openai.azure.com' + // keyVault: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.vaultcore.azure.net' + // monitor: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.monitor.azure.com' + // eventHub: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.servicebus.windows.net' + // cosmosDb: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.documents.azure.com' + // storageBlob: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net' + // storageFile: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net' + // storageTable: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.table.core.windows.net' + // storageQueue: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.queue.core.windows.net' + // cognitiveServices: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.cognitiveservices.azure.com' + // apimGateway: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.azure-api.net' + // aiServices: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.services.ai.azure.com' + // redis: '/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Network/privateDnsZones/privatelink.redis.azure.net' +} + +// PRIVATE ENDPOINTS - Names for private endpoints for various services +@description('Storage Blob private endpoint name. Leave blank to use default naming conventions.') +param storageBlobPrivateEndpointName string = '' + +@description('Storage File private endpoint name. Leave blank to use default naming conventions.') +param storageFilePrivateEndpointName string = '' + +@description('Storage Table private endpoint name. Leave blank to use default naming conventions.') +param storageTablePrivateEndpointName string = '' + +@description('Storage Queue private endpoint name. Leave blank to use default naming conventions.') +param storageQueuePrivateEndpointName string = '' + +@description('Cosmos DB private endpoint name. Leave blank to use default naming conventions.') +param cosmosDbPrivateEndpointName string = '' + +@description('Event Hub private endpoint name. Leave blank to use default naming conventions.') +param eventHubPrivateEndpointName string = '' + +@description('API Management V2 private endpoint name. Leave blank to use default naming conventions.') +param apimV2PrivateEndpointName string = '' + +@description('AI Foundry private endpoint base name. Leave blank to use default naming conventions.') +param aiFoundryPrivateEndpointName string = '' + +@description('Key Vault private endpoint name. Leave blank to use default naming conventions.') +param keyVaultPrivateEndpointName string = '' + +@description('Azure Managed Redis private endpoint name. Leave blank to use default naming conventions.') +param redisPrivateEndpointName string = '' + +// Services network access configuration + +@description('Network type for API Management service. Applies only to Premium and Developer SKUs.') +@allowed([ 'External', 'Internal' ]) +param apimNetworkType string = 'External' + +@description('Use private endpoint for API Management service. Applies only to StandardV2 and PremiumV2 SKUs.') +param apimV2UsePrivateEndpoint bool = true + +@description('API Management service external network access. When false, APIM must have private endpoint.') +param apimV2PublicNetworkAccess bool = true + +@description('Cosmos DB public network access.') +@allowed([ 'Enabled', 'Disabled' ]) +param cosmosDbPublicAccess string = 'Disabled' + +@description('Event Hub public network access. Needed to be Enabled when using APIM v2 SKUs during provisioning') +@allowed([ 'Enabled', 'Disabled' ]) +param eventHubNetworkAccess string = 'Enabled' + +@description('AI Foundry external network access.') +@allowed([ 'Enabled', 'Disabled' ]) +param aiFoundryExternalNetworkAccess string = 'Disabled' + +@description('Key Vault external network access.') +@allowed([ 'Enabled', 'Disabled' ]) +param keyVaultExternalNetworkAccess string = 'Disabled' + +@description('Azure Managed Redis public network access. When Disabled, private endpoint is the exclusive access method.') +@allowed([ 'Enabled', 'Disabled' ]) +param redisPublicNetworkAccess string = 'Disabled' + +@description('Use Azure Monitor Private Link Scope for Log Analytics and Application Insights.') +param useAzureMonitorPrivateLinkScope bool = false + +// +// FEATURE FLAGS - Deploy specific capabilities +// +@description('Create Application Insights dashboards.') +param createAppInsightsDashboards bool = false + +@description('Enable AI Model Inference in API Management.') +param enableAIModelInference bool = true + +@description('Enable Document Intelligence in API Management.') +param enableDocumentIntelligence bool = true + +@description('Enable Azure AI Search integration.') +param enableAzureAISearch bool = true + +@description('Enable PII redaction in AI Gateway') +param enableAIGatewayPiiRedaction bool = true + +@description('Enable OpenAI realtime capabilities') +param enableOpenAIRealtime bool = true + +@description('Enable Microsoft Entra ID authentication for API Management.') +param entraAuth bool = true + +@description('Enable API Center for API governance and discovery.') +param enableAPICenter bool = true + +@description('Enable Azure Managed Redis (AMR). When true (default), the Redis resource and APIM cache integration are provisioned.') +param enableManagedRedis bool = true + +@description('Azure Monitor diagnostic log settings for inference APIs. Controls frontend/backend request/response headers, body bytes, and LLM-specific log settings.') +param azureMonitorLogSettings object = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +@description('Application Insights diagnostic log settings for inference APIs. Controls which headers are captured and body byte limits.') +param appInsightsLogSettings object = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} + +// +// COMPUTE SKU & SIZE - SKUs and capacity settings for services +// +@description('API Management service SKU. Only Developer and Premium are supported.') +@allowed([ 'Developer', 'Premium', 'StandardV2', 'PremiumV2' ]) +param apimSku string = 'Developer' + +@description('API Management service SKU units.') +param apimSkuUnits int = 1 + +@description('Event Hub capacity units.') +param eventHubCapacityUnits int = 1 + +@description('Cosmos DB throughput in Request Units (RUs).') +param cosmosDbRUs int = 400 + +@description('Logic Apps SKU capacity units.') +param logicAppsSkuCapacityUnits int = 1 + +@description('SKU for the API Center service.') +@allowed(['Free', 'Standard']) +param apicSku string = 'Free' + +@description('SKU for the Key Vault service.') +@allowed(['standard', 'premium']) +param keyVaultSkuName string = 'standard' + +@description('Redis Enterprise / Azure Managed Redis SKU name. Allowed values align to Microsoft.Cache/redisEnterprise@2025-07-01.') +@allowed([ + 'Enterprise_E1' + 'Enterprise_E5' + 'Enterprise_E10' + 'Enterprise_E20' + 'Enterprise_E50' + 'Enterprise_E100' + 'Enterprise_E200' + 'Enterprise_E400' + 'EnterpriseFlash_F300' + 'EnterpriseFlash_F700' + 'EnterpriseFlash_F1500' + 'Balanced_B0' + 'Balanced_B1' + 'Balanced_B3' + 'Balanced_B5' + 'Balanced_B10' + 'Balanced_B20' + 'Balanced_B50' + 'Balanced_B100' + 'Balanced_B150' + 'Balanced_B250' + 'Balanced_B350' + 'Balanced_B500' + 'Balanced_B700' + 'Balanced_B1000' + 'MemoryOptimized_M10' + 'MemoryOptimized_M20' + 'MemoryOptimized_M50' + 'MemoryOptimized_M100' + 'MemoryOptimized_M150' + 'MemoryOptimized_M250' + 'MemoryOptimized_M350' + 'MemoryOptimized_M500' + 'MemoryOptimized_M700' + 'MemoryOptimized_M1000' + 'MemoryOptimized_M1500' + 'MemoryOptimized_M2000' + 'ComputeOptimized_X3' + 'ComputeOptimized_X5' + 'ComputeOptimized_X10' + 'ComputeOptimized_X20' + 'ComputeOptimized_X50' + 'ComputeOptimized_X100' + 'ComputeOptimized_X150' + 'ComputeOptimized_X250' + 'ComputeOptimized_X350' + 'ComputeOptimized_X500' + 'ComputeOptimized_X700' + 'FlashOptimized_A250' + 'FlashOptimized_A500' + 'FlashOptimized_A700' + 'FlashOptimized_A1000' + 'FlashOptimized_A1500' + 'FlashOptimized_A2000' + 'FlashOptimized_A4500' +]) +param redisSkuName string = 'Balanced_B10' + +@description('Redis Enterprise cluster capacity. Only used for Enterprise_* and EnterpriseFlash_* SKUs. Valid values are (2, 4, 6, ...) for Enterprise SKUs and (3, 9, 15, ...) for EnterpriseFlash SKUs.') +param redisSkuCapacity int = 2 + +@description('Minimum TLS version for Redis connections.') +param redisMinimumTlsVersion string = '1.2' + +// +// ACCELERATOR SPECIFIC PARAMETERS - Additional parameters for the solution (should not be modified without careful consideration) +// + +@description('Name of the Storage Account file share for Logic App content.') +param logicContentShareName string = 'usage-logic-content' + +// +// Governance Hub AI Backends +// + +@description('AI Search instances configuration - add more instances by adding to this array.') +param aiSearchInstances array = [ + // { + // name: 'ai-search-01' + // url: 'https://REPLACE1.search.windows.net/' + // description: 'AI Search Instance 1' + // } + // { + // name: 'ai-search-02' + // url: 'https://REPLACE2.search.windows.net/' + // description: 'AI Search Instance 2' + // } +] + +@description('AI Foundry instances configuration array. The first element (index 0) is the **primary** Foundry resource. The primary Foundry powers the APIM AI Gateway content safety and PII processing capabilities (via the AI Services unified endpoint) AND can also host LLM model deployments. Add more entries to deploy additional Foundry resources in different regions for additional LLM capacity / regional routing. All entries can host LLM deployments declared in aiFoundryModelsConfig. Each entry may optionally set `networkInjectionEnabled: true|false` to opt the specific Foundry resource into (or out of) agent network injection (delegated to Microsoft.App/environments). When omitted, the global `foundryNetworkInjectionEnabled` flag applies. Note: agent subnet is regional - only enable injection for instances in the same region as the VNet.') +param aiFoundryInstances array = [ + { + name: !empty(aiFoundryResourceName) ? aiFoundryResourceName : '' + location: location + customSubDomainName: '' + defaultProjectName: 'citadel-governance-project' + networkInjectionEnabled: true + } + { + name: !empty(aiFoundryResourceName) ? aiFoundryResourceName : '' + location: 'eastus2' + customSubDomainName: '' + defaultProjectName: 'citadel-governance-project' + networkInjectionEnabled: false + } +] + +@description('AI Foundry model deployments configuration - configure model deployments for Foundry instances.') +@metadata({ + example: ''' + Each model object should have: + - name: Model name (required) - e.g., 'gpt-4o', 'DeepSeek-R1' + - publisher: Publisher/format identifier, e.g., 'OpenAI', 'DeepSeek', 'Microsoft' (used as modelFormat in backend config) + - version: Version of the model + - sku: SKU name for the deployment, e.g., 'GlobalStandard', 'Standard' + - capacity: Capacity/TPM quota + - retirementDate: (Optional) Retirement date for the model in YYYY-MM-DD format + - apiVersion: (Optional) API version for OpenAI-type backend requests (default: '2024-02-15-preview') + - timeout: (Optional) Request timeout in seconds (default: 120) + - inferenceApiVersion: (Optional) API version for inference-type requests (e.g., '2024-05-01-preview' for non-OpenAI models) + - aiserviceIndex: (Optional) Index of the AI Foundry instance to deploy to. Leave empty to deploy to all instances + ''' +}) +// Leaving 'aiserviceIndex' empty or omitted means this model deployment will be created for all AI Foundry resources in 'aiFoundryInstances', +// Adding 'aiserviceIndex' with a numeric value (0, 1, etc.) means that the model will be deployed only to that specific instance by index +// The aiservice field will be automatically populated based on aiserviceIndex and the generated foundry resource names +param aiFoundryModelsConfig array = [ + { + name: 'gpt-4o-mini' + publisher: 'OpenAI' + version: '2024-07-18' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-09-30' + aiserviceIndex: 0 + } + { + name: 'gpt-4o' + publisher: 'OpenAI' + version: '2024-11-20' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-09-30' + aiserviceIndex: 0 + } + { + name: 'DeepSeek-R1' + publisher: 'DeepSeek' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + aiserviceIndex: 0 + } + { + name: 'Phi-4' + publisher: 'Microsoft' + version: '3' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + aiserviceIndex: 0 + } + { + name: 'text-embedding-3-large' + publisher: 'OpenAI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-04-14' + aiserviceIndex: 0 + } + { + name: 'gpt-5' + publisher: 'OpenAI' + version: '2025-08-07' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-02-05' + aiserviceIndex: 1 + } + { + name: 'DeepSeek-R1' + publisher: 'DeepSeek' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + aiserviceIndex: 1 + } + { + name: 'text-embedding-3-large' + publisher: 'OpenAI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-04-14' + aiserviceIndex: 1 + } +] + +@description('Name of the text embedding model deployment in the primary Microsoft Foundry to be used for APIM semantic caching.') +param primaryFoundryEmbeddingModelName string = 'text-embedding-3-large' + +@description('Microsoft Entra ID tenant ID for authentication (only used when entraAuth is true).') +param entraTenantId string = '' + +@description('Microsoft Entra ID client ID for authentication (only used when entraAuth is true). If empty and entraAuth is true, an app registration will be auto-provisioned.') +param entraClientId string = '' + +@description('Audience value for Microsoft Entra ID authentication (only used when entraAuth is true).') +param entraAudience string = '' + +@secure() +@description('Entra ID client secret for the app registration (only used when entraAuth is true with a pre-existing app registration, i.e., entraClientId is provided). When auto-provisioning, the secret is stored in Key Vault automatically by the entra-id module.') +param entraClientSecret string = '' + +@description('Enable the Unified AI Wildcard API (3rd API alongside Azure OpenAI and Universal LLM)') +param enableUnifiedAiApi bool = true + +// Load abbreviations from JSON file +var abbrs = loadJsonContent('./abbreviations.json') +// Generate a unique token for resources +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) + +// Transform aiFoundryModelsConfig to include the actual aiservice names based on aiserviceIndex +var transformedAiFoundryModelsConfig = [for model in aiFoundryModelsConfig: union(model, { + aiservice: contains(model, 'aiserviceIndex') + ? (!empty(aiFoundryInstances[model.aiserviceIndex].name) + ? aiFoundryInstances[model.aiserviceIndex].name + : 'aif-${resourceToken}-${model.aiserviceIndex}') + : '' +})] + +// Group models by aiserviceIndex for backend configuration +// Each model now includes full metadata: name, sku, capacity, modelFormat, modelVersion, retirementDate +var modelsGroupedByInstance = [for (instance, i) in aiFoundryInstances: { + instanceIndex: i + models: filter(map(aiFoundryModelsConfig, model => contains(model, 'aiserviceIndex') && model.aiserviceIndex == i ? union({ + name: model.name + sku: model.sku + capacity: model.capacity + modelFormat: model.publisher + modelVersion: model.version + retirementDate: model.?retirementDate ?? '' + }, !empty(model.?apiVersion) ? { apiVersion: model.apiVersion } : {}, + !empty(model.?inferenceApiVersion) ? { inferenceApiVersion: model.inferenceApiVersion } : {}, + contains(model, 'timeout') ? { timeout: model.timeout } : {} + ) : {}), m => !empty(m)) +}] + +/** + * LLM Backend Configuration Array + * + * Defines all LLM backends that APIM will route requests to. This enables: + * - Multi-model support across different LLM providers + * - Load balancing and failover for the same model across multiple backends + * - Flexible authentication schemes per backend + * + * Each backend object should have: + * - backendId: Unique identifier (used in APIM backend resource name) + * - backendType: 'ai-foundry' | 'azure-openai' | 'external' + * - endpoint: Base URL of the LLM service + * - authScheme: 'managedIdentity' | 'apiKey' | 'token' + * - supportedModels: Array of model objects with: + * - name: Model name (required) + * - sku: SKU name for deployment (default: 'Standard') + * - capacity: Capacity/TPM quota (default: 100) + * - modelFormat: Model format identifier, e.g., 'OpenAI', 'DeepSeek', 'Microsoft' (default: 'OpenAI') + * - modelVersion: Version of the model (default: '1') + * - retirementDate: (Optional) Retirement date for the model in YYYY-MM-DD format + * - apiVersion: (Optional) API version for OpenAI-type requests (default: '2024-02-15-preview') + * - timeout: (Optional) Request timeout in seconds (default: 120) + * - inferenceApiVersion: (Optional) API version for inference-type requests + * - priority: (Optional) 1-5, default 1 (lower = higher priority) + * - weight: (Optional) 1-1000, default 100 (higher = more traffic) + * + * This configuration is now dynamically generated from aiFoundryInstances and aiFoundryModelsConfig as part of the deployment. + * If you wish to onboard existing LLM backends, you can extend the llmBackendConfig variable with additional backend objects. + + var llmBackendConfig array = [ + // AI Foundry Instance 0 - Location: location (parameter) + // Models: gpt-4o-mini, gpt-4o, gpt-4.1, DeepSeek-R1, Phi-4 + { + backendId: 'aif-REPLACE-0' + backendType: 'ai-foundry' + endpoint: 'https://aif-REPLACE-0.services.ai.azure.com/models' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-4o-mini', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-07-18', retirementDate: '2026-09-30' } + { name: 'gpt-4o', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2024-11-20', retirementDate: '2026-09-30' } + { name: 'gpt-4.1', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2025-04-14', retirementDate: '2026-10-14', apiVersion: '2025-04-01-preview', timeout: 180 } + { name: 'DeepSeek-R1', sku: 'GlobalStandard', capacity: 1, modelFormat: 'DeepSeek', modelVersion: '1', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + { name: 'Phi-4', sku: 'GlobalStandard', capacity: 1, modelFormat: 'Microsoft', modelVersion: '3', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + ] + priority: 1 + weight: 100 + } + // AI Foundry Instance 1 - Location: eastus2 + // Models: gpt-5, DeepSeek-R1 + { + backendId: 'aif-REPLACE-1' + backendType: 'ai-foundry' + endpoint: 'https://aif-REPLACE-1.services.ai.azure.com/models' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-5', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '2025-08-07', retirementDate: '2027-02-05' } + { name: 'DeepSeek-R1', sku: 'GlobalStandard', capacity: 1, modelFormat: 'DeepSeek', modelVersion: '1', retirementDate: '2099-12-30', inferenceApiVersion: '2024-05-01-preview' } + ] + priority: 1 + weight: 100 + } +] + + */ + +// Dynamically generate LLM backend configuration from AI Foundry instances and models +var llmBackendConfig = [for (instance, i) in aiFoundryInstances: { + backendId: !empty(instance.name) ? '${instance.name}-${i}' : 'aif-${resourceToken}-${i}' + backendType: 'ai-foundry' + endpoint: 'https://${!empty(instance.name) ? instance.name : 'aif-${resourceToken}-${i}'}.cognitiveservices.azure.com/' + authScheme: 'managedIdentity' + supportedModels: modelsGroupedByInstance[i].models + priority: 1 + weight: 100 +}] + +var primaryFoundryName = !empty(aiFoundryInstances[0].name) ? aiFoundryInstances[0].name : 'aif-${resourceToken}-0' +// Primary Foundry endpoint - serves APIM AI Gateway as both: +// 1. Backend URL for `content-safety-backend` (Content Safety API) +// 2. Named-value `piiServiceUrl` (Language Service / PII detection API) +// Both APIs are exposed on the AI Services account base endpoint. +var primaryFoundryEndpoint = 'https://${primaryFoundryName}.cognitiveservices.azure.com/' +var primaryFoundryEmbeddingsBackendUrl = '${primaryFoundryEndpoint}openai/deployments/${primaryFoundryEmbeddingModelName}/embeddings' + +var openAiPrivateDnsZoneName = 'privatelink.openai.azure.com' +var keyVaultPrivateDnsZoneName = 'privatelink.vaultcore.azure.net' +var monitorPrivateDnsZoneName = 'privatelink.monitor.azure.com' +var eventHubPrivateDnsZoneName = 'privatelink.servicebus.windows.net' +var cosmosDbPrivateDnsZoneName = 'privatelink.documents.azure.com' +var storageBlobPrivateDnsZoneName = 'privatelink.blob.core.windows.net' +var storageFilePrivateDnsZoneName = 'privatelink.file.core.windows.net' +var storageTablePrivateDnsZoneName = 'privatelink.table.core.windows.net' +var storageQueuePrivateDnsZoneName = 'privatelink.queue.core.windows.net' +var aiCogntiveServicesDnsZoneName = 'privatelink.cognitiveservices.azure.com' +var apimV2SkuDnsZoneName = 'privatelink.azure-api.net' +var aiServicesDnsZoneName = 'privatelink.services.ai.azure.com' +var redisPrivateDnsZoneName = 'privatelink.redis.azure.net' + +// AI Foundry requires 3 DNS zones for full private endpoint support +var aiFoundryDnsZoneNames = [ + aiCogntiveServicesDnsZoneName // privatelink.cognitiveservices.azure.com + openAiPrivateDnsZoneName // privatelink.openai.azure.com + aiServicesDnsZoneName // privatelink.services.ai.azure.com +] + +// Extract existing DNS zone resource IDs from the parameter (for BYO network scenarios) +// These are used when useExistingVnet is true and existingPrivateDnsZones is provided +var existingKeyVaultDnsZoneId = existingPrivateDnsZones.?keyVault ?? '' +var existingMonitorDnsZoneId = existingPrivateDnsZones.?monitor ?? '' +var existingEventHubDnsZoneId = existingPrivateDnsZones.?eventHub ?? '' +var existingCosmosDbDnsZoneId = existingPrivateDnsZones.?cosmosDb ?? '' +var existingStorageBlobDnsZoneId = existingPrivateDnsZones.?storageBlob ?? '' +var existingStorageFileDnsZoneId = existingPrivateDnsZones.?storageFile ?? '' +var existingStorageTableDnsZoneId = existingPrivateDnsZones.?storageTable ?? '' +var existingStorageQueueDnsZoneId = existingPrivateDnsZones.?storageQueue ?? '' +var existingCognitiveServicesDnsZoneId = existingPrivateDnsZones.?cognitiveServices ?? '' +var existingApimGatewayDnsZoneId = existingPrivateDnsZones.?apimGateway ?? '' +var existingAiServicesDnsZoneId = existingPrivateDnsZones.?aiServices ?? '' +var existingOpenAiDnsZoneId = existingPrivateDnsZones.?openai ?? '' +var existingRedisDnsZoneId = existingPrivateDnsZones.?redis ?? '' + +// Existing DNS zone resource IDs for AI Foundry (for BYO network scenarios) +var aiFoundryDnsZoneResourceIds = union( + !empty(existingCognitiveServicesDnsZoneId) ? [existingCognitiveServicesDnsZoneId] : [], + !empty(existingOpenAiDnsZoneId) ? [existingOpenAiDnsZoneId] : [], + !empty(existingAiServicesDnsZoneId) ? [existingAiServicesDnsZoneId] : [] +) + +// Determine if we're using explicit DNS zone resource IDs (new approach) vs legacy RG/Subscription lookup +#disable-next-line no-unused-vars +var useExplicitDnsZoneIds = !empty(existingCosmosDbDnsZoneId) || !empty(existingEventHubDnsZoneId) || !empty(existingStorageBlobDnsZoneId) + +// Base DNS zones (always included) +var baseDnsZoneNames = [ + openAiPrivateDnsZoneName + aiCogntiveServicesDnsZoneName + keyVaultPrivateDnsZoneName + eventHubPrivateDnsZoneName + cosmosDbPrivateDnsZoneName + storageBlobPrivateDnsZoneName + storageFilePrivateDnsZoneName + storageTablePrivateDnsZoneName + storageQueuePrivateDnsZoneName + apimV2SkuDnsZoneName + aiServicesDnsZoneName + redisPrivateDnsZoneName +] + +// Only include Azure Monitor DNS zone when Private Link Scope is enabled +var privateDnsZoneNames = useAzureMonitorPrivateLinkScope ? concat(baseDnsZoneNames, [monitorPrivateDnsZoneName]) : baseDnsZoneNames + +// Organize resources in a resource group +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: !empty(resourceGroupName) ? resourceGroupName : '${abbrs.resourcesResourceGroups}${environmentName}' + location: location + tags: tags +} + +module dnsDeployment './modules/networking/dns.bicep' = [for privateDnsZoneName in privateDnsZoneNames: if(!useExistingVnet) { + name: 'dns-deployment-${privateDnsZoneName}' + scope: resourceGroup + params: { + name: privateDnsZoneName + tags: tags + } +}] + +module vnet './modules/networking/vnet.bicep' = if(!useExistingVnet) { + name: 'vnet' + scope: resourceGroup + params: { + name: !empty(vnetName) ? vnetName : 'vnet-${resourceToken}' + apimSubnetName: !empty(apimSubnetName) ? apimSubnetName : 'snet-apim' + apimNsgName: !empty(apimNsgName) ? apimNsgName : 'nsg-apim-${resourceToken}' + privateEndpointSubnetName: !empty(privateEndpointSubnetName) ? privateEndpointSubnetName : 'snet-private-endpoint' + privateEndpointNsgName: !empty(privateEndpointNsgName) ? privateEndpointNsgName : 'nsg-pe-${resourceToken}' + functionAppSubnetName: !empty(functionAppSubnetName) ? functionAppSubnetName : 'snet-functionapp' + functionAppNsgName: !empty(functionAppNsgName) ? functionAppNsgName : 'nsg-functionapp-${resourceToken}' + enableAgentSubnet: foundryNetworkInjectionEnabled + agentSubnetName: !empty(agentSubnetName) ? agentSubnetName : 'snet-agents' + agentSubnetNsgName: !empty(agentSubnetNsgName) ? agentSubnetNsgName : 'nsg-agents-${resourceToken}' + agentSubnetAddressPrefix: agentSubnetPrefix + vnetAddressPrefix: vnetAddressPrefix + apimSubnetAddressPrefix: apimSubnetPrefix + isAPIMV2SKU: apimSku == 'StandardV2' || apimSku == 'PremiumV2' + privateEndpointSubnetAddressPrefix: privateEndpointSubnetPrefix + functionAppSubnetAddressPrefix: functionAppSubnetPrefix + location: location + tags: tags + privateDnsZoneNames: privateDnsZoneNames + apimRouteTableName: !empty(apimRouteTableName) ? apimRouteTableName : 'rt-apim-${resourceToken}' + } + dependsOn: [ + dnsDeployment + ] +} + +module vnetExisting './modules/networking/vnet-existing.bicep' = if(useExistingVnet) { + name: 'vnetExisting' + scope: resourceGroup + params: { + name: vnetName + apimSubnetName: !empty(apimSubnetName) ? apimSubnetName : 'snet-apim' + privateEndpointSubnetName: !empty(privateEndpointSubnetName) ? privateEndpointSubnetName : 'snet-private-endpoint' + functionAppSubnetName: !empty(functionAppSubnetName) ? functionAppSubnetName : 'snet-functionapp' + agentSubnetName: foundryNetworkInjectionEnabled ? agentSubnetName : '' + vnetRG: existingVnetRG + } + dependsOn: [ + dnsDeployment + ] +} + +module apimManagedIdentity './modules/security/managed-identity-apim.bicep' = { + name: 'apim-managed-identity' + scope: resourceGroup + params: { + name: !empty(apimIdentityName) ? apimIdentityName : '${abbrs.managedIdentityUserAssignedIdentities}apim-${resourceToken}' + location: location + tags: tags + } +} + +module usageManagedIdentity './modules/security/managed-identity-usage.bicep' = { + name: 'logicapp-usage-managed-identity' + scope: resourceGroup + params: { + name: !empty(usageLogicAppIdentityName) ? usageLogicAppIdentityName : '${abbrs.managedIdentityUserAssignedIdentities}logicapp-${resourceToken}' + location: location + tags: tags + cosmosDbAccountName: cosmosDb.outputs.cosmosDbAccountName + } +} + +module monitoring './modules/monitor/monitoring.bicep' = { + name: 'monitoring' + scope: resourceGroup + params: { + location: location + tags: tags + logAnalyticsName: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.operationalInsightsWorkspaces}${resourceToken}' + useExistingLogAnalytics: useExistingLogAnalytics + existingLogAnalyticsName: existingLogAnalyticsName + existingLogAnalyticsRG: existingLogAnalyticsRG + existingLogAnalyticsSubscriptionId: !empty(existingLogAnalyticsSubscriptionId) ? existingLogAnalyticsSubscriptionId : subscription().subscriptionId + apimApplicationInsightsName: !empty(apimApplicationInsightsName) ? apimApplicationInsightsName : '${abbrs.insightsComponents}apim-${resourceToken}' + apimApplicationInsightsDashboardName: !empty(apimApplicationInsightsDashboardName) ? apimApplicationInsightsDashboardName : '${abbrs.portalDashboards}apim-${resourceToken}' + functionApplicationInsightsName: !empty(funcApplicationInsightsName) ? funcApplicationInsightsName : '${abbrs.insightsComponents}func-${resourceToken}' + functionApplicationInsightsDashboardName: !empty(funcAplicationInsightsDashboardName) ? funcAplicationInsightsDashboardName : '${abbrs.portalDashboards}func-${resourceToken}' + foundryApplicationInsightsName: !empty(foundryApplicationInsightsName) ? foundryApplicationInsightsName : '${abbrs.insightsComponents}aif-${resourceToken}' + foundryApplicationInsightsDashboardName: !empty(foundryApplicationInsightsDashboardName) ? foundryApplicationInsightsDashboardName : '${abbrs.portalDashboards}aif-${resourceToken}' + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + applicationInsightsDnsZoneName: monitorPrivateDnsZoneName + createDashboard: createAppInsightsDashboards + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingMonitorDnsZoneId + usePrivateLinkScope: useAzureMonitorPrivateLinkScope + } +} + +module keyVault './modules/keyvault/keyvault.bicep' = { + name: 'key-vault' + scope: resourceGroup + params: { + keyVaultName: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + skuName: keyVaultSkuName + publicNetworkAccess: keyVaultExternalNetworkAccess + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + keyVaultPrivateEndpointName: !empty(keyVaultPrivateEndpointName) ? keyVaultPrivateEndpointName : '${abbrs.keyVaultVaults}pe-${resourceToken}' + keyVaultDnsZoneName: keyVaultPrivateDnsZoneName + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingKeyVaultDnsZoneId + apimPrincipalId: apimManagedIdentity.outputs.managedIdentityPrincipalId + } +} + +// AI Foundry deployment. +// The first element of `aiFoundryInstances` is the **primary** Foundry resource. Its endpoint is +// reused by APIM as the backend for content safety and as the named-value URL for PII / language +// processing (both APIs are exposed on the unified AI Services endpoint of the account). +// Additional entries in `aiFoundryInstances` simply provide more regional Foundry resources that +// can host LLM model deployments declared in `aiFoundryModelsConfig`. +module foundry 'modules/foundry/foundry.bicep' = { + name: 'ai-foundry' + scope: resourceGroup + params: { + aiServicesConfig: aiFoundryInstances + modelsConfig: transformedAiFoundryModelsConfig + lawId: monitoring.outputs.logAnalyticsWorkspaceId + apimPrincipalId: apimManagedIdentity.outputs.managedIdentityPrincipalId + foundryProjectName: 'citadel-governance-project' + appInsightsInstrumentationKey: monitoring.outputs.foundryApplicationInsightsInstrumentationKey + appInsightsId: monitoring.outputs.foundryApplicationInsightsId + publicNetworkAccess: aiFoundryExternalNetworkAccess + disableKeyAuth: false + resourceToken: resourceToken + tags: tags + // Networking parameters for private endpoints + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + vNetLocation: useExistingVnet ? vnetExisting.outputs.location : vnet.outputs.location + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + aiFoundryPrivateEndpointBaseName: !empty(aiFoundryPrivateEndpointName) ? aiFoundryPrivateEndpointName : '${abbrs.cognitiveServicesAccounts}foundry-pe-${resourceToken}' + aiServicesDnsZoneNames: aiFoundryDnsZoneNames + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceIds: aiFoundryDnsZoneResourceIds + networkInjectionEnabled: foundryNetworkInjectionEnabled + agentSubnetName: foundryNetworkInjectionEnabled ? (useExistingVnet ? vnetExisting.outputs.agentSubnetName : vnet.outputs.agentSubnetName) : '' + // Key Vault connection parameters + keyVaultId: keyVault.outputs.keyVaultId + keyVaultUri: keyVault.outputs.keyVaultUri + } +} + +module eventHub './modules/event-hub/event-hub.bicep' = { + name: 'event-hub' + scope: resourceGroup + params: { + name: !empty(eventHubNamespaceName) ? eventHubNamespaceName : '${abbrs.eventHubNamespaces}${resourceToken}' + location: location + tags: tags + eventHubPrivateEndpointName: !empty(eventHubPrivateEndpointName) ? eventHubPrivateEndpointName : '${abbrs.eventHubNamespaces}pe-${resourceToken}' + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + eventHubDnsZoneName: eventHubPrivateDnsZoneName + publicNetworkAccess: eventHubNetworkAccess + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingEventHubDnsZoneId + capacity: eventHubCapacityUnits + } +} + +module managedRedis './modules/redis/redis.bicep' = if (enableManagedRedis) { + name: 'managed-redis' + scope: resourceGroup + params: { + name: !empty(redisCacheName) ? redisCacheName : '${abbrs.cacheRedis}${resourceToken}' + location: location + tags: tags + skuName: redisSkuName + skuCapacity: redisSkuCapacity + publicNetworkAccess: redisPublicNetworkAccess + minimumTlsVersion: redisMinimumTlsVersion + usePrivateEndpoint: true + redisPrivateEndpointName: !empty(redisPrivateEndpointName) ? redisPrivateEndpointName : '${abbrs.cacheRedis}pe-${resourceToken}' + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + redisDnsZoneName: redisPrivateDnsZoneName + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingRedisDnsZoneId + } +} + +// ============================================================================ +// ENTRA ID CONFIGURATION +// ============================================================================ +// Entra ID App Registration is created independently by the entra-id-setup script +// (bicep/infra/entra-id-setup/setup.ps1) which stores values as azd environment +// variables. These values flow through main.bicepparam -> parameters here. +// For bring-your-own app registrations, set the values directly via azd env set. +// See: bicep/infra/entra-id-setup/README.md + +var resolvedEntraTenantId = !empty(entraTenantId) ? entraTenantId : subscription().tenantId +var resolvedEntraClientId = !empty(entraClientId) ? entraClientId : 'not-configured' +var resolvedEntraAudience = !empty(entraAudience) ? entraAudience : (entraAuth ? 'api://${resolvedEntraClientId}' : 'https://cognitiveservices.azure.com/.default') + +// Store client secret in Key Vault when provided via parameter (for BYOA scenarios +// where the secret isn't already in Key Vault from the entra-id-setup script) +module entraClientSecretKv './modules/keyvault/keyvault-secret.bicep' = if (entraAuth && !empty(entraClientSecret)) { + name: 'entra-client-secret-kv' + scope: resourceGroup + params: { + keyVaultName: keyVault.outputs.keyVaultName + secretName: 'ENTRA-APP-CLIENT-SECRET' + secretValue: entraClientSecret + } +} + +module apim './modules/apim/apim.bicep' = { + name: 'apim' + scope: resourceGroup + params: { + name: !empty(apimServiceName) ? apimServiceName : '${abbrs.apiManagementService}${resourceToken}' + location: location + tags: tags + applicationInsightsName: monitoring.outputs.apimApplicationInsightsName + managedIdentityName: apimManagedIdentity.outputs.managedIdentityName + entraAuth: entraAuth + clientAppId: resolvedEntraClientId + tenantId: resolvedEntraTenantId + audience: resolvedEntraAudience + eventHubName: eventHub.outputs.eventHubName + eventHubEndpoint: eventHub.outputs.eventHubEndpoint + eventHubPIIName: eventHub.outputs.eventHubPIIName + eventHubPIIEndpoint: eventHub.outputs.eventHubEndpoint + apimSubnetId: useExistingVnet ? vnetExisting.outputs.apimSubnetId : vnet.outputs.apimSubnetId + aiLanguageServiceUrl: primaryFoundryEndpoint + contentSafetyServiceUrl: primaryFoundryEndpoint + apimNetworkType: apimNetworkType + enablePIIAnonymization: enableAIGatewayPiiRedaction + enableAIModelInference: enableAIModelInference + enableDocumentIntelligence: enableDocumentIntelligence + enableOpenAIRealtime: enableOpenAIRealtime + enableAzureAISearch: enableAzureAISearch + aiSearchInstances: aiSearchInstances + llmBackendConfig: llmBackendConfig + enableRedisCache: enableManagedRedis + redisCacheConnectionString: enableManagedRedis ? managedRedis.outputs.redisCacheConnectionString : '' + redisCacheResourceId: enableManagedRedis ? managedRedis.outputs.redisResourceId : '' + enableEmbeddingsBackend: enableManagedRedis + embeddingsBackendUrl: enableManagedRedis ? primaryFoundryEmbeddingsBackendUrl : '' + sku: apimSku + skuCount: apimSkuUnits + usePrivateEndpoint: apimV2UsePrivateEndpoint + apimV2PrivateEndpointName: !empty(apimV2PrivateEndpointName) ? apimV2PrivateEndpointName : '${abbrs.apiManagementService}pe-${resourceToken}' + apimV2PublicNetworkAccess: apimV2PublicNetworkAccess + privateEndpointSubnetId: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetId : vnet.outputs.privateEndpointSubnetId + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingApimGatewayDnsZoneId + isMCPSampleDeployed: true + azureMonitorLogSettings: azureMonitorLogSettings + appInsightsLogSettings: appInsightsLogSettings + enableUnifiedAiApi: enableUnifiedAiApi + enableJwtAuth: entraAuth + jwtTenantId: resolvedEntraTenantId + jwtAppRegistrationId: resolvedEntraClientId + } +} + +module cosmosDb './modules/cosmos-db/cosmos-db.bicep' = { + name: 'cosmos-db' + scope: resourceGroup + params: { + accountName: !empty(cosmosDbAccountName) ? cosmosDbAccountName : '${abbrs.documentDBDatabaseAccounts}${resourceToken}' + location: location + tags: tags + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + cosmosDnsZoneName: cosmosDbPrivateDnsZoneName + cosmosPrivateEndpointName: !empty(cosmosDbPrivateEndpointName) ? cosmosDbPrivateEndpointName : '${abbrs.documentDBDatabaseAccounts}pe-${resourceToken}' + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + dnsZoneResourceId: existingCosmosDbDnsZoneId + throughput: cosmosDbRUs + publicAccess: cosmosDbPublicAccess + } +} + +module storageAccount './modules/functionapp/storageaccount.bicep' = { + name: 'storage' + scope: resourceGroup + params: { + location: location + tags: tags + storageAccountName: !empty(storageAccountName) ? storageAccountName : 'funcusage${resourceToken}' + functionAppManagedIdentityName: usageManagedIdentity.outputs.managedIdentityName + vNetName: useExistingVnet ? vnetExisting.outputs.vnetName : vnet.outputs.vnetName + privateEndpointSubnetName: useExistingVnet ? vnetExisting.outputs.privateEndpointSubnetName : vnet.outputs.privateEndpointSubnetName + storageBlobDnsZoneName: storageBlobPrivateDnsZoneName + storageFileDnsZoneName: storageFilePrivateDnsZoneName + storageTableDnsZoneName: storageTablePrivateDnsZoneName + storageQueueDnsZoneName: storageQueuePrivateDnsZoneName + storageBlobPrivateEndpointName: !empty(storageBlobPrivateEndpointName) ? storageBlobPrivateEndpointName : '${abbrs.storageStorageAccounts}blob-pe-${resourceToken}' + storageFilePrivateEndpointName: !empty(storageFilePrivateEndpointName) ? storageFilePrivateEndpointName : '${abbrs.storageStorageAccounts}file-pe-${resourceToken}' + storageTablePrivateEndpointName: !empty(storageTablePrivateEndpointName) ? storageTablePrivateEndpointName : '${abbrs.storageStorageAccounts}table-pe-${resourceToken}' + storageQueuePrivateEndpointName: !empty(storageQueuePrivateEndpointName) ? storageQueuePrivateEndpointName : '${abbrs.storageStorageAccounts}queue-pe-${resourceToken}' + logicContentShareName: logicContentShareName + vNetRG: useExistingVnet ? vnetExisting.outputs.vnetRG : vnet.outputs.vnetRG + dnsZoneRG: !useExistingVnet ? resourceGroup.name : dnsZoneRG + dnsSubscriptionId: !empty(dnsSubscriptionId) ? dnsSubscriptionId : subscription().subscriptionId + storageBlobDnsZoneResourceId: existingStorageBlobDnsZoneId + storageFileDnsZoneResourceId: existingStorageFileDnsZoneId + storageTableDnsZoneResourceId: existingStorageTableDnsZoneId + storageQueueDnsZoneResourceId: existingStorageQueueDnsZoneId + } +} + +module logicApp './modules/logicapp/logicapp.bicep' = { + name: 'usageLogicApp' + scope: resourceGroup + params: { + location: location + tags: tags + logicAppName: !empty(usageProcessingLogicAppName) ? usageProcessingLogicAppName : '${abbrs.logicWorkflows}usage-${resourceToken}' + azdserviceName: 'usageProcessingLogicApp' + storageAccountName: storageAccount.outputs.storageAccountName + applicationInsightsName: monitoring.outputs.funcApplicationInsightsName + skuFamily: 'WS' + skuName: 'WS1' + skuCapaicty: logicAppsSkuCapacityUnits + skuSize: 'WS1' + skuTier: 'WorkflowStandard' + isReserved: false + cosmosDbAccountName: cosmosDb.outputs.cosmosDbAccountName + eventHubName: eventHub.outputs.eventHubName + eventHubNamespaceName: eventHub.outputs.eventHubNamespaceName + cosmosDBDatabaseName: cosmosDb.outputs.cosmosDbDatabaseName + cosmosDBContainerConfigName: cosmosDb.outputs.cosmosDbStreamingExportConfigContainerName + cosmosDBContainerUsageName: cosmosDb.outputs.cosmosDbContainerName + cosmosDBContainerPIIName: cosmosDb.outputs.cosmosDbPiiUsageContainerName + cosmosDBContainerLLMUsageName: cosmosDb.outputs.cosmosDbLLMUsageContainerName + eventHubPIIName: eventHub.outputs.eventHubPIIName + apimAppInsightsName: monitoring.outputs.apimApplicationInsightsName + functionAppSubnetId: useExistingVnet ? vnetExisting.outputs.functionAppSubnetId : vnet.outputs.functionAppSubnetId + fileShareName: logicContentShareName + } +} + +module apiCenter './modules/apic/apic.bicep' = if(enableAPICenter) { + name: 'api-center' + scope: resourceGroup + params: { + apicServiceName: !empty(apicServiceName) ? apicServiceName : '${abbrs.apiCenterService}${resourceToken}' + apicsku: apicSku + location: !empty(apicLocation) ? apicLocation : location + tags: tags + } +} + +module apiCenterOnboarding './modules/apim/api-center-onboarding-all.bicep' = if(enableAPICenter) { + name: 'api-center-onboarding' + scope: resourceGroup + params: { + apiCenterServiceName: enableAPICenter ? apiCenter.outputs.name : '' + apiCenterWorkspaceName: enableAPICenter ? apiCenter.outputs.defaultWorkspaceName : 'default' + apimGatewayUrl: apim.outputs.apimGatewayUrl + isMCPSampleDeployed: true + enableAzureAISearch: enableAzureAISearch + enableAIModelInference: enableAIModelInference + enableOpenAIRealtime: enableOpenAIRealtime + enableDocumentIntelligence: enableDocumentIntelligence + } + dependsOn: [ + apim + apiCenter + ] +} + +// Grant AI Foundry resources access to Key Vault (deployed after both Key Vault and Foundry) +module keyVaultFoundryRbac './modules/keyvault/keyvault-rbac.bicep' = { + name: 'key-vault-foundry-rbac' + scope: resourceGroup + params: { + keyVaultName: keyVault.outputs.keyVaultName + aiFoundryPrincipalIds: foundry.outputs.aiFoundryPrincipalIds + } + dependsOn: [ + keyVault + foundry + ] +} + +output APIM_NAME string = apim.outputs.apimName +output APIM_AOI_PATH string = apim.outputs.apimOpenaiApiPath +output APIM_GATEWAY_URL string = apim.outputs.apimGatewayUrl +output AZURE_RESOURCE_GROUP string = resourceGroup.name +output AI_FOUNDRY_SERVICES array = foundry.outputs.extendedAIServicesConfig +output LLM_BACKEND_CONFIG array = llmBackendConfig +output KEY_VAULT_NAME string = keyVault.outputs.keyVaultName +output KEY_VAULT_URI string = keyVault.outputs.keyVaultUri +output ENTRA_AUTH_ENABLED bool = entraAuth +output ENTRA_CLIENT_ID string = resolvedEntraClientId +output ENTRA_TENANT_ID string = resolvedEntraTenantId +output ENTRA_AUDIENCE string = resolvedEntraAudience diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicepparam new file mode 100644 index 000000000..cb7163bb7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.bicepparam @@ -0,0 +1,335 @@ +using './main.bicep' + +// ============================================================================ +// BASIC PARAMETERS +// ============================================================================ +param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', 'citadel-dev') +param location = readEnvironmentVariable('AZURE_LOCATION', 'swedencentral') +param apicLocation = readEnvironmentVariable('APIC_LOCATION', 'swedencentral') +param tags = { + 'azd-env-name': readEnvironmentVariable('AZURE_ENV_NAME', 'citadel-dev') + SecurityControl: 'Ignore' +} + +// ============================================================================ +// RESOURCE NAMES - Assign custom names to different provisioned services +// ============================================================================ +param resourceGroupName = readEnvironmentVariable('AZURE_RESOURCE_GROUP', '') +param apimIdentityName = readEnvironmentVariable('APIM_IDENTITY_NAME', '') +param usageLogicAppIdentityName = readEnvironmentVariable('USAGE_LOGIC_APP_IDENTITY_NAME', '') +param apimServiceName = readEnvironmentVariable('APIM_SERVICE_NAME', '') +param logAnalyticsName = readEnvironmentVariable('LOG_ANALYTICS_NAME', '') +param apimApplicationInsightsDashboardName = readEnvironmentVariable('APIM_APP_INSIGHTS_DASHBOARD_NAME', '') +param funcAplicationInsightsDashboardName = readEnvironmentVariable('FUNC_APP_INSIGHTS_DASHBOARD_NAME', '') +param foundryApplicationInsightsDashboardName = readEnvironmentVariable('FOUNDRY_APP_INSIGHTS_DASHBOARD_NAME', '') +param apimApplicationInsightsName = readEnvironmentVariable('APIM_APP_INSIGHTS_NAME', '') +param funcApplicationInsightsName = readEnvironmentVariable('FUNC_APP_INSIGHTS_NAME', '') +param foundryApplicationInsightsName = readEnvironmentVariable('FOUNDRY_APP_INSIGHTS_NAME', '') +param eventHubNamespaceName = readEnvironmentVariable('EVENTHUB_NAMESPACE_NAME', '') +param cosmosDbAccountName = readEnvironmentVariable('COSMOS_DB_ACCOUNT_NAME', '') +param usageProcessingLogicAppName = readEnvironmentVariable('USAGE_PROCESSING_LOGIC_APP_NAME', '') +param storageAccountName = readEnvironmentVariable('STORAGE_ACCOUNT_NAME', '') +param apicServiceName = readEnvironmentVariable('APIC_SERVICE_NAME', '') +param aiFoundryResourceName = readEnvironmentVariable('AI_FOUNDRY_RESOURCE_NAME', '') +param keyVaultName = readEnvironmentVariable('KEY_VAULT_NAME', '') +param redisCacheName = readEnvironmentVariable('REDIS_CACHE_NAME', '') + +// ============================================================================ +// MONITORING - Log Analytics configuration +// ============================================================================ +param useExistingLogAnalytics = bool(readEnvironmentVariable('USE_EXISTING_LOG_ANALYTICS', 'false')) +param existingLogAnalyticsName = readEnvironmentVariable('EXISTING_LOG_ANALYTICS_NAME', '') +param existingLogAnalyticsRG = readEnvironmentVariable('EXISTING_LOG_ANALYTICS_RG', '') +param existingLogAnalyticsSubscriptionId = readEnvironmentVariable('EXISTING_LOG_ANALYTICS_SUBSCRIPTION_ID', '') + +// ============================================================================ +// NETWORKING PARAMETERS - Network configuration and access controls +// ============================================================================ +param vnetName = readEnvironmentVariable('VNET_NAME', '') +param useExistingVnet = bool(readEnvironmentVariable('USE_EXISTING_VNET', 'false')) +param existingVnetRG = readEnvironmentVariable('EXISTING_VNET_RG', '') + +// Subnet names +param apimSubnetName = readEnvironmentVariable('APIM_SUBNET_NAME', '') +param privateEndpointSubnetName = readEnvironmentVariable('PRIVATE_ENDPOINT_SUBNET_NAME', '') +param functionAppSubnetName = readEnvironmentVariable('FUNCTION_APP_SUBNET_NAME', '') +param agentSubnetName = readEnvironmentVariable('AGENT_SUBNET_NAME', '') + +// NSG & route table names +param apimNsgName = readEnvironmentVariable('APIM_NSG_NAME', '') +param privateEndpointNsgName = readEnvironmentVariable('PRIVATE_ENDPOINT_NSG_NAME', '') +param functionAppNsgName = readEnvironmentVariable('FUNCTION_APP_NSG_NAME', '') +param agentSubnetNsgName = readEnvironmentVariable('AGENT_SUBNET_NSG_NAME', '') +param apimRouteTableName = readEnvironmentVariable('APIM_ROUTE_TABLE_NAME', '') + +// VNet address space and subnet prefixes +param vnetAddressPrefix = readEnvironmentVariable('VNET_ADDRESS_PREFIX', '10.170.0.0/24') +param apimSubnetPrefix = readEnvironmentVariable('APIM_SUBNET_PREFIX', '10.170.0.0/26') +param privateEndpointSubnetPrefix = readEnvironmentVariable('PRIVATE_ENDPOINT_SUBNET_PREFIX', '10.170.0.64/26') +param functionAppSubnetPrefix = readEnvironmentVariable('FUNCTION_APP_SUBNET_PREFIX', '10.170.0.128/26') +param agentSubnetPrefix = readEnvironmentVariable('AGENT_SUBNET_PREFIX', '10.170.0.192/26') + +// Foundry network injection (agents). Defaults to true; required agent subnet is provisioned automatically when not using an existing VNet. +param foundryNetworkInjectionEnabled = bool(readEnvironmentVariable('FOUNDRY_NETWORK_INJECTION_ENABLED', 'true')) + +// DNS Zone parameters (legacy approach - single subscription/RG) +param dnsZoneRG = readEnvironmentVariable('DNS_ZONE_RG', '') +param dnsSubscriptionId = readEnvironmentVariable('DNS_SUBSCRIPTION_ID', '') + +// Existing Private DNS Zones (BYO approach - specify resource IDs per DNS zone type) +// Use this when you have existing Private DNS Zones in different subscriptions/resource groups +// Leave empty strings to use the legacy dnsZoneRG/dnsSubscriptionId approach +param existingPrivateDnsZones = { + openai: readEnvironmentVariable('EXISTING_DNS_ZONE_OPENAI', '') // privatelink.openai.azure.com + keyVault: readEnvironmentVariable('EXISTING_DNS_ZONE_KEYVAULT', '') // privatelink.vaultcore.azure.net + monitor: readEnvironmentVariable('EXISTING_DNS_ZONE_MONITOR', '') // privatelink.monitor.azure.com + eventHub: readEnvironmentVariable('EXISTING_DNS_ZONE_EVENTHUB', '') // privatelink.servicebus.windows.net + cosmosDb: readEnvironmentVariable('EXISTING_DNS_ZONE_COSMOSDB', '') // privatelink.documents.azure.com + storageBlob: readEnvironmentVariable('EXISTING_DNS_ZONE_STORAGE_BLOB', '') // privatelink.blob.core.windows.net + storageFile: readEnvironmentVariable('EXISTING_DNS_ZONE_STORAGE_FILE', '') // privatelink.file.core.windows.net + storageTable: readEnvironmentVariable('EXISTING_DNS_ZONE_STORAGE_TABLE', '') // privatelink.table.core.windows.net + storageQueue: readEnvironmentVariable('EXISTING_DNS_ZONE_STORAGE_QUEUE', '') // privatelink.queue.core.windows.net + cognitiveServices: readEnvironmentVariable('EXISTING_DNS_ZONE_COGNITIVE', '') // privatelink.cognitiveservices.azure.com + apimGateway: readEnvironmentVariable('EXISTING_DNS_ZONE_APIM', '') // privatelink.azure-api.net + aiServices: readEnvironmentVariable('EXISTING_DNS_ZONE_AI_SERVICES', '') // privatelink.services.azure.com + redis: readEnvironmentVariable('EXISTING_DNS_ZONE_REDIS', '') // privatelink.redis.azure.net +} + +// Private Endpoint names +param storageBlobPrivateEndpointName = readEnvironmentVariable('STORAGE_BLOB_PE_NAME', '') +param storageFilePrivateEndpointName = readEnvironmentVariable('STORAGE_FILE_PE_NAME', '') +param storageTablePrivateEndpointName = readEnvironmentVariable('STORAGE_TABLE_PE_NAME', '') +param storageQueuePrivateEndpointName = readEnvironmentVariable('STORAGE_QUEUE_PE_NAME', '') +param cosmosDbPrivateEndpointName = readEnvironmentVariable('COSMOS_DB_PE_NAME', '') +param eventHubPrivateEndpointName = readEnvironmentVariable('EVENTHUB_PE_NAME', '') +param apimV2PrivateEndpointName = readEnvironmentVariable('APIM_V2_PE_NAME', '') +param aiFoundryPrivateEndpointName = readEnvironmentVariable('AI_FOUNDRY_PE_NAME', '') +param keyVaultPrivateEndpointName = readEnvironmentVariable('KEY_VAULT_PE_NAME', '') +param redisPrivateEndpointName = readEnvironmentVariable('REDIS_PE_NAME', '') + +// Services network access configuration +param apimNetworkType = readEnvironmentVariable('APIM_NETWORK_TYPE', 'External') +param apimV2UsePrivateEndpoint = bool(readEnvironmentVariable('APIM_V2_USE_PRIVATE_ENDPOINT', 'true')) +param apimV2PublicNetworkAccess = bool(readEnvironmentVariable('APIM_V2_PUBLIC_NETWORK_ACCESS', 'true')) +param cosmosDbPublicAccess = readEnvironmentVariable('COSMOS_DB_PUBLIC_ACCESS', 'Disabled') +param eventHubNetworkAccess = readEnvironmentVariable('EVENTHUB_NETWORK_ACCESS', 'Enabled') +param aiFoundryExternalNetworkAccess = readEnvironmentVariable('AI_FOUNDRY_EXTERNAL_NETWORK_ACCESS', 'Disabled') +param keyVaultExternalNetworkAccess = readEnvironmentVariable('KEY_VAULT_EXTERNAL_NETWORK_ACCESS', 'Disabled') +param useAzureMonitorPrivateLinkScope = bool(readEnvironmentVariable('USE_AZURE_MONITOR_PRIVATE_LINK_SCOPE', 'false')) +param redisPublicNetworkAccess = readEnvironmentVariable('REDIS_PUBLIC_NETWORK_ACCESS', 'Disabled') + +// ============================================================================ +// FEATURE FLAGS - Deploy specific capabilities +// ============================================================================ +param createAppInsightsDashboards = bool(readEnvironmentVariable('CREATE_DASHBOARDS', 'false')) +param enableAIModelInference = bool(readEnvironmentVariable('ENABLE_AI_MODEL_INFERENCE', 'true')) +param enableDocumentIntelligence = bool(readEnvironmentVariable('ENABLE_DOCUMENT_INTELLIGENCE', 'false')) +param enableAzureAISearch = bool(readEnvironmentVariable('ENABLE_AZURE_AI_SEARCH', 'false')) +param enableAIGatewayPiiRedaction = bool(readEnvironmentVariable('ENABLE_PII_REDACTION', 'true')) +param enableOpenAIRealtime = bool(readEnvironmentVariable('ENABLE_OPENAI_REALTIME', 'true')) +param entraAuth = bool(readEnvironmentVariable('AZURE_ENTRA_AUTH', 'false')) +param enableAPICenter = bool(readEnvironmentVariable('ENABLE_API_CENTER', 'false')) +param enableManagedRedis = bool(readEnvironmentVariable('ENABLE_MANAGED_REDIS', 'true')) +param enableUnifiedAiApi = bool(readEnvironmentVariable('ENABLE_UNIFIED_AI_API', 'true')) + +// ============================================================================ +// INFERENCE API DIAGNOSTIC LOG SETTINGS +// ============================================================================ + +// Azure Monitor diagnostic log settings for inference APIs +// Controls frontend/backend request/response headers, body bytes, and LLM-specific log settings. +// Max size in bytes for request/response bodies is 262144 bytes (256 KB). +param azureMonitorLogSettings = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: ['Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests'], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +// Application Insights diagnostic log settings for inference APIs +// Controls which headers are captured and body byte limits (max 8192 bytes). +param appInsightsLogSettings = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 8192 } +} + +// ============================================================================ +// COMPUTE SKU & SIZE - SKUs and capacity settings for services +// ============================================================================ +param apimSku = readEnvironmentVariable('APIM_SKU', 'StandardV2') +param apimSkuUnits = int(readEnvironmentVariable('APIM_SKU_UNITS', '1')) +param eventHubCapacityUnits = int(readEnvironmentVariable('EVENTHUB_CAPACITY', '1')) +param cosmosDbRUs = int(readEnvironmentVariable('COSMOS_DB_RUS', '400')) +param logicAppsSkuCapacityUnits = int(readEnvironmentVariable('LOGIC_APPS_SKU_CAPACITY_UNITS', '1')) +param apicSku = readEnvironmentVariable('APIC_SKU', 'Free') +param keyVaultSkuName = readEnvironmentVariable('KEY_VAULT_SKU_NAME', 'standard') +param redisSkuName = readEnvironmentVariable('REDIS_SKU_NAME', 'Balanced_B1') +param redisSkuCapacity = int(readEnvironmentVariable('REDIS_SKU_CAPACITY', '1')) + +// ============================================================================ +// ACCELERATOR SPECIFIC PARAMETERS +// ============================================================================ +param logicContentShareName = readEnvironmentVariable('LOGIC_CONTENT_SHARE_NAME', 'usage-logic-content') + +// AI Search instances configuration - add more instances by adding to this array +// Example: [{name: 'ai-search-01', url: 'https://search1.search.windows.net/', description: 'AI Search 1'}] +param aiSearchInstances = [] + +// AI Foundry instances configuration array +// Per-instance `networkInjectionEnabled` opts the specific Foundry into (or out of) +// agent network injection. Omit it to inherit the global foundryNetworkInjectionEnabled flag. +// Agent subnet is regional - typically only enable injection for the instance in the VNet's region. +param aiFoundryInstances = [ + { + name: readEnvironmentVariable('AI_FOUNDRY_RESOURCE_NAME', '') + location: readEnvironmentVariable('AZURE_LOCATION', 'eastus') + customSubDomainName: '' + defaultProjectName: 'citadel-governance-project' + networkInjectionEnabled: false + } + { + name: readEnvironmentVariable('AI_FOUNDRY_RESOURCE_NAME', '') + location: 'eastus2' + customSubDomainName: '' + defaultProjectName: 'citadel-governance-project' + networkInjectionEnabled: false + } +] + +// AI Foundry model deployments configuration +// Each model can optionally include metadata for the Unified AI API routing: +// - apiVersion: API version for OpenAI-type requests (default: '2024-02-15-preview') +// - timeout: Request timeout in seconds (default: 120) +// - inferenceApiVersion: API version for inference-type requests (e.g., '2024-05-01-preview' for non-OpenAI models) +param aiFoundryModelsConfig = [ + { + name: 'gpt-4.1' + publisher: 'OpenAI' + version: '2025-04-14' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-10-14' + apiVersion: '2025-04-01-preview' + timeout: 180 + aiserviceIndex: 0 + } + { + name: 'DeepSeek-R1' + publisher: 'DeepSeek' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + inferenceApiVersion: '2024-05-01-preview' + aiserviceIndex: 0 + } + { + name: 'text-embedding-3-large' + publisher: 'OpenAI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-04-14' + aiserviceIndex: 0 + } + { + name: 'Mistral-Large-3' + publisher: 'Mistral AI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2099-12-30' + aiserviceIndex: 0 + } + { + name: 'gpt-5.4-mini' + publisher: 'OpenAI' + version: '2026-03-17' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-09-30' + aiserviceIndex: 0 + } + { + name: 'Phi-4' + publisher: 'Microsoft' + version: '7' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-10-14' + apiVersion: '2025-04-01-preview' + timeout: 180 + aiserviceIndex: 0 + } + { + name: 'Phi-4' + publisher: 'Microsoft' + version: '7' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-10-14' + apiVersion: '2025-04-01-preview' + timeout: 180 + aiserviceIndex: 1 + } + { + name: 'gpt-5.4-mini' + publisher: 'OpenAI' + version: '2026-03-17' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2026-09-30' + aiserviceIndex: 1 + } + { + name: 'gpt-5.2' + publisher: 'OpenAI' + version: '2025-12-11' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-02-05' + aiserviceIndex: 1 + } + { + name: 'DeepSeek-R1' + publisher: 'DeepSeek' + version: '1' + sku: 'GlobalStandard' + capacity: 1 + retirementDate: '2099-12-30' + inferenceApiVersion: '2024-05-01-preview' + aiserviceIndex: 1 + } + { + name: 'text-embedding-3-large' + publisher: 'OpenAI' + version: '1' + sku: 'GlobalStandard' + capacity: 100 + retirementDate: '2027-04-14' + aiserviceIndex: 1 + } +] + +// Semantic caching APIM integration configurations +param primaryFoundryEmbeddingModelName = readEnvironmentVariable('PRIMARY_FOUNDRY_EMBEDDING_MODEL_NAME', 'text-embedding-3-large') + +// ============================================================================ +// ENTRA ID AUTHENTICATION +// ============================================================================ +// Values are populated by the entra-id-setup script (bicep/infra/entra-id-setup/setup.ps1) +// which creates the App Registration and stores values as azd environment variables. +// For bring-your-own app registrations, set these values directly via 'azd env set'. +param entraTenantId = readEnvironmentVariable('AZURE_TENANT_ID', '') +param entraClientId = readEnvironmentVariable('AZURE_CLIENT_ID', '') +param entraAudience = readEnvironmentVariable('AZURE_AUDIENCE', '') +param entraClientSecret = readEnvironmentVariable('ENTRA_CLIENT_SECRET', '') diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.dev.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.dev.bicepparam new file mode 100644 index 000000000..662bdc3ff --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.dev.bicepparam @@ -0,0 +1,56 @@ +using './main.bicep' + +/* + * Development Environment Configuration + * Optimized for cost and quick deployments + */ + +// Basic Configuration +param environmentName = 'ai-hub-citadel-dev' +param location = 'swedencentral' +param apicLocation = 'swedencentral' +param resourceGroupName = '' // Auto-generated based on environmentName +param tags = { + 'azd-env-name': 'ai-hub-citadel-dev' + SecurityControl: 'Ignore' + Environment: 'Development' + CostCenter: 'Engineering' +} + +// Use Developer SKU for lower cost +param apimSku = 'Developer' +param apimSkuUnits = 1 + +// Minimal capacity for dev +param cosmosDbRUs = 400 +param eventHubCapacityUnits = 1 + +// Enable dashboards for monitoring during development +param createAppInsightsDashboards = false + +// Disable API Center in dev to save costs +param enableAPICenter = true + +// Enable features for testing +param enableAIGatewayPiiRedaction = true +param enableAIModelInference = true + +// Azure Managed Redis (AMR) +param enableManagedRedis = true +param redisPublicNetworkAccess = 'Disabled' +param redisSkuName = 'Balanced_B0' +param redisSkuCapacity = 1 + +// No Entra ID auth in dev (simplifies testing) +param entraAuth = false + +// Use new Log Analytics workspace (don't use existing) +param useExistingLogAnalytics = false + +// Public network access for easier development +param cosmosDbPublicAccess = 'Enabled' +param eventHubNetworkAccess = 'Enabled' +param keyVaultExternalNetworkAccess = 'Enabled' + +// Key Vault SKU (standard is sufficient for dev) +param keyVaultSkuName = 'standard' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.prod.bicepparam b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.prod.bicepparam new file mode 100644 index 000000000..e795c631d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/main.parameters.prod.bicepparam @@ -0,0 +1,115 @@ +using './main.bicep' + +/* + * Production Environment Configuration + * Optimized for high availability and performance + */ + +// Basic Configuration +param environmentName = 'citadel-prod' +param location = 'swedencentral' +param apicLocation = 'swedencentral' +param resourceGroupName = '' // Auto-generated based on environmentName +param tags = { + 'azd-env-name': 'citadel-prod' + SecurityControl: 'Required' + Environment: 'Production' + CostCenter: 'Platform' + Criticality: 'High' +} + +// Production-grade SKUs +param apimSku = 'PremiumV2' +param apimSkuUnits = 2 + +// Higher capacity for production workloads +param cosmosDbRUs = 3000 +param eventHubCapacityUnits = 5 + +// Enable monitoring dashboards +param createAppInsightsDashboards = true + +// Enable all production features +param enableAPICenter = true +param enableAIGatewayPiiRedaction = true +param enableAIModelInference = true +// Azure Managed Redis (AMR) +param enableManagedRedis = true +param redisPublicNetworkAccess = 'Disabled' +param redisSkuName = 'Balanced_B10' +param redisSkuCapacity = 2 +param enableDocumentIntelligence = true +param enableAzureAISearch = true +param enableOpenAIRealtime = true + +// Enable Entra ID authentication for production +param entraAuth = true +param entraTenantId = '' // Set via environment variable or override +param entraClientId = '' // Set via environment variable or override +param entraAudience = '' // Set via environment variable or override + +// Secure network configuration +param apimNetworkType = 'Internal' +param apimV2UsePrivateEndpoint = true +param apimV2PublicNetworkAccess = false +param cosmosDbPublicAccess = 'Disabled' +param eventHubNetworkAccess = 'Disabled' +param keyVaultExternalNetworkAccess = 'Disabled' + +// Key Vault SKU (premium recommended for production for HSM support) +param keyVaultSkuName = 'premium' + +// Enable Azure Monitor Private Link Scope +param useAzureMonitorPrivateLinkScope = true + +// Log Analytics Configuration (use existing workspace if available) +param useExistingLogAnalytics = false +param existingLogAnalyticsName = '' // Set to existing workspace name if reusing +param existingLogAnalyticsRG = '' // Set to existing workspace resource group +param existingLogAnalyticsSubscriptionId = '' // Set if workspace is in different subscription + +// Production AI Foundry configuration +param aiFoundryInstances = [ + { + name: '' + location: 'eastus' + customSubDomainName: '' + defaultProjectName: 'production-project' + } + { + name: '' + location: 'westus' + customSubDomainName: '' + defaultProjectName: 'production-project' + } +] + +param aiFoundryModelsConfig = [ + { + name: 'gpt-4o' + publisher: 'OpenAI' + version: '2024-11-20' + sku: 'GlobalStandard' + capacity: 200 + retirementDate: '2026-09-30' + aiserviceIndex: 0 + } + { + name: 'gpt-4o-mini' + publisher: 'OpenAI' + version: '2024-07-18' + sku: 'GlobalStandard' + capacity: 200 + retirementDate: '2026-09-30' + aiserviceIndex: 0 + } + { + name: 'gpt-4o' + publisher: 'OpenAI' + version: '2024-11-20' + sku: 'GlobalStandard' + capacity: 200 + retirementDate: '2026-09-30' + aiserviceIndex: 1 + } +] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic-metadata.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic-metadata.json new file mode 100644 index 000000000..82ec9c6d6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic-metadata.json @@ -0,0 +1,74 @@ +{ + "metadata": [ + { + "name": "Visibility", + "schema": "{\"title\":\"Visibility\",\"description\":\"Flag to indicate if the service is visible in API Center portal\",\"type\":\"boolean\"}", + "assignedTo": [ + { + "entity": "api", + "required": false + } + ] + }, + { + "name": "Categories", + "schema": "{\"title\":\"Categories\",\"description\":\"Category classification for the API\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"oneOf\":[{\"const\":\"Productivity\",\"description\":\"Productivity\"},{\"const\":\"Developer Tools\",\"description\":\"Developer Tools\"},{\"const\":\"AI/ML\",\"description\":\"AI/ML\"},{\"const\":\"OpenAI\",\"description\":\"OpenAI\"},{\"const\":\"Data & Analytics\",\"description\":\"Data & Analytics\"},{\"const\":\"Security\",\"description\":\"Security\"},{\"const\":\"Communication\",\"description\":\"Communication\"},{\"const\":\"Business Intelligence\",\"description\":\"Business Intelligence\"},{\"const\":\"Integration\",\"description\":\"Integration\"},{\"const\":\"Search\",\"description\":\"Search\"},{\"const\":\"Model Inference\",\"description\":\"Model Inference\"},{\"const\":\"LLM\",\"description\":\"LLM\"},{\"const\":\"Multi-Provider\",\"description\":\"Multi-Provider\"},{\"const\":\"Real-time\",\"description\":\"Real-time\"},{\"const\":\"Document Processing\",\"description\":\"Document Processing\"},{\"const\":\"Sample\",\"description\":\"Sample\"},{\"const\":\"Weather\",\"description\":\"Weather\"},{\"const\":\"Other\",\"description\":\"Other\"}]}}", + "assignedTo": [ + { + "entity": "api", + "required": false + } + ] + }, + { + "name": "Vendor", + "schema": "{\"title\":\"Vendor\",\"description\":\"The vendor or provider of the API\",\"type\":\"string\",\"oneOf\":[{\"const\":\"Microsoft\",\"description\":\"Microsoft\"},{\"const\":\"Partner\",\"description\":\"Partner\"},{\"const\":\"Internal\",\"description\":\"Internal\"}]}", + "assignedTo": [ + { + "entity": "api", + "required": false + } + ] + }, + { + "name": "Endpoint", + "schema": "{\"title\":\"Endpoint\",\"description\":\"The endpoint type and location of the API\",\"type\":\"string\",\"oneOf\":[{\"const\":\"Public\",\"description\":\"Public\"},{\"const\":\"Private Azure\",\"description\":\"Private Azure\"},{\"const\":\"Private On-premises\",\"description\":\"Private On-premises\"}]}", + "assignedTo": [ + { + "entity": "deployment", + "required": false + } + ] + }, + { + "name": "Type", + "schema": "{\"title\":\"Type\",\"description\":\"Defines the architectural pattern and accessibility model of the API service\",\"type\":\"string\",\"oneOf\":[{\"const\":\"Remote Internal\",\"description\":\"Internal remote backend service\"},{\"const\":\"Remote Public\",\"description\":\"Publicly accessible remote backend service\"},{\"const\":\"Remote\",\"description\":\"Remote backend service\"},{\"const\":\"AI Gateway\",\"description\":\"Service that is governed by AI Gateway\"},{\"const\":\"AI Service\",\"description\":\"Azure AI Service\"},{\"const\":\"Sample API\",\"description\":\"Sample or demonstration API\"}]}", + "assignedTo": [ + { + "entity": "api", + "required": false + } + ] + }, + { + "name": "Icon", + "schema": "{\"title\":\"Icon\",\"description\":\"URL to the icon image for the API\",\"type\":\"string\",\"format\":\"uri\"}", + "assignedTo": [ + { + "entity": "api", + "required": false + } + ] + }, + { + "name": "securitySchemes", + "schema": "{\"title\":\"securitySchemes\",\"description\":\"API Security Schemes\",\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"name\":{\"title\":\"name\",\"description\":\"Name of the API Security Scheme\",\"type\":\"string\"},\"type\":{\"type\":\"string\",\"title\":\"type\",\"description\":\"The Type of the API Security Scheme\",\"oneOf\":[{\"const\":\"http\",\"description\":\"Basic, Bearer and other HTTP authentications schemes\"},{\"const\":\"apiKey\",\"description\":\"API keys and cookie authentication\"},{\"const\":\"oauth2\",\"description\":\"OAuth 2.0 is the industry-standard protocol for authorization.\"},{\"const\":\"openIdConnect \",\"description\":\"OpenID Connect Discovery\"}]},\"authorizationurl\":{\"title\":\"authorizationUrl\",\"type\":\"string\",\"format\":\"uri\"}},\"required\":[\"name\",\"type\"]}}", + "assignedTo": [ + { + "entity": "deployment", + "required": false + } + ] + } + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic.bicep new file mode 100644 index 000000000..7ec0f2703 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/apic.bicep @@ -0,0 +1,221 @@ +// ------------------ +// PARAMETERS +// ------------------ + +// Typically, parameters would be decorated with appropriate metadata and attributes, but as they are very repetetive in these labs we omit them for brevity. + +param apicsku string = 'Free' +param location string = resourceGroup().location +param apicServiceName string +param tags object = {} + +param loadSampleMCPs bool = false + +// ------------------ +// VARIABLES +// ------------------ + +// Load MCP configurations from JSON file +var mcpConfigs = json(loadTextContent('remote-mcp-servers.json')).mcps +var apicMetadataSchema = json(loadTextContent('./apic-metadata.json')).metadata +// ------------------ +// RESOURCES +// ------------------ + +// Create API Center Service resource +resource apiCenterService 'Microsoft.ApiCenter/services@2024-06-01-preview' = { + name: apicServiceName + location: location + tags: tags + sku: { + name: apicsku + } + properties: { + // Configure portal settings + portalSettings: { + // Enable or disable the developer portal + enabled: true + // Configure portal identity provider + // identityProvider: { + // type: 'AzureActiveDirectory' + // } + } + // Configure site profile + siteProfile: { + name: 'Citadel AI Registry' + companyName: 'Citadel AI Registry' + companyUrl: 'https://yourcompany.com' + supportEmail: 'support@yourcompany.com' + termsOfServiceUrl: 'https://yourcompany.com/terms' + privacyPolicyUrl: 'https://yourcompany.com/privacy' + } + } +} + +// Use default workspace +resource apiCenterWorkspace 'Microsoft.ApiCenter/services/workspaces@2024-06-01-preview' = { + parent: apiCenterService + name: 'default' + properties: { + title: 'Default workspace' + description: 'Default workspace' + } +} + +resource apiCenterMetadata 'Microsoft.ApiCenter/services/metadataSchemas@2024-03-01' = [for metadata in apicMetadataSchema: { + name: metadata.name + parent: apiCenterService + properties: { + schema: metadata.schema + assignedTo: [for assignedTo in metadata.assignedTo: { + deprecated: false + entity: assignedTo.entity + required: assignedTo.required + } + ] + } +}] + +// Add environment resources +resource apiEnvironmentDev 'Microsoft.ApiCenter/services/workspaces/environments@2024-06-01-preview' = { + parent: apiCenterWorkspace + name: 'api-dev' + properties: { + title: 'API Development' + description: 'API default development environment' + kind: 'REST' + server: { + managementPortalUri: [ + 'https://portal.azure.com/' + ] + type: 'Development' + } + } +} + +resource apiEnvironmentProd 'Microsoft.ApiCenter/services/workspaces/environments@2024-06-01-preview' = { + parent: apiCenterWorkspace + name: 'api-prod' + properties: { + title: 'API Production' + description: 'API default production environment' + kind: 'REST' + server: { + managementPortalUri: [ + 'https://portal.azure.com/' + ] + type: 'Development' + } + } +} + +resource mcpEnvironmentDev 'Microsoft.ApiCenter/services/workspaces/environments@2024-06-01-preview' = { + parent: apiCenterWorkspace + name: 'mcp-dev' + properties: { + title: 'MCP Development' + description: 'mcp default development environment' + kind: 'MCP' + server: { + managementPortalUri: [ + 'https://portal.azure.com/' + ] + type: 'Development' + } + } +} + +resource mcpEnvironmentProd 'Microsoft.ApiCenter/services/workspaces/environments@2024-06-01-preview' = { + parent: apiCenterWorkspace + name: 'mcp-prod' + properties: { + title: 'MCP Production' + description: 'mcp default production environment' + kind: 'MCP' + server: { + managementPortalUri: [ + 'https://portal.azure.com/' + ] + type: 'Production' + } + } +} + +// Add API resources using a loop +resource apiCenterAPI 'Microsoft.ApiCenter/services/workspaces/apis@2024-06-01-preview' = [for mcp in mcpConfigs: { + parent: apiCenterWorkspace + name: mcp.mcpName + properties: { + title: '${toUpper(substring(mcp.mcpName, 0, 1))}${substring(mcp.mcpName, 1)}' + kind: 'MCP' + lifecycleState: 'Development' + externalDocumentation: [ + { + description: 'Install VS Code' + title: 'Install VS Code' + url: 'https://insiders.vscode.dev/redirect/mcp/install?name=${mcp.mcpName}&config={"type":"sse","url":"${mcp.InstallVSCodeURL}"}' + } + { + description: '${mcp.mcpName} MCP documentation' + title: '${mcp.mcpName} MCP documentation' + url: mcp.DodumentationURL + } + ] + contacts: [] + customProperties: {} + summary: mcp.description + description: mcp.description + } +}] + +// Add API Version resources using a loop +resource apiVersion 'Microsoft.ApiCenter/services/workspaces/apis/versions@2024-06-01-preview' = [for (mcp, i) in mcpConfigs: { + parent: apiCenterAPI[i] + name: '1-0-0' + properties: { + title: '1-0-0' + lifecycleStage: 'Development' + } +}] + +// Add API Definition resource +resource apiDefinition 'Microsoft.ApiCenter/services/workspaces/apis/versions/definitions@2024-06-01-preview' = [for (mcp, i) in mcpConfigs: { + parent: apiVersion[i] + name: 'default' + properties: { + description: 'default' + title: 'default' + } +}] + +// Add API Deployment resource +resource apiDeployment 'Microsoft.ApiCenter/services/workspaces/apis/deployments@2024-06-01-preview' = [for (mcp, i) in mcpConfigs: { + parent: apiCenterAPI[i] + name: 'mcpdeployment' + properties: { + description: 'mcpdeployment' + title: 'mcpdeployment' + environmentId: '/workspaces/default/environments/${apiEnvironmentDev.name}' + definitionId: '/workspaces/default/apis/${mcp.mcpName}/versions/${apiVersion[i].name}/definitions/${apiDefinition[i].name}' + state: 'active' + server: { + runtimeUri: [ + mcp.InstallVSCodeURL + ] + } + } +}] + +// ------------------ +// OUTPUTS +// ------------------ + +output id string = apiCenterService.id +output name string = apiCenterService.name +output defaultWorkspaceName string = apiCenterWorkspace.name + +output apiEnvironmentName string = apiEnvironmentDev.name +output mcpEnvironmentName string = mcpEnvironmentDev.name + +output apiEnvironmentNameProd string = apiEnvironmentProd.name +output mcpEnvironmentNameProd string = mcpEnvironmentProd.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers-SAMPLES.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers-SAMPLES.json new file mode 100644 index 000000000..58ae23025 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers-SAMPLES.json @@ -0,0 +1,58 @@ +{ + "mcps": [ + { + "mcpName": "atlassian", + "description": "Track issues, manage sprints, create docs, and search knowledge bases", + "InstallVSCodeURL": "https://mcp.atlassian.com/v1/sse", + "DodumentationURL": "https://www.atlassian.com/platform/remote-mcp-server" + }, + { + "mcpName": "cloudflare", + "description": "Get up to date reference information on Cloudflare", + "InstallVSCodeURL": "https://docs.mcp.cloudflare.com/sse", + "DodumentationURL": "https://github.com/cloudflare/mcp-server-cloudflare" + }, + { + "mcpName": "sentry", + "description": "Search, query, and debug errors intelligently", + "InstallVSCodeURL": "https://mcp.sentry.dev/sse", + "DodumentationURL": "https://docs.sentry.io/product/sentry-mcp/" + }, + { + "mcpName": "paypal", + "description": "Create and manage invoices, and analyze sales activity", + "InstallVSCodeURL": "https://mcp.paypal.com/sse", + "DodumentationURL": "https://developer.paypal.com/tools/mcp-server/" + }, + { + "mcpName": "linear", + "description": "Search for and create issues, projects, and comments", + "InstallVSCodeURL": "https://mcp.linear.app/sse", + "DodumentationURL": "https://linear.app/changelog/2025-05-01-mcp" + }, + { + "mcpName": "plaid", + "description": "Analyze metrics, usage, and support tickets", + "InstallVSCodeURL": "https://api.dashboard.plaid.com/mcp/sse", + "DodumentationURL": "https://plaid.com/blog/plaid-mcp-ai-assistant-claude/" + }, + { + "mcpName": "asana", + "description": "Create, search, and assign tasks and deliverables", + "InstallVSCodeURL": "https://mcp.asana.com/sse", + "DodumentationURL": "https://developers.asana.com/docs/using-asanas-model-control-protocol-mcp-server" + }, + { + "mcpName": "square", + "description": "Search and manage transaction, merchant, and payment data", + "InstallVSCodeURL": "https://mcp.squareup.com/sse", + "DodumentationURL": "https://github.com/square/square-mcp-server" + }, + { + "mcpName": "intercom", + "description": "Analyze support trends and triage customer issues", + "InstallVSCodeURL": "https://mcp.intercom.com/sse", + "DodumentationURL": "https://www.intercom.com/blog/introducing-model-context-protocol-fin/" + } + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers.json new file mode 100644 index 000000000..9d5bda720 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/remote-mcp-servers.json @@ -0,0 +1,3 @@ +{ + "mcps": [] +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/servers.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/servers.json new file mode 100644 index 000000000..e1ed3bf02 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apic/servers.json @@ -0,0 +1,110 @@ +[ + { + "id": "github-mcp-server", + "name": "GitHub", + "description": "Access GitHub repositories, issues, and pull requests through secure API integration.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/github?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://api.githubcopilot.com/mcp" + } + ], + "repository": { + "url": "https://github.com/github/github-mcp-server", + "source": "partner", + "id": "" + } + }, + { + "id": "msdocs-mcp-server", + "name": "Microsoft docs", + "description": "AI assistant with real-time access to official Microsoft documentation.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/microsoft?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://learn.microsoft.com/api/mcp" + } + ] + }, + { + "id": "neon-mcp-server", + "name": "Neon", + "description": "Manage and query Neon Postgres databases with natural language.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/u/77690634?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://mcp.neon.tech/sse" + } + ], + "repository": { + "url": "https://github.com/neondatabase-labs/mcp-server-neon", + "source": "partner", + "id": "" + } + }, + { + "id": "stripe-mcp-server", + "name": "Stripe", + "description": "Payment processing and financial infrastructure tools.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/stripe?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://mcp.stripe.com/" + } + ] + }, + { + "id": "box-mcp-server", + "name": "Box", + "description": "Use Box to securely store, manage and share your photos, videos, and documents in the cloud.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/box?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://mcp.box.com/" + } + ] + }, + { + "id": "atlassian-mcp-server", + "name": "Atlassian", + "description": "Connect to Jira and Confluence for issue tracking and documentation.", + "version": "1.0.0", + "x-ms-icon": "https://avatars.githubusercontent.com/atlassian?s=64", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://mcp.atlassian.com/v1/sse" + } + ] + }, + { + "id": "postman-mcp-server", + "name": "Postman", + "description": "Postman’s remote MCP server connects AI agents, assistants, and chatbots directly to your APIs on Postman.", + "version": "1.0.0", + "x-ms-icon": "https://cdn.jsdelivr.net/gh/Azure/MCP/community/registry/icons/postman.svg", + "x-ms-partner": true, + "remotes": [ + { + "transport_type": "sse", + "url": "https://mcp.postman.com/mcp" + } + ] + } +] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/README-llm-backends.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/README-llm-backends.md new file mode 100644 index 000000000..65ab03efe --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/README-llm-backends.md @@ -0,0 +1,345 @@ +# LLM Backend Modules + +This directory contains Bicep modules for dynamic LLM backend configuration in Azure API Management (APIM). + +## Modules Overview + +### 1. llm-backends.bicep +**Purpose**: Creates individual APIM backend resources for each LLM endpoint. + +**Inputs**: +- `llmBackendConfig`: Array of backend configurations +- `managedIdentityClientId`: Client ID for authentication +- `configureCircuitBreaker`: Enable/disable circuit breaker + +**Outputs**: +- `backendIds`: Array of created backend IDs +- `backendDetails`: Detailed backend information for pool creation + +**Features**: +- Supports Azure OpenAI, AI Foundry, and external LLM providers +- Configures circuit breakers for resilience +- Sets up managed identity authentication +- Configures TLS validation + +### 2. llm-backend-pools.bicep +**Purpose**: Creates backend pools for load balancing and failover. + +**Logic**: +- Analyzes `backendDetails` to find models supported by multiple backends +- Creates pools only for multi-backend models +- Single-backend models route directly (no pool needed) + +**Outputs**: +- `poolNames`: Array of created pool names +- `modelToPoolMap`: Maps model names to pool names +- `modelToBackendMap`: Maps single-backend models to backend IDs +- `policyFragmentConfig`: Configuration for policy generation + +**Example**: +``` +Backends: [A supports gpt-4, B supports gpt-4, C supports phi-4] +Result: + - Creates "gpt-4-backend-pool" with backends A and B + - No pool for phi-4 (only supported by C) +``` + +### 3. llm-policy-fragments.bicep +**Purpose**: Generates APIM policy fragments with dynamic backend configurations. + +**Generated Fragments**: + +1. **set-backend-pools**: Contains C# code defining all available backend pools +2. **set-backend-authorization**: Configures authentication based on backend type + +**Features**: +- Injects backend pool definitions into policy fragment +- Supports multiple authentication schemes +- Handles URL rewriting for Azure OpenAI +- Works with AI Foundry OpenAI-compatible endpoints + +### 4. inference-api.bicep +**Purpose**: Creates the Universal LLM API that uses the dynamic backends. + +**Integration**: +- Consumes policy fragments from llm-policy-fragments +- Uses backends and pools from llm-backends and llm-backend-pools +- Provides unified OpenAI-compatible endpoint + +## Usage + +### Basic Example + +```bicep +// 1. Create backends +module llmBackends './llm-backends.bicep' = { + name: 'llm-backends' + params: { + apimServiceName: apimService.name + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: [ + { + backendId: 'ai-foundry-gpt4' + backendType: 'ai-foundry' + endpoint: 'https://my-project.eastus.inference.ml.azure.com' + authScheme: 'managedIdentity' + supportedModels: ['gpt-4', 'gpt-4-turbo'] + } + ] + } +} + +// 2. Create backend pools +module llmBackendPools './llm-backend-pools.bicep' = { + name: 'llm-backend-pools' + params: { + apimServiceName: apimService.name + backendDetails: llmBackends.outputs.backendDetails + } +} + +// 3. Generate policy fragments +module llmPolicyFragments './llm-policy-fragments.bicep' = { + name: 'llm-policy-fragments' + params: { + apimServiceName: apimService.name + policyFragmentConfig: llmBackendPools.outputs.policyFragmentConfig + managedIdentityClientId: managedIdentity.properties.clientId + } +} + +// 4. Create API +module universalLlmApi './inference-api.bicep' = { + name: 'universal-llm-api' + params: { + apiManagementName: apimService.name + inferenceAPIName: 'universal-llm-api' + inferenceAPIPath: 'llm' + policyXml: loadTextContent('./policies/universal-llm-api-policy-v2.xml') + } + dependsOn: [ + llmBackends + llmBackendPools + llmPolicyFragments + ] +} +``` + +## Backend Configuration Schema + +```typescript +interface BackendConfig { + backendId: string; // Unique identifier + backendType: 'ai-foundry' | 'azure-openai' | 'external'; + endpoint: string; // Base URL (e.g., https://xxx.inference.ml.azure.com) + authScheme: 'managedIdentity' | 'apiKey' | 'token'; + supportedModels: string[]; // Model names (e.g., ['gpt-4', 'gpt-4-turbo']) + priority?: number; // 1-5, default 1 (lower = higher priority) + weight?: number; // 1-1000, default 100 (higher = more traffic) +} +``` + +## Request Flow + +``` +1. Client Request → APIM Gateway + POST /llm/openai/chat/completions + Body: { "model": "gpt-4", "messages": [...] } + +2. Policy: Extract Model + → requestedModel = "gpt-4" + +3. Policy Fragment: set-backend-pools + → Loads available backend pools + → backendPools = [{ poolName: "gpt-4-backend-pool", supportedModels: ["gpt-4"] }] + +4. Policy Fragment: set-target-backend-pool + → Matches "gpt-4" to "gpt-4-backend-pool" + → targetBackendPool = "gpt-4-backend-pool" + +5. Policy Fragment: set-backend-authorization + → Gets authentication for backend type + → Sets Authorization header with managed identity token + → Rewrites URL if needed (Azure OpenAI) + +6. Backend Pool Routing + → Selects backend based on priority and weight + → Routes to healthy backend + +7. Response → Client +``` + +## Load Balancing Behavior + +### Priority-Based Routing +- Backends with **lower priority** are preferred +- Priority range: 1-5 (1 = highest priority) + +### Weight-Based Distribution +- Within the same priority, traffic is distributed by weight +- Weight range: 1-1000 +- Higher weight = more traffic + +### Example: +```json +[ + { "backendId": "primary", "priority": 1, "weight": 100 }, + { "backendId": "secondary", "priority": 1, "weight": 50 }, + { "backendId": "tertiary", "priority": 2, "weight": 100 } +] +``` + +Behavior: +- Primary receives ~67% of traffic (100 / 150) +- Secondary receives ~33% of traffic (50 / 150) +- Tertiary only receives traffic if both primary and secondary fail + +## Authentication Schemes + +### Managed Identity (Recommended) +```json +{ + "authScheme": "managedIdentity" +} +``` + +APIM uses its user-assigned managed identity to authenticate: +- Azure OpenAI: `Cognitive Services OpenAI User` role +- AI Foundry: `Cognitive Services User` role + +### API Key (Not Recommended) +```json +{ + "authScheme": "apiKey" +} +``` + +Requires configuration in backend credentials. + +## Backend Types + +### ai-foundry +- Azure AI Foundry inference endpoints +- OpenAI-compatible API paths +- Managed identity authentication +- No URL rewriting needed + +### azure-openai +- Azure OpenAI Service endpoints +- Requires URL rewriting to add `/deployments/{model}/` path +- Managed identity authentication +- Model name must match deployment name + +### external +- External LLM providers (OpenAI, Anthropic, etc.) +- Custom authentication via backend credentials +- No URL rewriting + +## Circuit Breaker Configuration + +Protects backends from cascading failures: + +```bicep +circuitBreaker: { + rules: [ + { + failureCondition: { + count: 3 // Fail after 3 errors + interval: 'PT5M' // Within 5 minutes + statusCodeRanges: [ + { min: 429, max: 429 } // Rate limiting + { min: 500, max: 503 } // Server errors + ] + } + tripDuration: 'PT1M' // Circuit open for 1 minute + acceptRetryAfter: true // Respect Retry-After header + } + ] +} +``` + +## Error Handling + +### Model Not Found +Returns `400 Bad Request` if model is not mapped to any backend: +```json +{ + "error": { + "message": "Model 'unknown-model' is not supported", + "type": "invalid_request_error", + "code": "model_not_supported" + } +} +``` + +### No Allowed Backend Pool +Returns `403 Forbidden` if RBAC prevents access: +```json +{ + "error": { + "message": "Access denied to backend pool for model 'gpt-4'", + "type": "insufficient_permissions", + "code": "forbidden_backend_pool" + } +} +``` + +## Testing + +### Verify Backend Creation +```bash +az apim backend list \ + --resource-group \ + --service-name \ + --query "[].{name:name, url:properties.url}" +``` + +### Verify Backend Pool +```bash +az apim backend show \ + --resource-group \ + --service-name \ + --backend-id gpt-4-backend-pool \ + --query "{name:name, type:properties.type, services:properties.pool.services}" +``` + +### Test Request +```bash +curl -X POST "https://.azure-api.net/llm/openai/chat/completions" \ + -H "Content-Type: application/json" \ + -H "api-key: " \ + -d '{ + "model": "gpt-4", + "messages": [{"role": "user", "content": "Hello!"}] + }' +``` + +## Troubleshooting + +### Backend Not Routing +1. Check backend exists: `az apim backend show ...` +2. Verify policy fragment: `az apim policy-fragment show ...` +3. Check API policy includes fragment: `az apim api policy show ...` + +### Authentication Failing +1. Verify managed identity has required roles +2. Check `uami-client-id` named value is set +3. Ensure backend endpoint is correct + +### Pool Not Created +Pools are only created for models supported by **multiple backends**. If only one backend supports a model, requests route directly to that backend (no pool needed). + +## Best Practices + +1. **Naming Convention**: Use descriptive backend IDs (provider-region-model) +2. **Priority Strategy**: Reserve priority 1 for production backends +3. **Weight Distribution**: Start with equal weights, adjust based on capacity +4. **Circuit Breakers**: Always enable for production deployments +5. **Monitoring**: Track backend health in APIM analytics +6. **Documentation**: Keep model-to-backend mapping documented + +## See Also + +- [Dynamic LLM Backend Configuration Guide](../../guides/dynamic-llm-backend-configuration.md) +- [APIM Backend Pools Documentation](https://learn.microsoft.com/azure/api-management/backends) +- [AI Foundry Inference](https://learn.microsoft.com/azure/ai-foundry/how-to/inference) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-model-inference/ai-model-inference-api-spec.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-model-inference/ai-model-inference-api-spec.yaml new file mode 100644 index 000000000..492640b5c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-model-inference/ai-model-inference-api-spec.yaml @@ -0,0 +1,816 @@ +openapi: 3.0.1 +info: + title: AI Model Inference API + description: Reference for Azure AI Model Inference API + license: + name: MIT + url: https://opensource.org/licenses/MIT + version: '1.0' +servers: + - url: https://REPLACE.services.ai.azure.com/models +paths: + /info: + get: + summary: Get model information + description: Returns the information about the model deployed under the endpoint. + operationId: get-info-api-version-api-version + parameters: + - name: api-version + in: query + description: The version of the API in the format "YYYY-MM-DD" or "YYYY-MM-DD-preview". + required: true + schema: + enum: + - 2024-05-01-preview + type: string + default: 2024-05-01-preview + - name: Authorization + in: header + schema: + enum: + - '' + type: '' + - name: azureml-model-deployment + in: header + schema: + enum: + - '' + type: '' + default: '' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ModelInfo' + example: + model_name: string + model_provider_name: string + model_type: audio-generation + /chat/completions: + post: + summary: Create chat completion + description: Creates a model response for the given chat conversation. + operationId: post-chat-completions-api-version-api-version + parameters: + - name: api-version + in: query + description: The version of the API in the format "YYYY-MM-DD" or "YYYY-MM-DD-preview". + required: true + schema: + enum: + - 2024-05-01-preview + type: string + default: 2024-05-01-preview + - name: x-ms-model-mesh-model-name + in: header + description: Name of the target deployment model name (like phi-4) + required: true + schema: + enum: + - '' + type: '' + default: '' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionRequest' + example: + messages: + - role: system + content: You are a helpful assistant + - role: user + content: Explain how distance between earth and the moon is calculated. + frequency_penalty: 0 + presence_penalty: 0 + max_tokens: 256 + seed: 42 + stop: <|endoftext|> + stream: false + temperature: 0 + top_p: 1 + response_format: + type: text + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionResponse' + example: + choices: + - finish_reason: stop + index: 0 + message: + content: string + role: assistant + tool_calls: + - function: + arguments: string + name: string + id: string + type: function + created: 0 + id: string + model: string + object: chat.completion + system_fingerprint: string + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedError' + example: + error: string + message: string + status: 0 + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + example: + error: string + message: string + status: 0 + '422': + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/UnprocessableContentError' + example: + code: string + detail: + loc: + - string + value: string + error: string + message: string + status: 0 + '429': + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/TooManyRequestsError' + example: + error: string + message: string + status: 0 + '500': + description: Content Filter Error + content: + application/json: + schema: + $ref: '#/components/schemas/ContentFilterError' + example: + code: string + error: string + message: string + param: string + status: 0 + /embeddings: + post: + summary: Create text embeddings + description: Creates an embedding vector representing the input text. + operationId: post-embeddings-api-version-api-version + parameters: + - name: api-version + in: query + description: The version of the API in the format "YYYY-MM-DD" or "YYYY-MM-DD-preview". + required: true + schema: + enum: + - 2024-05-01-preview + type: string + default: 2024-05-01-preview + - name: x-ms-model-mesh-model-name + in: header + description: Name of the target AI deployment name + required: true + schema: + enum: + - '' + type: '' + default: '' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEmbeddingRequest' + example: + input: + - This is a very good text + input_type: text + encoding_format: float + dimensions: 1024 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEmbeddingResponse' + example: + data: + - embedding: + - 0 + index: 0 + object: embedding + model: string + object: list + usage: + prompt_patches: 0 + prompt_tokens: 0 + total_patches: 0 + total_tokens: 0 + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedError' + example: + error: string + message: string + status: 0 + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + example: + error: string + message: string + status: 0 + '422': + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/UnprocessableContentError' + example: + code: string + detail: + loc: + - string + value: string + error: string + message: string + status: 0 + '429': + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/TooManyRequestsError' + example: + error: string + message: string + status: 0 + '500': + description: Content Filter Error + content: + application/json: + schema: + $ref: '#/components/schemas/ContentFilterError' + example: + code: string + error: string + message: string + param: string + status: 0 + /images/embeddings: + post: + summary: Create image embeddings + description: Creates an embedding vector representing the input image and text pair. + operationId: post-images-embeddings-api-version-api-version + parameters: + - name: api-version + in: query + description: The version of the API in the format "YYYY-MM-DD" or "YYYY-MM-DD-preview". + required: true + schema: + enum: + - 2024-05-01-preview + type: string + default: 2024-05-01-preview + - name: x-ms-model-mesh-model-name + in: header + description: Target AI deployment name + required: true + schema: + enum: + - '' + type: '' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateImageEmbeddingRequest' + example: + input: + - image: string + text: string + dimensions: 0 + encoding_format: base64 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEmbeddingResponse' + example: + data: + - embedding: + - 0 + index: 0 + object: embedding + model: string + object: list + usage: + prompt_patches: 0 + prompt_tokens: 0 + total_patches: 0 + total_tokens: 0 + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedError' + example: + error: string + message: string + status: 0 + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundError' + example: + error: string + message: string + status: 0 + '422': + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/UnprocessableContentError' + example: + code: string + detail: + loc: + - string + value: string + error: string + message: string + status: 0 + '429': + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/TooManyRequestsError' + example: + error: string + message: string + status: 0 + '500': + description: Content Filter Error + content: + application/json: + schema: + $ref: '#/components/schemas/ContentFilterError' + example: + code: string + error: string + message: string + param: string + status: 0 +components: + schemas: + ModelInfo: + type: object + properties: + model_name: + type: string + description: The name of the model. + model_provider_name: + type: string + description: The provider of the model. + model_type: + $ref: '#/components/schemas/ModelType' + ModelType: + enum: + - audio-generation + - chat-completions + - embeddings + - image-embeddings + - image-generation + - text-generation + type: string + CreateChatCompletionRequest: + type: object + properties: + model: + type: string + description: The model name. This parameter is ignored if the endpoint serves only one model. + messages: + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestMessage' + description: A list of messages comprising the conversation so far. + frequency_penalty: + type: number + description: Helps prevent word repetitions by reducing the chance of a word being selected if it has already been used. + max_tokens: + type: integer + description: The maximum number of tokens that can be generated in the chat completion. + presence_penalty: + type: number + description: Helps prevent the same topics from being repeated by penalizing a word if it exists in the completion already. + response_format: + $ref: '#/components/schemas/ChatCompletionResponseFormat' + seed: + type: integer + description: 'If specified, our system will make a best effort to sample deterministically.' + stop: + type: array + items: + type: string + description: Sequences where the API will stop generating further tokens. + stream: + type: boolean + description: 'If set, partial message deltas will be sent.' + temperature: + type: number + description: Non-negative number. + tool_choice: + $ref: '#/components/schemas/ChatCompletionToolChoiceOption' + tools: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTool' + description: A list of tools the model may call. + top_p: + type: number + description: 'An alternative to sampling with temperature, called nucleus sampling.' + ChatCompletionRequestMessage: + type: object + properties: + content: + type: string + description: The contents of the message. + role: + $ref: '#/components/schemas/ChatMessageRole' + tool_calls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionMessageToolCall' + ChatMessageRole: + enum: + - assistant + - system + - tool + - user + type: string + ChatCompletionResponseFormat: + type: object + properties: + type: + $ref: '#/components/schemas/ChatCompletionResponseFormatType' + ChatCompletionResponseFormatType: + enum: + - json_object + - text + type: string + ChatCompletionToolChoiceOption: + enum: + - none + - auto + - required + type: string + ChatCompletionTool: + type: object + properties: + function: + $ref: '#/components/schemas/FunctionObject' + type: + $ref: '#/components/schemas/ToolType' + FunctionObject: + type: object + properties: + description: + type: string + description: A description of what the function does. + name: + type: string + description: The name of the function to be called. + parameters: + type: object + description: 'The parameters the functions accepts, described as a JSON Schema object.' + ToolType: + enum: + - function + type: string + CreateChatCompletionResponse: + type: object + properties: + choices: + type: array + items: + $ref: '#/components/schemas/Choices' + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + id: + type: string + description: A unique identifier for the chat completion. + model: + type: string + description: The model used for the chat completion. + object: + $ref: '#/components/schemas/ChatCompletionObject' + system_fingerprint: + type: string + description: This fingerprint represents the backend configuration that the model runs with. + usage: + $ref: '#/components/schemas/CompletionUsage' + Choices: + type: object + properties: + finish_reason: + $ref: '#/components/schemas/ChatCompletionFinishReason' + index: + type: integer + description: The index of the choice in the list of choices. + message: + $ref: '#/components/schemas/ChatCompletionResponseMessage' + ChatCompletionFinishReason: + enum: + - stop + - length + - content_filter + - tool_calls + type: string + ChatCompletionResponseMessage: + type: object + properties: + content: + type: string + description: The contents of the message. + role: + $ref: '#/components/schemas/ChatMessageRole' + tool_calls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionMessageToolCall' + ChatCompletionMessageToolCall: + type: object + properties: + function: + $ref: '#/components/schemas/Function' + id: + type: string + description: The ID of the tool call. + type: + $ref: '#/components/schemas/ToolType' + Function: + type: object + properties: + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format.' + name: + type: string + description: The name of the function to call. + ChatCompletionObject: + enum: + - chat.completion + type: string + CompletionUsage: + type: object + properties: + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + CreateEmbeddingRequest: + type: object + properties: + model: + type: string + description: The model name. This parameter is ignored if the endpoint serves only one model. + input: + type: array + items: + type: string + description: 'Input text to embed, encoded as a string or array of tokens.' + dimensions: + type: integer + description: The number of dimensions the resulting output embeddings should have. + encoding_format: + $ref: '#/components/schemas/EmbeddingEncodingFormat' + input_type: + $ref: '#/components/schemas/EmbeddingInputType' + CreateEmbeddingResponse: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Embedding' + model: + type: string + description: The name of the model used to generate the embedding. + object: + $ref: '#/components/schemas/ListObject' + usage: + $ref: '#/components/schemas/Usage' + Embedding: + type: object + properties: + embedding: + type: array + items: + type: number + description: The embedding vector. + index: + type: integer + description: The index of the embedding in the list of embeddings. + object: + $ref: '#/components/schemas/EmbeddingObject' + EmbeddingEncodingFormat: + enum: + - base64 + - float + - int8 + - uint8 + - binary + - ubinary + type: string + EmbeddingInputType: + enum: + - text + - query + - document + type: string + EmbeddingObject: + enum: + - embedding + type: string + ListObject: + enum: + - list + type: string + CreateImageEmbeddingRequest: + type: object + properties: + input: + type: array + items: + $ref: '#/components/schemas/EmbeddingInput' + description: Input image to embed. + dimensions: + type: integer + description: The number of dimensions the resulting output embeddings should have. + encoding_format: + $ref: '#/components/schemas/EmbeddingEncodingFormat' + EmbeddingInput: + type: object + properties: + image: + type: string + description: The input image encoded in `base64` string as a data URL. + text: + type: string + description: Optional. The text input to feed into the model. + UnauthorizedError: + type: object + properties: + error: + type: string + description: The error description. + message: + type: string + description: The error message. + status: + type: integer + description: The HTTP status code. + NotFoundError: + type: object + properties: + error: + type: string + description: The error description. + message: + type: string + description: The error message. + status: + type: integer + description: The HTTP status code. + UnprocessableContentError: + type: object + properties: + code: + type: string + description: The error code. + detail: + $ref: '#/components/schemas/Detail' + error: + type: string + description: The error description. + message: + type: string + description: The error message. + status: + type: integer + description: The HTTP status code. + Detail: + type: object + properties: + loc: + type: array + items: + type: string + description: The parameter causing the issue. + value: + type: string + description: The value passed to the parameter causing issues. + TooManyRequestsError: + type: object + properties: + error: + type: string + description: The error description. + message: + type: string + description: The error message. + status: + type: integer + description: The HTTP status code. + ContentFilterError: + type: object + properties: + code: + type: string + description: The error code. + error: + type: string + description: The error description. + message: + type: string + description: The error message. + param: + type: string + description: The parameter that triggered the content filter. + status: + type: integer + description: The HTTP status code. + Usage: + type: object + properties: + prompt_patches: + type: integer + description: The number of image patches used by the image prompt. + prompt_tokens: + type: integer + description: The number of tokens used by the prompt. + total_patches: + type: integer + description: The total number of patches used by the request. + total_tokens: + type: integer + description: The total number of tokens used by the request. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/Azure AI Search Service API.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/Azure AI Search Service API.openapi.yaml new file mode 100644 index 000000000..fb136eb90 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/Azure AI Search Service API.openapi.yaml @@ -0,0 +1,7505 @@ +openapi: 3.0.1 +info: + title: AI Search Service API + description: 'Client that can be used to manage and query indexes and documents, as well as manage other resources, on a search service.' + version: '1.0' +servers: + - url: https://REPLACE-BY-APIM-POLICY +paths: + '/datasources(''{dataSourceName}'')': + get: + tags: + - DataSources + summary: DataSources_Get + description: Retrieves a datasource definition. + operationId: DataSources_Get + parameters: + - name: dataSourceName + in: path + description: The name of the datasource to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + put: + tags: + - DataSources + summary: DataSources_CreateOrUpdate + description: Creates a new datasource or updates a datasource if it already exists. + operationId: DataSources_CreateOrUpdate + parameters: + - name: dataSourceName + in: path + description: The name of the datasource to create or update. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + - name: Prefer + in: header + description: 'For HTTP PUT requests, instructs the service to return the created/updated resource on success.' + required: true + schema: + enum: + - return=representation + type: string + x-ms-parameter-location: method + requestBody: + description: The definition of the datasource to create or update. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + delete: + tags: + - DataSources + summary: DataSources_Delete + description: Deletes a datasource. + operationId: DataSources_Delete + parameters: + - name: dataSourceName + in: path + description: The name of the datasource to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + responses: + '204': + description: '' + '404': + description: '' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /datasources: + get: + tags: + - DataSources + summary: DataSources_List + description: Lists all datasources available for a search service. + operationId: DataSources_List + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: $select + in: query + description: 'Selects which top-level properties of the data sources to retrieve. Specified as a comma-separated list of JSON property names, or `*` for all properties. The default is all properties.' + schema: + type: string + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListDataSourcesResult' + example: + value: + - name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + post: + tags: + - DataSources + summary: DataSources_Create + description: Creates a new datasource. + operationId: DataSources_Create + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The definition of the datasource to create. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerDataSource' + example: + name: string + description: string + type: azuresql + credentials: + connectionString: string + container: + name: string + query: string + dataChangeDetectionPolicy: + '@odata.type': string + dataDeletionDetectionPolicy: + '@odata.type': string + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexers(''{indexerName}'')/search.reset': + post: + tags: + - Indexers + summary: Indexers_Reset + description: Resets the change tracking state associated with an indexer. + operationId: Indexers_Reset + parameters: + - name: indexerName + in: path + description: The name of the indexer to reset. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '204': + description: '' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexers(''{indexerName}'')/search.run': + post: + tags: + - Indexers + summary: Indexers_Run + description: Runs an indexer on-demand. + operationId: Indexers_Run + parameters: + - name: indexerName + in: path + description: The name of the indexer to run. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '202': + description: '' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexers(''{indexerName}'')': + get: + tags: + - Indexers + summary: Indexers_Get + description: Retrieves an indexer definition. + operationId: Indexers_Get + parameters: + - name: indexerName + in: path + description: The name of the indexer to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + put: + tags: + - Indexers + summary: Indexers_CreateOrUpdate + description: Creates a new indexer or updates an indexer if it already exists. + operationId: Indexers_CreateOrUpdate + parameters: + - name: indexerName + in: path + description: The name of the indexer to create or update. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + - name: Prefer + in: header + description: 'For HTTP PUT requests, instructs the service to return the created/updated resource on success.' + required: true + schema: + enum: + - return=representation + type: string + x-ms-parameter-location: method + requestBody: + description: The definition of the indexer to create or update. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + delete: + tags: + - Indexers + summary: Indexers_Delete + description: Deletes an indexer. + operationId: Indexers_Delete + parameters: + - name: indexerName + in: path + description: The name of the indexer to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + responses: + '204': + description: '' + '404': + description: '' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /indexers: + get: + tags: + - Indexers + summary: Indexers_List + description: Lists all indexers available for a search service. + operationId: Indexers_List + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: $select + in: query + description: 'Selects which top-level properties of the indexers to retrieve. Specified as a comma-separated list of JSON property names, or `*` for all properties. The default is all properties.' + schema: + type: string + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListIndexersResult' + example: + value: + - name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + post: + tags: + - Indexers + summary: Indexers_Create + description: Creates a new indexer. + operationId: Indexers_Create + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The definition of the indexer to create. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexer' + example: + name: string + description: string + dataSourceName: string + skillsetName: string + targetIndexName: string + schedule: + interval: string + startTime: string + parameters: + batchSize: 0 + maxFailedItems: 0 + maxFailedItemsPerBatch: 0 + configuration: + parsingMode: default + excludedFileNameExtensions: '' + indexedFileNameExtensions: '' + failOnUnsupportedContentType: false + failOnUnprocessableDocument: false + indexStorageMetadataOnlyForOversizedDocuments: false + delimitedTextHeaders: string + delimitedTextDelimiter: string + firstLineContainsHeaders: true + documentRoot: string + dataToExtract: contentAndMetadata + imageAction: none + allowSkillsetToReadFileData: false + pdfTextRotationAlgorithm: none + executionEnvironment: standard + queryTimeout: '2025-06-17' + fieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + outputFieldMappings: + - sourceFieldName: string + targetFieldName: string + mappingFunction: + name: string + parameters: { } + disabled: false + '@odata.etag': string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexers(''{indexerName}'')/search.status': + get: + tags: + - Indexers + summary: Indexers_GetStatus + description: Returns the current status and execution history of an indexer. + operationId: Indexers_GetStatus + parameters: + - name: indexerName + in: path + description: The name of the indexer for which to retrieve status. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerStatus' + example: + status: unknown + lastResult: + status: transientFailure + errorMessage: string + startTime: string + endTime: string + errors: + - key: string + errorMessage: string + statusCode: 0 + name: string + details: string + documentationLink: string + warnings: + - key: string + message: string + name: string + details: string + documentationLink: string + itemsProcessed: 0 + itemsFailed: 0 + initialTrackingState: string + finalTrackingState: string + executionHistory: + - status: transientFailure + errorMessage: string + startTime: string + endTime: string + errors: + - key: string + errorMessage: string + statusCode: 0 + name: string + details: string + documentationLink: string + warnings: + - key: string + message: string + name: string + details: string + documentationLink: string + itemsProcessed: 0 + itemsFailed: 0 + initialTrackingState: string + finalTrackingState: string + limits: + maxRunTime: string + maxDocumentExtractionSize: 0 + maxDocumentContentCharactersToExtract: 0 + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/skillsets(''{skillsetName}'')': + get: + tags: + - Skillsets + summary: Skillsets_Get + description: Retrieves a skillset in a search service. + operationId: Skillsets_Get + parameters: + - name: skillsetName + in: path + description: The name of the skillset to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: The skillset is successfully returned. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + put: + tags: + - Skillsets + summary: Skillsets_CreateOrUpdate + description: Creates a new skillset in a search service or updates the skillset if it already exists. + operationId: Skillsets_CreateOrUpdate + parameters: + - name: skillsetName + in: path + description: The name of the skillset to create or update. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + - name: Prefer + in: header + description: 'For HTTP PUT requests, instructs the service to return the created/updated resource on success.' + required: true + schema: + enum: + - return=representation + type: string + x-ms-parameter-location: method + requestBody: + description: The skillset containing one or more skills to create or update in a search service. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + responses: + '200': + description: The skillset is successfully updated. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + '201': + description: The skillset is successfully created. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + delete: + tags: + - Skillsets + summary: Skillsets_Delete + description: Deletes a skillset in a search service. + operationId: Skillsets_Delete + parameters: + - name: skillsetName + in: path + description: The name of the skillset to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + responses: + '204': + description: The skillset is successfully deleted. + '404': + description: The provided skillset name is not found. + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /skillsets: + get: + tags: + - Skillsets + summary: Skillsets_List + description: List all skillsets in a search service. + operationId: Skillsets_List + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: $select + in: query + description: 'Selects which top-level properties of the skillsets to retrieve. Specified as a comma-separated list of JSON property names, or `*` for all properties. The default is all properties.' + schema: + type: string + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: The list is successfully returned. + content: + application/json: + schema: + $ref: '#/components/schemas/ListSkillsetsResult' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + post: + tags: + - Skillsets + summary: Skillsets_Create + description: Creates a new skillset in a search service. + operationId: Skillsets_Create + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The skillset containing one or more skills to create in a search service. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + responses: + '201': + description: The skillset is successfully created. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndexerSkillset' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/synonymmaps(''{synonymMapName}'')': + get: + tags: + - SynonymMaps + summary: SynonymMaps_Get + description: Retrieves a synonym map definition. + operationId: SynonymMaps_Get + parameters: + - name: synonymMapName + in: path + description: The name of the synonym map to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + put: + tags: + - SynonymMaps + summary: SynonymMaps_CreateOrUpdate + description: Creates a new synonym map or updates a synonym map if it already exists. + operationId: SynonymMaps_CreateOrUpdate + parameters: + - name: synonymMapName + in: path + description: The name of the synonym map to create or update. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + - name: Prefer + in: header + description: 'For HTTP PUT requests, instructs the service to return the created/updated resource on success.' + required: true + schema: + enum: + - return=representation + type: string + x-ms-parameter-location: method + requestBody: + description: The definition of the synonym map to create or update. + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + delete: + tags: + - SynonymMaps + summary: SynonymMaps_Delete + description: Deletes a synonym map. + operationId: SynonymMaps_Delete + parameters: + - name: synonymMapName + in: path + description: The name of the synonym map to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + responses: + '204': + description: '' + '404': + description: '' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /synonymmaps: + get: + tags: + - SynonymMaps + summary: SynonymMaps_List + description: Lists all synonym maps available for a search service. + operationId: SynonymMaps_List + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: $select + in: query + description: 'Selects which top-level properties of the synonym maps to retrieve. Specified as a comma-separated list of JSON property names, or `*` for all properties. The default is all properties.' + schema: + type: string + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListSynonymMapsResult' + example: + value: + - name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + post: + tags: + - SynonymMaps + summary: SynonymMaps_Create + description: Creates a new synonym map. + operationId: SynonymMaps_Create + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The definition of the synonym map to create. + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SynonymMap' + example: + name: string + format: solr + synonyms: string + encryptionKey: + keyVaultKeyName: string + keyVaultKeyVersion: string + keyVaultUri: string + accessCredentials: + applicationId: string + applicationSecret: string + '@odata.etag': string + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /indexes: + get: + tags: + - Indexes + summary: Indexes_List + description: Lists all indexes available for a search service. + operationId: Indexes_List + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: $select + in: query + description: 'Selects which top-level properties of the index definitions to retrieve. Specified as a comma-separated list of JSON property names, or `*` for all properties. The default is all properties.' + schema: + type: string + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ListIndexesResult' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + post: + tags: + - Indexes + summary: Indexes_Create + description: Creates a new search index. + operationId: Indexes_Create + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The definition of the index to create. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + responses: + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{indexName}'')': + get: + tags: + - Indexes + summary: Indexes_Get + description: Retrieves an index definition. + operationId: Indexes_Get + parameters: + - name: indexName + in: path + description: The name of the index to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + put: + tags: + - Indexes + summary: Indexes_CreateOrUpdate + description: Creates a new search index or updates an index if it already exists. + operationId: Indexes_CreateOrUpdate + parameters: + - name: indexName + in: path + description: The definition of the index to create or update. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: allowIndexDowntime + in: query + description: 'Allows new analyzers, tokenizers, token filters, or char filters to be added to an index by taking the index offline for at least a few seconds. This temporarily causes indexing and query requests to fail. Performance and write availability of the index can be impaired for several minutes after the index is updated, or longer for very large indexes.' + schema: + type: boolean + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + - name: Prefer + in: header + description: 'For HTTP PUT requests, instructs the service to return the created/updated resource on success.' + required: true + schema: + enum: + - return=representation + type: string + x-ms-parameter-location: method + requestBody: + description: The definition of the index to create or update. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + '201': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/SearchIndex' + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + delete: + tags: + - Indexes + summary: Indexes_Delete + description: 'Deletes a search index and all the documents it contains. This operation is permanent, with no recovery option. Make sure you have a master copy of your index definition, data ingestion code, and a backup of the primary data source in case you need to re-build the index.' + operationId: Indexes_Delete + parameters: + - name: indexName + in: path + description: The name of the index to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + - name: If-Match + in: header + description: Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value. + schema: + type: string + x-ms-parameter-location: method + - name: If-None-Match + in: header + description: Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value. + schema: + type: string + x-ms-parameter-location: method + responses: + '204': + description: '' + '404': + description: '' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{indexName}'')/search.stats': + get: + tags: + - Indexes + summary: Indexes_GetStatistics + description: 'Returns statistics for the given index, including a document count and storage usage.' + operationId: Indexes_GetStatistics + parameters: + - name: indexName + in: path + description: The name of the index for which to retrieve statistics. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GetIndexStatisticsResult' + example: + documentCount: 0 + storageSize: 0 + vectorIndexSize: 0 + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{indexName}'')/search.analyze': + post: + tags: + - Indexes + summary: Indexes_Analyze + description: Shows how an analyzer breaks text into tokens. + operationId: Indexes_Analyze + parameters: + - name: indexName + in: path + description: The name of the index for which to test an analyzer. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + requestBody: + description: The text and analyzer or analysis components to test. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeRequest' + example: + text: string + analyzer: ar.microsoft + tokenizer: classic + tokenFilters: + - arabic_normalization + charFilters: + - html_strip + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeResult' + example: + tokens: + - token: string + startOffset: 0 + endOffset: 0 + position: 0 + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + /servicestats: + get: + tags: + - Service + summary: GetServiceStatistics + description: Gets service level statistics for a search service. + operationId: GetServiceStatistics + parameters: + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + example: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: The tracking ID sent with the request to help with debugging. + schema: + type: string + format: uuid + x-ms-client-request-id: true + x-ms-parameter-grouping: + name: request-options + x-ms-parameter-location: method + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ServiceStatistics' + example: + counters: + documentCount: + usage: 0 + quota: 0 + indexesCount: + usage: 0 + quota: 0 + indexersCount: + usage: 0 + quota: 0 + dataSourcesCount: + usage: 0 + quota: 0 + storageSize: + usage: 0 + quota: 0 + synonymMaps: + usage: 0 + quota: 0 + skillsetCount: + usage: 0 + quota: 0 + vectorIndexSize: + usage: 0 + quota: 0 + limits: + maxFieldsPerIndex: 0 + maxFieldNestingDepthPerIndex: 0 + maxComplexCollectionFieldsPerIndex: 0 + maxComplexObjectsInCollectionsPerDocument: 0 + '400': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' +components: + schemas: + AnalyzeRequest: + required: + - text + type: object + properties: + text: + type: string + description: The text to break into tokens. + analyzer: + $ref: '#/components/schemas/LexicalAnalyzerName' + tokenizer: + $ref: '#/components/schemas/LexicalTokenizerName' + tokenFilters: + type: array + items: + $ref: '#/components/schemas/TokenFilterName' + description: An optional list of token filters to use when breaking the given text. This parameter can only be set when using the tokenizer parameter. + charFilters: + type: array + items: + $ref: '#/components/schemas/CharFilterName' + description: An optional list of character filters to use when breaking the given text. This parameter can only be set when using the tokenizer parameter. + description: Specifies some text and analysis components used to break that text into tokens. + AnalyzeResult: + required: + - tokens + type: object + properties: + tokens: + type: array + items: + $ref: '#/components/schemas/AnalyzedTokenInfo' + description: The list of tokens returned by the analyzer specified in the request. + description: The result of testing an analyzer on text. + AnalyzedTokenInfo: + required: + - endOffset + - position + - startOffset + - token + type: object + properties: + token: + type: string + description: The token returned by the analyzer. + readOnly: true + startOffset: + type: integer + description: The index of the first character of the token in the input text. + format: int32 + readOnly: true + endOffset: + type: integer + description: The index of the last character of the token in the input text. + format: int32 + readOnly: true + position: + type: integer + description: 'The position of the token in the input text relative to other tokens. The first token in the input text has position 0, the next has position 1, and so on. Depending on the analyzer used, some tokens might have the same position, for example if they are synonyms of each other.' + format: int32 + readOnly: true + description: Information about a token returned by an analyzer. + LexicalAnalyzerName: + enum: + - ar.microsoft + - ar.lucene + - hy.lucene + - bn.microsoft + - eu.lucene + - bg.microsoft + - bg.lucene + - ca.microsoft + - ca.lucene + - zh-Hans.microsoft + - zh-Hans.lucene + - zh-Hant.microsoft + - zh-Hant.lucene + - hr.microsoft + - cs.microsoft + - cs.lucene + - da.microsoft + - da.lucene + - nl.microsoft + - nl.lucene + - en.microsoft + - en.lucene + - et.microsoft + - fi.microsoft + - fi.lucene + - fr.microsoft + - fr.lucene + - gl.lucene + - de.microsoft + - de.lucene + - el.microsoft + - el.lucene + - gu.microsoft + - he.microsoft + - hi.microsoft + - hi.lucene + - hu.microsoft + - hu.lucene + - is.microsoft + - id.microsoft + - id.lucene + - ga.lucene + - it.microsoft + - it.lucene + - ja.microsoft + - ja.lucene + - kn.microsoft + - ko.microsoft + - ko.lucene + - lv.microsoft + - lv.lucene + - lt.microsoft + - ml.microsoft + - ms.microsoft + - mr.microsoft + - nb.microsoft + - no.lucene + - fa.lucene + - pl.microsoft + - pl.lucene + - pt-BR.microsoft + - pt-BR.lucene + - pt-PT.microsoft + - pt-PT.lucene + - pa.microsoft + - ro.microsoft + - ro.lucene + - ru.microsoft + - ru.lucene + - sr-cyrillic.microsoft + - sr-latin.microsoft + - sk.microsoft + - sl.microsoft + - es.microsoft + - es.lucene + - sv.microsoft + - sv.lucene + - ta.microsoft + - te.microsoft + - th.microsoft + - th.lucene + - tr.microsoft + - tr.lucene + - uk.microsoft + - ur.microsoft + - vi.microsoft + - standard.lucene + - standardasciifolding.lucene + - keyword + - pattern + - simple + - stop + - whitespace + type: string + description: Defines the names of all text analyzers supported by the search engine. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Language-support + x-ms-enum: + name: LexicalAnalyzerName + modelAsString: true + values: + - value: ar.microsoft + name: ArMicrosoft + description: Microsoft analyzer for Arabic. + - value: ar.lucene + name: ArLucene + description: Lucene analyzer for Arabic. + - value: hy.lucene + name: HyLucene + description: Lucene analyzer for Armenian. + - value: bn.microsoft + name: BnMicrosoft + description: Microsoft analyzer for Bangla. + - value: eu.lucene + name: EuLucene + description: Lucene analyzer for Basque. + - value: bg.microsoft + name: BgMicrosoft + description: Microsoft analyzer for Bulgarian. + - value: bg.lucene + name: BgLucene + description: Lucene analyzer for Bulgarian. + - value: ca.microsoft + name: CaMicrosoft + description: Microsoft analyzer for Catalan. + - value: ca.lucene + name: CaLucene + description: Lucene analyzer for Catalan. + - value: zh-Hans.microsoft + name: ZhHansMicrosoft + description: Microsoft analyzer for Chinese (Simplified). + - value: zh-Hans.lucene + name: ZhHansLucene + description: Lucene analyzer for Chinese (Simplified). + - value: zh-Hant.microsoft + name: ZhHantMicrosoft + description: Microsoft analyzer for Chinese (Traditional). + - value: zh-Hant.lucene + name: ZhHantLucene + description: Lucene analyzer for Chinese (Traditional). + - value: hr.microsoft + name: HrMicrosoft + description: Microsoft analyzer for Croatian. + - value: cs.microsoft + name: CsMicrosoft + description: Microsoft analyzer for Czech. + - value: cs.lucene + name: CsLucene + description: Lucene analyzer for Czech. + - value: da.microsoft + name: DaMicrosoft + description: Microsoft analyzer for Danish. + - value: da.lucene + name: DaLucene + description: Lucene analyzer for Danish. + - value: nl.microsoft + name: NlMicrosoft + description: Microsoft analyzer for Dutch. + - value: nl.lucene + name: NlLucene + description: Lucene analyzer for Dutch. + - value: en.microsoft + name: EnMicrosoft + description: Microsoft analyzer for English. + - value: en.lucene + name: EnLucene + description: Lucene analyzer for English. + - value: et.microsoft + name: EtMicrosoft + description: Microsoft analyzer for Estonian. + - value: fi.microsoft + name: FiMicrosoft + description: Microsoft analyzer for Finnish. + - value: fi.lucene + name: FiLucene + description: Lucene analyzer for Finnish. + - value: fr.microsoft + name: FrMicrosoft + description: Microsoft analyzer for French. + - value: fr.lucene + name: FrLucene + description: Lucene analyzer for French. + - value: gl.lucene + name: GlLucene + description: Lucene analyzer for Galician. + - value: de.microsoft + name: DeMicrosoft + description: Microsoft analyzer for German. + - value: de.lucene + name: DeLucene + description: Lucene analyzer for German. + - value: el.microsoft + name: ElMicrosoft + description: Microsoft analyzer for Greek. + - value: el.lucene + name: ElLucene + description: Lucene analyzer for Greek. + - value: gu.microsoft + name: GuMicrosoft + description: Microsoft analyzer for Gujarati. + - value: he.microsoft + name: HeMicrosoft + description: Microsoft analyzer for Hebrew. + - value: hi.microsoft + name: HiMicrosoft + description: Microsoft analyzer for Hindi. + - value: hi.lucene + name: HiLucene + description: Lucene analyzer for Hindi. + - value: hu.microsoft + name: HuMicrosoft + description: Microsoft analyzer for Hungarian. + - value: hu.lucene + name: HuLucene + description: Lucene analyzer for Hungarian. + - value: is.microsoft + name: IsMicrosoft + description: Microsoft analyzer for Icelandic. + - value: id.microsoft + name: IdMicrosoft + description: Microsoft analyzer for Indonesian (Bahasa). + - value: id.lucene + name: IdLucene + description: Lucene analyzer for Indonesian. + - value: ga.lucene + name: GaLucene + description: Lucene analyzer for Irish. + - value: it.microsoft + name: ItMicrosoft + description: Microsoft analyzer for Italian. + - value: it.lucene + name: ItLucene + description: Lucene analyzer for Italian. + - value: ja.microsoft + name: JaMicrosoft + description: Microsoft analyzer for Japanese. + - value: ja.lucene + name: JaLucene + description: Lucene analyzer for Japanese. + - value: kn.microsoft + name: KnMicrosoft + description: Microsoft analyzer for Kannada. + - value: ko.microsoft + name: KoMicrosoft + description: Microsoft analyzer for Korean. + - value: ko.lucene + name: KoLucene + description: Lucene analyzer for Korean. + - value: lv.microsoft + name: LvMicrosoft + description: Microsoft analyzer for Latvian. + - value: lv.lucene + name: LvLucene + description: Lucene analyzer for Latvian. + - value: lt.microsoft + name: LtMicrosoft + description: Microsoft analyzer for Lithuanian. + - value: ml.microsoft + name: MlMicrosoft + description: Microsoft analyzer for Malayalam. + - value: ms.microsoft + name: MsMicrosoft + description: Microsoft analyzer for Malay (Latin). + - value: mr.microsoft + name: MrMicrosoft + description: Microsoft analyzer for Marathi. + - value: nb.microsoft + name: NbMicrosoft + description: Microsoft analyzer for Norwegian (Bokmål). + - value: no.lucene + name: NoLucene + description: Lucene analyzer for Norwegian. + - value: fa.lucene + name: FaLucene + description: Lucene analyzer for Persian. + - value: pl.microsoft + name: PlMicrosoft + description: Microsoft analyzer for Polish. + - value: pl.lucene + name: PlLucene + description: Lucene analyzer for Polish. + - value: pt-BR.microsoft + name: PtBrMicrosoft + description: Microsoft analyzer for Portuguese (Brazil). + - value: pt-BR.lucene + name: PtBrLucene + description: Lucene analyzer for Portuguese (Brazil). + - value: pt-PT.microsoft + name: PtPtMicrosoft + description: Microsoft analyzer for Portuguese (Portugal). + - value: pt-PT.lucene + name: PtPtLucene + description: Lucene analyzer for Portuguese (Portugal). + - value: pa.microsoft + name: PaMicrosoft + description: Microsoft analyzer for Punjabi. + - value: ro.microsoft + name: RoMicrosoft + description: Microsoft analyzer for Romanian. + - value: ro.lucene + name: RoLucene + description: Lucene analyzer for Romanian. + - value: ru.microsoft + name: RuMicrosoft + description: Microsoft analyzer for Russian. + - value: ru.lucene + name: RuLucene + description: Lucene analyzer for Russian. + - value: sr-cyrillic.microsoft + name: SrCyrillicMicrosoft + description: Microsoft analyzer for Serbian (Cyrillic). + - value: sr-latin.microsoft + name: SrLatinMicrosoft + description: Microsoft analyzer for Serbian (Latin). + - value: sk.microsoft + name: SkMicrosoft + description: Microsoft analyzer for Slovak. + - value: sl.microsoft + name: SlMicrosoft + description: Microsoft analyzer for Slovenian. + - value: es.microsoft + name: EsMicrosoft + description: Microsoft analyzer for Spanish. + - value: es.lucene + name: EsLucene + description: Lucene analyzer for Spanish. + - value: sv.microsoft + name: SvMicrosoft + description: Microsoft analyzer for Swedish. + - value: sv.lucene + name: SvLucene + description: Lucene analyzer for Swedish. + - value: ta.microsoft + name: TaMicrosoft + description: Microsoft analyzer for Tamil. + - value: te.microsoft + name: TeMicrosoft + description: Microsoft analyzer for Telugu. + - value: th.microsoft + name: ThMicrosoft + description: Microsoft analyzer for Thai. + - value: th.lucene + name: ThLucene + description: Lucene analyzer for Thai. + - value: tr.microsoft + name: TrMicrosoft + description: Microsoft analyzer for Turkish. + - value: tr.lucene + name: TrLucene + description: Lucene analyzer for Turkish. + - value: uk.microsoft + name: UkMicrosoft + description: Microsoft analyzer for Ukrainian. + - value: ur.microsoft + name: UrMicrosoft + description: Microsoft analyzer for Urdu. + - value: vi.microsoft + name: ViMicrosoft + description: Microsoft analyzer for Vietnamese. + - value: standard.lucene + name: StandardLucene + description: Standard Lucene analyzer. + - value: standardasciifolding.lucene + name: StandardAsciiFoldingLucene + description: Standard ASCII Folding Lucene analyzer. See https://docs.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search#Analyzers + - value: keyword + name: Keyword + description: 'Treats the entire content of a field as a single token. This is useful for data like zip codes, ids, and some product names. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordAnalyzer.html' + - value: pattern + name: Pattern + description: Flexibly separates text into terms via a regular expression pattern. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/PatternAnalyzer.html + - value: simple + name: Simple + description: Divides text at non-letters and converts them to lower case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/SimpleAnalyzer.html + - value: stop + name: Stop + description: Divides text at non-letters; Applies the lowercase and stopword token filters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopAnalyzer.html + - value: whitespace + name: Whitespace + description: An analyzer that uses the whitespace tokenizer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/WhitespaceAnalyzer.html + LexicalTokenizerName: + enum: + - classic + - edgeNGram + - keyword_v2 + - letter + - lowercase + - microsoft_language_tokenizer + - microsoft_language_stemming_tokenizer + - nGram + - path_hierarchy_v2 + - pattern + - standard_v2 + - uax_url_email + - whitespace + type: string + description: Defines the names of all tokenizers supported by the search engine. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + x-ms-enum: + name: LexicalTokenizerName + modelAsString: true + values: + - value: classic + name: Classic + description: Grammar-based tokenizer that is suitable for processing most European-language documents. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/ClassicTokenizer.html + - value: edgeNGram + name: EdgeNGram + description: Tokenizes the input from an edge into n-grams of the given size(s). See https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenizer.html + - value: keyword_v2 + name: Keyword + description: Emits the entire input as a single token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordTokenizer.html + - value: letter + name: Letter + description: Divides text at non-letters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/LetterTokenizer.html + - value: lowercase + name: Lowercase + description: Divides text at non-letters and converts them to lower case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/LowerCaseTokenizer.html + - value: microsoft_language_tokenizer + name: MicrosoftLanguageTokenizer + description: Divides text using language-specific rules. + - value: microsoft_language_stemming_tokenizer + name: MicrosoftLanguageStemmingTokenizer + description: Divides text using language-specific rules and reduces words to their base forms. + - value: nGram + name: NGram + description: Tokenizes the input into n-grams of the given size(s). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenizer.html + - value: path_hierarchy_v2 + name: PathHierarchy + description: Tokenizer for path-like hierarchies. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/path/PathHierarchyTokenizer.html + - value: pattern + name: Pattern + description: Tokenizer that uses regex pattern matching to construct distinct tokens. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternTokenizer.html + - value: standard_v2 + name: Standard + description: 'Standard Lucene analyzer; Composed of the standard tokenizer, lowercase filter and stop filter. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/StandardTokenizer.html' + - value: uax_url_email + name: UaxUrlEmail + description: Tokenizes urls and emails as one token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/UAX29URLEmailTokenizer.html + - value: whitespace + name: Whitespace + description: Divides text at whitespace. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/WhitespaceTokenizer.html + TokenFilterName: + enum: + - arabic_normalization + - apostrophe + - asciifolding + - cjk_bigram + - cjk_width + - classic + - common_grams + - edgeNGram_v2 + - elision + - german_normalization + - hindi_normalization + - indic_normalization + - keyword_repeat + - kstem + - length + - limit + - lowercase + - nGram_v2 + - persian_normalization + - phonetic + - porter_stem + - reverse + - scandinavian_normalization + - scandinavian_folding + - shingle + - snowball + - sorani_normalization + - stemmer + - stopwords + - trim + - truncate + - unique + - uppercase + - word_delimiter + type: string + description: Defines the names of all token filters supported by the search engine. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + x-ms-enum: + name: TokenFilterName + modelAsString: true + values: + - value: arabic_normalization + name: ArabicNormalization + description: A token filter that applies the Arabic normalizer to normalize the orthography. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ar/ArabicNormalizationFilter.html + - value: apostrophe + name: Apostrophe + description: Strips all characters after an apostrophe (including the apostrophe itself). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/tr/ApostropheFilter.html + - value: asciifolding + name: AsciiFolding + description: 'Converts alphabetic, numeric, and symbolic Unicode characters which are not in the first 127 ASCII characters (the "Basic Latin" Unicode block) into their ASCII equivalents, if such equivalents exist. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ASCIIFoldingFilter.html' + - value: cjk_bigram + name: CjkBigram + description: Forms bigrams of CJK terms that are generated from the standard tokenizer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/cjk/CJKBigramFilter.html + - value: cjk_width + name: CjkWidth + description: 'Normalizes CJK width differences. Folds fullwidth ASCII variants into the equivalent basic Latin, and half-width Katakana variants into the equivalent Kana. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/cjk/CJKWidthFilter.html' + - value: classic + name: Classic + description: 'Removes English possessives, and dots from acronyms. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/ClassicFilter.html' + - value: common_grams + name: CommonGram + description: 'Construct bigrams for frequently occurring terms while indexing. Single terms are still indexed too, with bigrams overlaid. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/commongrams/CommonGramsFilter.html' + - value: edgeNGram_v2 + name: EdgeNGram + description: Generates n-grams of the given size(s) starting from the front or the back of an input token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenFilter.html + - value: elision + name: Elision + description: 'Removes elisions. For example, "l''avion" (the plane) will be converted to "avion" (plane). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/util/ElisionFilter.html' + - value: german_normalization + name: GermanNormalization + description: Normalizes German characters according to the heuristics of the German2 snowball algorithm. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/de/GermanNormalizationFilter.html + - value: hindi_normalization + name: HindiNormalization + description: Normalizes text in Hindi to remove some differences in spelling variations. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/hi/HindiNormalizationFilter.html + - value: indic_normalization + name: IndicNormalization + description: Normalizes the Unicode representation of text in Indian languages. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/in/IndicNormalizationFilter.html + - value: keyword_repeat + name: KeywordRepeat + description: 'Emits each incoming token twice, once as keyword and once as non-keyword. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/KeywordRepeatFilter.html' + - value: kstem + name: KStem + description: A high-performance kstem filter for English. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/en/KStemFilter.html + - value: length + name: Length + description: Removes words that are too long or too short. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LengthFilter.html + - value: limit + name: Limit + description: Limits the number of tokens while indexing. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LimitTokenCountFilter.html + - value: lowercase + name: Lowercase + description: Normalizes token text to lower case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/LowerCaseFilter.htm + - value: nGram_v2 + name: NGram + description: Generates n-grams of the given size(s). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenFilter.html + - value: persian_normalization + name: PersianNormalization + description: Applies normalization for Persian. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/fa/PersianNormalizationFilter.html + - value: phonetic + name: Phonetic + description: Create tokens for phonetic matches. See https://lucene.apache.org/core/4_10_3/analyzers-phonetic/org/apache/lucene/analysis/phonetic/package-tree.html + - value: porter_stem + name: PorterStem + description: Uses the Porter stemming algorithm to transform the token stream. See http://tartarus.org/~martin/PorterStemmer + - value: reverse + name: Reverse + description: Reverses the token string. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/reverse/ReverseStringFilter.html + - value: scandinavian_normalization + name: ScandinavianNormalization + description: Normalizes use of the interchangeable Scandinavian characters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ScandinavianNormalizationFilter.html + - value: scandinavian_folding + name: ScandinavianFoldingNormalization + description: 'Folds Scandinavian characters åÅäæÄÆ->a and öÖøØ->o. It also discriminates against use of double vowels aa, ae, ao, oe and oo, leaving just the first one. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ScandinavianFoldingFilter.html' + - value: shingle + name: Shingle + description: Creates combinations of tokens as a single token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/shingle/ShingleFilter.html + - value: snowball + name: Snowball + description: A filter that stems words using a Snowball-generated stemmer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/snowball/SnowballFilter.html + - value: sorani_normalization + name: SoraniNormalization + description: Normalizes the Unicode representation of Sorani text. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ckb/SoraniNormalizationFilter.html + - value: stemmer + name: Stemmer + description: Language specific stemming filter. See https://docs.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search#TokenFilters + - value: stopwords + name: Stopwords + description: Removes stop words from a token stream. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopFilter.html + - value: trim + name: Trim + description: Trims leading and trailing whitespace from tokens. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/TrimFilter.html + - value: truncate + name: Truncate + description: Truncates the terms to a specific length. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/TruncateTokenFilter.html + - value: unique + name: Unique + description: Filters out tokens with same text as the previous token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/RemoveDuplicatesTokenFilter.html + - value: uppercase + name: Uppercase + description: Normalizes token text to upper case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/UpperCaseFilter.html + - value: word_delimiter + name: WordDelimiter + description: Splits words into subwords and performs optional transformations on subword groups. + CharFilterName: + enum: + - html_strip + type: string + description: Defines the names of all character filters supported by the search engine. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + x-ms-enum: + name: CharFilterName + modelAsString: true + values: + - value: html_strip + name: HtmlStrip + description: A character filter that attempts to strip out HTML constructs. See https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/charfilter/HTMLStripCharFilter.html + RegexFlags: + enum: + - CANON_EQ + - CASE_INSENSITIVE + - COMMENTS + - DOTALL + - LITERAL + - MULTILINE + - UNICODE_CASE + - UNIX_LINES + type: string + description: Defines flags that can be combined to control how regular expressions are used in the pattern analyzer and pattern tokenizer. + externalDocs: + url: http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html#field_summary + x-ms-enum: + name: RegexFlags + modelAsString: true + values: + - value: CANON_EQ + name: CanonEq + description: Enables canonical equivalence. + - value: CASE_INSENSITIVE + name: CaseInsensitive + description: Enables case-insensitive matching. + - value: COMMENTS + name: Comments + description: Permits whitespace and comments in the pattern. + - value: DOTALL + name: DotAll + description: Enables dotall mode. + - value: LITERAL + name: Literal + description: Enables literal parsing of the pattern. + - value: MULTILINE + name: Multiline + description: Enables multiline mode. + - value: UNICODE_CASE + name: UnicodeCase + description: Enables Unicode-aware case folding. + - value: UNIX_LINES + name: UnixLines + description: Enables Unix lines mode. + SearchFieldDataType: + enum: + - Edm.String + - Edm.Int32 + - Edm.Int64 + - Edm.Double + - Edm.Boolean + - Edm.DateTimeOffset + - Edm.GeographyPoint + - Edm.ComplexType + - Edm.Single + type: string + description: Defines the data type of a field in a search index. + x-ms-enum: + name: SearchFieldDataType + modelAsString: true + values: + - value: Edm.String + name: String + description: Indicates that a field contains a string. + - value: Edm.Int32 + name: Int32 + description: Indicates that a field contains a 32-bit signed integer. + - value: Edm.Int64 + name: Int64 + description: Indicates that a field contains a 64-bit signed integer. + - value: Edm.Double + name: Double + description: Indicates that a field contains an IEEE double-precision floating point number. + - value: Edm.Boolean + name: Boolean + description: Indicates that a field contains a Boolean value (true or false). + - value: Edm.DateTimeOffset + name: DateTimeOffset + description: 'Indicates that a field contains a date/time value, including timezone information.' + - value: Edm.GeographyPoint + name: GeographyPoint + description: Indicates that a field contains a geo-location in terms of longitude and latitude. + - value: Edm.ComplexType + name: Complex + description: Indicates that a field contains one or more complex objects that in turn have sub-fields of other types. + - value: Edm.Single + name: Single + description: Indicates that a field contains a single-precision floating point number. This is only valid when used with Collection(Edm.Single). + LexicalAnalyzer: + required: + - '@odata.type' + - name + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of analyzer. + name: + type: string + description: 'The name of the analyzer. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters.' + description: Base type for analyzers. + discriminator: + propertyName: '@odata.type' + CustomAnalyzer: + allOf: + - $ref: '#/components/schemas/LexicalAnalyzer' + description: 'Allows you to take control over the process of converting text into indexable/searchable tokens. It''s a user-defined configuration consisting of a single predefined tokenizer and one or more filters. The tokenizer is responsible for breaking text into tokens, and the filters for modifying tokens emitted by the tokenizer.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.CustomAnalyzer' + PatternAnalyzer: + allOf: + - $ref: '#/components/schemas/LexicalAnalyzer' + description: Flexibly separates text into terms via a regular expression pattern. This analyzer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.PatternAnalyzer' + LuceneStandardAnalyzer: + allOf: + - $ref: '#/components/schemas/LexicalAnalyzer' + description: 'Standard Apache Lucene analyzer; Composed of the standard tokenizer, lowercase filter and stop filter.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.StandardAnalyzer' + StopAnalyzer: + allOf: + - $ref: '#/components/schemas/LexicalAnalyzer' + description: Divides text at non-letters; Applies the lowercase and stopword token filters. This analyzer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StopAnalyzer' + LexicalTokenizer: + required: + - '@odata.type' + - name + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of tokenizer. + name: + type: string + description: 'The name of the tokenizer. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters.' + description: Base type for tokenizers. + discriminator: + propertyName: '@odata.type' + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + ClassicTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Grammar-based tokenizer that is suitable for processing most European-language documents. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.ClassicTokenizer' + TokenCharacterKind: + enum: + - letter + - digit + - whitespace + - punctuation + - symbol + type: string + description: Represents classes of characters on which a token filter can operate. + x-ms-enum: + name: TokenCharacterKind + modelAsString: false + values: + - value: letter + name: Letter + description: Keeps letters in tokens. + - value: digit + name: Digit + description: Keeps digits in tokens. + - value: whitespace + name: Whitespace + description: Keeps whitespace in tokens. + - value: punctuation + name: Punctuation + description: Keeps punctuation in tokens. + - value: symbol + name: Symbol + description: Keeps symbols in tokens. + EdgeNGramTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Tokenizes the input from an edge into n-grams of the given size(s). This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.EdgeNGramTokenizer' + KeywordTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Emits the entire input as a single token. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.KeywordTokenizer' + x-az-search-deprecated: true + KeywordTokenizerV2: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Emits the entire input as a single token. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.KeywordTokenizerV2' + MicrosoftTokenizerLanguage: + enum: + - bangla + - bulgarian + - catalan + - chineseSimplified + - chineseTraditional + - croatian + - czech + - danish + - dutch + - english + - french + - german + - greek + - gujarati + - hindi + - icelandic + - indonesian + - italian + - japanese + - kannada + - korean + - malay + - malayalam + - marathi + - norwegianBokmaal + - polish + - portuguese + - portugueseBrazilian + - punjabi + - romanian + - russian + - serbianCyrillic + - serbianLatin + - slovenian + - spanish + - swedish + - tamil + - telugu + - thai + - ukrainian + - urdu + - vietnamese + type: string + description: Lists the languages supported by the Microsoft language tokenizer. + x-ms-enum: + name: MicrosoftTokenizerLanguage + modelAsString: false + values: + - value: bangla + name: Bangla + description: Selects the Microsoft tokenizer for Bangla. + - value: bulgarian + name: Bulgarian + description: Selects the Microsoft tokenizer for Bulgarian. + - value: catalan + name: Catalan + description: Selects the Microsoft tokenizer for Catalan. + - value: chineseSimplified + name: ChineseSimplified + description: Selects the Microsoft tokenizer for Chinese (Simplified). + - value: chineseTraditional + name: ChineseTraditional + description: Selects the Microsoft tokenizer for Chinese (Traditional). + - value: croatian + name: Croatian + description: Selects the Microsoft tokenizer for Croatian. + - value: czech + name: Czech + description: Selects the Microsoft tokenizer for Czech. + - value: danish + name: Danish + description: Selects the Microsoft tokenizer for Danish. + - value: dutch + name: Dutch + description: Selects the Microsoft tokenizer for Dutch. + - value: english + name: English + description: Selects the Microsoft tokenizer for English. + - value: french + name: French + description: Selects the Microsoft tokenizer for French. + - value: german + name: German + description: Selects the Microsoft tokenizer for German. + - value: greek + name: Greek + description: Selects the Microsoft tokenizer for Greek. + - value: gujarati + name: Gujarati + description: Selects the Microsoft tokenizer for Gujarati. + - value: hindi + name: Hindi + description: Selects the Microsoft tokenizer for Hindi. + - value: icelandic + name: Icelandic + description: Selects the Microsoft tokenizer for Icelandic. + - value: indonesian + name: Indonesian + description: Selects the Microsoft tokenizer for Indonesian. + - value: italian + name: Italian + description: Selects the Microsoft tokenizer for Italian. + - value: japanese + name: Japanese + description: Selects the Microsoft tokenizer for Japanese. + - value: kannada + name: Kannada + description: Selects the Microsoft tokenizer for Kannada. + - value: korean + name: Korean + description: Selects the Microsoft tokenizer for Korean. + - value: malay + name: Malay + description: Selects the Microsoft tokenizer for Malay. + - value: malayalam + name: Malayalam + description: Selects the Microsoft tokenizer for Malayalam. + - value: marathi + name: Marathi + description: Selects the Microsoft tokenizer for Marathi. + - value: norwegianBokmaal + name: NorwegianBokmaal + description: Selects the Microsoft tokenizer for Norwegian (Bokmål). + - value: polish + name: Polish + description: Selects the Microsoft tokenizer for Polish. + - value: portuguese + name: Portuguese + description: Selects the Microsoft tokenizer for Portuguese. + - value: portugueseBrazilian + name: PortugueseBrazilian + description: Selects the Microsoft tokenizer for Portuguese (Brazil). + - value: punjabi + name: Punjabi + description: Selects the Microsoft tokenizer for Punjabi. + - value: romanian + name: Romanian + description: Selects the Microsoft tokenizer for Romanian. + - value: russian + name: Russian + description: Selects the Microsoft tokenizer for Russian. + - value: serbianCyrillic + name: SerbianCyrillic + description: Selects the Microsoft tokenizer for Serbian (Cyrillic). + - value: serbianLatin + name: SerbianLatin + description: Selects the Microsoft tokenizer for Serbian (Latin). + - value: slovenian + name: Slovenian + description: Selects the Microsoft tokenizer for Slovenian. + - value: spanish + name: Spanish + description: Selects the Microsoft tokenizer for Spanish. + - value: swedish + name: Swedish + description: Selects the Microsoft tokenizer for Swedish. + - value: tamil + name: Tamil + description: Selects the Microsoft tokenizer for Tamil. + - value: telugu + name: Telugu + description: Selects the Microsoft tokenizer for Telugu. + - value: thai + name: Thai + description: Selects the Microsoft tokenizer for Thai. + - value: ukrainian + name: Ukrainian + description: Selects the Microsoft tokenizer for Ukrainian. + - value: urdu + name: Urdu + description: Selects the Microsoft tokenizer for Urdu. + - value: vietnamese + name: Vietnamese + description: Selects the Microsoft tokenizer for Vietnamese. + MicrosoftLanguageTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Divides text using language-specific rules. + x-ms-discriminator-value: '#Microsoft.Azure.Search.MicrosoftLanguageTokenizer' + MicrosoftStemmingTokenizerLanguage: + enum: + - arabic + - bangla + - bulgarian + - catalan + - croatian + - czech + - danish + - dutch + - english + - estonian + - finnish + - french + - german + - greek + - gujarati + - hebrew + - hindi + - hungarian + - icelandic + - indonesian + - italian + - kannada + - latvian + - lithuanian + - malay + - malayalam + - marathi + - norwegianBokmaal + - polish + - portuguese + - portugueseBrazilian + - punjabi + - romanian + - russian + - serbianCyrillic + - serbianLatin + - slovak + - slovenian + - spanish + - swedish + - tamil + - telugu + - turkish + - ukrainian + - urdu + type: string + description: Lists the languages supported by the Microsoft language stemming tokenizer. + x-ms-enum: + name: MicrosoftStemmingTokenizerLanguage + modelAsString: false + values: + - value: arabic + name: Arabic + description: Selects the Microsoft stemming tokenizer for Arabic. + - value: bangla + name: Bangla + description: Selects the Microsoft stemming tokenizer for Bangla. + - value: bulgarian + name: Bulgarian + description: Selects the Microsoft stemming tokenizer for Bulgarian. + - value: catalan + name: Catalan + description: Selects the Microsoft stemming tokenizer for Catalan. + - value: croatian + name: Croatian + description: Selects the Microsoft stemming tokenizer for Croatian. + - value: czech + name: Czech + description: Selects the Microsoft stemming tokenizer for Czech. + - value: danish + name: Danish + description: Selects the Microsoft stemming tokenizer for Danish. + - value: dutch + name: Dutch + description: Selects the Microsoft stemming tokenizer for Dutch. + - value: english + name: English + description: Selects the Microsoft stemming tokenizer for English. + - value: estonian + name: Estonian + description: Selects the Microsoft stemming tokenizer for Estonian. + - value: finnish + name: Finnish + description: Selects the Microsoft stemming tokenizer for Finnish. + - value: french + name: French + description: Selects the Microsoft stemming tokenizer for French. + - value: german + name: German + description: Selects the Microsoft stemming tokenizer for German. + - value: greek + name: Greek + description: Selects the Microsoft stemming tokenizer for Greek. + - value: gujarati + name: Gujarati + description: Selects the Microsoft stemming tokenizer for Gujarati. + - value: hebrew + name: Hebrew + description: Selects the Microsoft stemming tokenizer for Hebrew. + - value: hindi + name: Hindi + description: Selects the Microsoft stemming tokenizer for Hindi. + - value: hungarian + name: Hungarian + description: Selects the Microsoft stemming tokenizer for Hungarian. + - value: icelandic + name: Icelandic + description: Selects the Microsoft stemming tokenizer for Icelandic. + - value: indonesian + name: Indonesian + description: Selects the Microsoft stemming tokenizer for Indonesian. + - value: italian + name: Italian + description: Selects the Microsoft stemming tokenizer for Italian. + - value: kannada + name: Kannada + description: Selects the Microsoft stemming tokenizer for Kannada. + - value: latvian + name: Latvian + description: Selects the Microsoft stemming tokenizer for Latvian. + - value: lithuanian + name: Lithuanian + description: Selects the Microsoft stemming tokenizer for Lithuanian. + - value: malay + name: Malay + description: Selects the Microsoft stemming tokenizer for Malay. + - value: malayalam + name: Malayalam + description: Selects the Microsoft stemming tokenizer for Malayalam. + - value: marathi + name: Marathi + description: Selects the Microsoft stemming tokenizer for Marathi. + - value: norwegianBokmaal + name: NorwegianBokmaal + description: Selects the Microsoft stemming tokenizer for Norwegian (Bokmål). + - value: polish + name: Polish + description: Selects the Microsoft stemming tokenizer for Polish. + - value: portuguese + name: Portuguese + description: Selects the Microsoft stemming tokenizer for Portuguese. + - value: portugueseBrazilian + name: PortugueseBrazilian + description: Selects the Microsoft stemming tokenizer for Portuguese (Brazil). + - value: punjabi + name: Punjabi + description: Selects the Microsoft stemming tokenizer for Punjabi. + - value: romanian + name: Romanian + description: Selects the Microsoft stemming tokenizer for Romanian. + - value: russian + name: Russian + description: Selects the Microsoft stemming tokenizer for Russian. + - value: serbianCyrillic + name: SerbianCyrillic + description: Selects the Microsoft stemming tokenizer for Serbian (Cyrillic). + - value: serbianLatin + name: SerbianLatin + description: Selects the Microsoft stemming tokenizer for Serbian (Latin). + - value: slovak + name: Slovak + description: Selects the Microsoft stemming tokenizer for Slovak. + - value: slovenian + name: Slovenian + description: Selects the Microsoft stemming tokenizer for Slovenian. + - value: spanish + name: Spanish + description: Selects the Microsoft stemming tokenizer for Spanish. + - value: swedish + name: Swedish + description: Selects the Microsoft stemming tokenizer for Swedish. + - value: tamil + name: Tamil + description: Selects the Microsoft stemming tokenizer for Tamil. + - value: telugu + name: Telugu + description: Selects the Microsoft stemming tokenizer for Telugu. + - value: turkish + name: Turkish + description: Selects the Microsoft stemming tokenizer for Turkish. + - value: ukrainian + name: Ukrainian + description: Selects the Microsoft stemming tokenizer for Ukrainian. + - value: urdu + name: Urdu + description: Selects the Microsoft stemming tokenizer for Urdu. + MicrosoftLanguageStemmingTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Divides text using language-specific rules and reduces words to their base forms. + x-ms-discriminator-value: '#Microsoft.Azure.Search.MicrosoftLanguageStemmingTokenizer' + NGramTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Tokenizes the input into n-grams of the given size(s). This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.NGramTokenizer' + PathHierarchyTokenizerV2: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Tokenizer for path-like hierarchies. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.PathHierarchyTokenizerV2' + PatternTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Tokenizer that uses regex pattern matching to construct distinct tokens. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.PatternTokenizer' + LuceneStandardTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Breaks text following the Unicode Text Segmentation rules. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StandardTokenizer' + x-az-search-deprecated: true + LuceneStandardTokenizerV2: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Breaks text following the Unicode Text Segmentation rules. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StandardTokenizerV2' + UaxUrlEmailTokenizer: + allOf: + - $ref: '#/components/schemas/LexicalTokenizer' + description: Tokenizes urls and emails as one token. This tokenizer is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.UaxUrlEmailTokenizer' + TokenFilter: + required: + - '@odata.type' + - name + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of token filter. + name: + type: string + description: 'The name of the token filter. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters.' + description: Base type for token filters. + discriminator: + propertyName: '@odata.type' + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + AsciiFoldingTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: 'Converts alphabetic, numeric, and symbolic Unicode characters which are not in the first 127 ASCII characters (the "Basic Latin" Unicode block) into their ASCII equivalents, if such equivalents exist. This token filter is implemented using Apache Lucene.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.AsciiFoldingTokenFilter' + CjkBigramTokenFilterScripts: + enum: + - han + - hiragana + - katakana + - hangul + type: string + description: Scripts that can be ignored by CjkBigramTokenFilter. + x-ms-enum: + name: CjkBigramTokenFilterScripts + modelAsString: false + values: + - value: han + name: Han + description: Ignore Han script when forming bigrams of CJK terms. + - value: hiragana + name: Hiragana + description: Ignore Hiragana script when forming bigrams of CJK terms. + - value: katakana + name: Katakana + description: Ignore Katakana script when forming bigrams of CJK terms. + - value: hangul + name: Hangul + description: Ignore Hangul script when forming bigrams of CJK terms. + CjkBigramTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Forms bigrams of CJK terms that are generated from the standard tokenizer. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.CjkBigramTokenFilter' + CommonGramTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: 'Construct bigrams for frequently occurring terms while indexing. Single terms are still indexed too, with bigrams overlaid. This token filter is implemented using Apache Lucene.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.CommonGramTokenFilter' + DictionaryDecompounderTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Decomposes compound words found in many Germanic languages. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.DictionaryDecompounderTokenFilter' + EdgeNGramTokenFilterSide: + enum: + - front + - back + type: string + description: Specifies which side of the input an n-gram should be generated from. + x-ms-enum: + name: EdgeNGramTokenFilterSide + modelAsString: false + values: + - value: front + name: Front + description: Specifies that the n-gram should be generated from the front of the input. + - value: back + name: Back + description: Specifies that the n-gram should be generated from the back of the input. + EdgeNGramTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Generates n-grams of the given size(s) starting from the front or the back of an input token. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.EdgeNGramTokenFilter' + x-az-search-deprecated: true + EdgeNGramTokenFilterV2: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Generates n-grams of the given size(s) starting from the front or the back of an input token. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.EdgeNGramTokenFilterV2' + ElisionTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: 'Removes elisions. For example, "l''avion" (the plane) will be converted to "avion" (plane). This token filter is implemented using Apache Lucene.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.ElisionTokenFilter' + KeepTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: A token filter that only keeps tokens with text contained in a specified list of words. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.KeepTokenFilter' + KeywordMarkerTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Marks terms as keywords. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.KeywordMarkerTokenFilter' + LengthTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Removes words that are too long or too short. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.LengthTokenFilter' + LimitTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Limits the number of tokens while indexing. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.LimitTokenFilter' + NGramTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Generates n-grams of the given size(s). This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.NGramTokenFilter' + x-az-search-deprecated: true + NGramTokenFilterV2: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Generates n-grams of the given size(s). This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.NGramTokenFilterV2' + PatternCaptureTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Uses Java regexes to emit multiple tokens - one for each capture group in one or more patterns. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.PatternCaptureTokenFilter' + PatternReplaceTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: 'A character filter that replaces characters in the input string. It uses a regular expression to identify character sequences to preserve and a replacement pattern to identify characters to replace. For example, given the input text "aa bb aa bb", pattern "(aa)\s+(bb)", and replacement "$1#$2", the result would be "aa#bb aa#bb". This token filter is implemented using Apache Lucene.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.PatternReplaceTokenFilter' + PhoneticEncoder: + enum: + - metaphone + - doubleMetaphone + - soundex + - refinedSoundex + - caverphone1 + - caverphone2 + - cologne + - nysiis + - koelnerPhonetik + - haasePhonetik + - beiderMorse + type: string + description: Identifies the type of phonetic encoder to use with a PhoneticTokenFilter. + x-ms-enum: + name: PhoneticEncoder + modelAsString: false + values: + - value: metaphone + name: Metaphone + description: Encodes a token into a Metaphone value. + - value: doubleMetaphone + name: DoubleMetaphone + description: Encodes a token into a double metaphone value. + - value: soundex + name: Soundex + description: Encodes a token into a Soundex value. + - value: refinedSoundex + name: RefinedSoundex + description: Encodes a token into a Refined Soundex value. + - value: caverphone1 + name: Caverphone1 + description: Encodes a token into a Caverphone 1.0 value. + - value: caverphone2 + name: Caverphone2 + description: Encodes a token into a Caverphone 2.0 value. + - value: cologne + name: Cologne + description: Encodes a token into a Cologne Phonetic value. + - value: nysiis + name: Nysiis + description: Encodes a token into a NYSIIS value. + - value: koelnerPhonetik + name: KoelnerPhonetik + description: Encodes a token using the Kölner Phonetik algorithm. + - value: haasePhonetik + name: HaasePhonetik + description: Encodes a token using the Haase refinement of the Kölner Phonetik algorithm. + - value: beiderMorse + name: BeiderMorse + description: Encodes a token into a Beider-Morse value. + PhoneticTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Create tokens for phonetic matches. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.PhoneticTokenFilter' + ShingleTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Creates combinations of tokens as a single token. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.ShingleTokenFilter' + SnowballTokenFilterLanguage: + enum: + - armenian + - basque + - catalan + - danish + - dutch + - english + - finnish + - french + - german + - german2 + - hungarian + - italian + - kp + - lovins + - norwegian + - porter + - portuguese + - romanian + - russian + - spanish + - swedish + - turkish + type: string + description: The language to use for a Snowball token filter. + x-ms-enum: + name: SnowballTokenFilterLanguage + modelAsString: false + values: + - value: armenian + name: Armenian + description: Selects the Lucene Snowball stemming tokenizer for Armenian. + - value: basque + name: Basque + description: Selects the Lucene Snowball stemming tokenizer for Basque. + - value: catalan + name: Catalan + description: Selects the Lucene Snowball stemming tokenizer for Catalan. + - value: danish + name: Danish + description: Selects the Lucene Snowball stemming tokenizer for Danish. + - value: dutch + name: Dutch + description: Selects the Lucene Snowball stemming tokenizer for Dutch. + - value: english + name: English + description: Selects the Lucene Snowball stemming tokenizer for English. + - value: finnish + name: Finnish + description: Selects the Lucene Snowball stemming tokenizer for Finnish. + - value: french + name: French + description: Selects the Lucene Snowball stemming tokenizer for French. + - value: german + name: German + description: Selects the Lucene Snowball stemming tokenizer for German. + - value: german2 + name: German2 + description: Selects the Lucene Snowball stemming tokenizer that uses the German variant algorithm. + - value: hungarian + name: Hungarian + description: Selects the Lucene Snowball stemming tokenizer for Hungarian. + - value: italian + name: Italian + description: Selects the Lucene Snowball stemming tokenizer for Italian. + - value: kp + name: Kp + description: Selects the Lucene Snowball stemming tokenizer for Dutch that uses the Kraaij-Pohlmann stemming algorithm. + - value: lovins + name: Lovins + description: Selects the Lucene Snowball stemming tokenizer for English that uses the Lovins stemming algorithm. + - value: norwegian + name: Norwegian + description: Selects the Lucene Snowball stemming tokenizer for Norwegian. + - value: porter + name: Porter + description: Selects the Lucene Snowball stemming tokenizer for English that uses the Porter stemming algorithm. + - value: portuguese + name: Portuguese + description: Selects the Lucene Snowball stemming tokenizer for Portuguese. + - value: romanian + name: Romanian + description: Selects the Lucene Snowball stemming tokenizer for Romanian. + - value: russian + name: Russian + description: Selects the Lucene Snowball stemming tokenizer for Russian. + - value: spanish + name: Spanish + description: Selects the Lucene Snowball stemming tokenizer for Spanish. + - value: swedish + name: Swedish + description: Selects the Lucene Snowball stemming tokenizer for Swedish. + - value: turkish + name: Turkish + description: Selects the Lucene Snowball stemming tokenizer for Turkish. + SnowballTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: A filter that stems words using a Snowball-generated stemmer. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.SnowballTokenFilter' + StemmerTokenFilterLanguage: + enum: + - arabic + - armenian + - basque + - brazilian + - bulgarian + - catalan + - czech + - danish + - dutch + - dutchKp + - english + - lightEnglish + - minimalEnglish + - possessiveEnglish + - porter2 + - lovins + - finnish + - lightFinnish + - french + - lightFrench + - minimalFrench + - galician + - minimalGalician + - german + - german2 + - lightGerman + - minimalGerman + - greek + - hindi + - hungarian + - lightHungarian + - indonesian + - irish + - italian + - lightItalian + - sorani + - latvian + - norwegian + - lightNorwegian + - minimalNorwegian + - lightNynorsk + - minimalNynorsk + - portuguese + - lightPortuguese + - minimalPortuguese + - portugueseRslp + - romanian + - russian + - lightRussian + - spanish + - lightSpanish + - swedish + - lightSwedish + - turkish + type: string + description: The language to use for a stemmer token filter. + x-ms-enum: + name: StemmerTokenFilterLanguage + modelAsString: false + values: + - value: arabic + name: Arabic + description: Selects the Lucene stemming tokenizer for Arabic. + - value: armenian + name: Armenian + description: Selects the Lucene stemming tokenizer for Armenian. + - value: basque + name: Basque + description: Selects the Lucene stemming tokenizer for Basque. + - value: brazilian + name: Brazilian + description: Selects the Lucene stemming tokenizer for Portuguese (Brazil). + - value: bulgarian + name: Bulgarian + description: Selects the Lucene stemming tokenizer for Bulgarian. + - value: catalan + name: Catalan + description: Selects the Lucene stemming tokenizer for Catalan. + - value: czech + name: Czech + description: Selects the Lucene stemming tokenizer for Czech. + - value: danish + name: Danish + description: Selects the Lucene stemming tokenizer for Danish. + - value: dutch + name: Dutch + description: Selects the Lucene stemming tokenizer for Dutch. + - value: dutchKp + name: DutchKp + description: Selects the Lucene stemming tokenizer for Dutch that uses the Kraaij-Pohlmann stemming algorithm. + - value: english + name: English + description: Selects the Lucene stemming tokenizer for English. + - value: lightEnglish + name: LightEnglish + description: Selects the Lucene stemming tokenizer for English that does light stemming. + - value: minimalEnglish + name: MinimalEnglish + description: Selects the Lucene stemming tokenizer for English that does minimal stemming. + - value: possessiveEnglish + name: PossessiveEnglish + description: Selects the Lucene stemming tokenizer for English that removes trailing possessives from words. + - value: porter2 + name: Porter2 + description: Selects the Lucene stemming tokenizer for English that uses the Porter2 stemming algorithm. + - value: lovins + name: Lovins + description: Selects the Lucene stemming tokenizer for English that uses the Lovins stemming algorithm. + - value: finnish + name: Finnish + description: Selects the Lucene stemming tokenizer for Finnish. + - value: lightFinnish + name: LightFinnish + description: Selects the Lucene stemming tokenizer for Finnish that does light stemming. + - value: french + name: French + description: Selects the Lucene stemming tokenizer for French. + - value: lightFrench + name: LightFrench + description: Selects the Lucene stemming tokenizer for French that does light stemming. + - value: minimalFrench + name: MinimalFrench + description: Selects the Lucene stemming tokenizer for French that does minimal stemming. + - value: galician + name: Galician + description: Selects the Lucene stemming tokenizer for Galician. + - value: minimalGalician + name: MinimalGalician + description: Selects the Lucene stemming tokenizer for Galician that does minimal stemming. + - value: german + name: German + description: Selects the Lucene stemming tokenizer for German. + - value: german2 + name: German2 + description: Selects the Lucene stemming tokenizer that uses the German variant algorithm. + - value: lightGerman + name: LightGerman + description: Selects the Lucene stemming tokenizer for German that does light stemming. + - value: minimalGerman + name: MinimalGerman + description: Selects the Lucene stemming tokenizer for German that does minimal stemming. + - value: greek + name: Greek + description: Selects the Lucene stemming tokenizer for Greek. + - value: hindi + name: Hindi + description: Selects the Lucene stemming tokenizer for Hindi. + - value: hungarian + name: Hungarian + description: Selects the Lucene stemming tokenizer for Hungarian. + - value: lightHungarian + name: LightHungarian + description: Selects the Lucene stemming tokenizer for Hungarian that does light stemming. + - value: indonesian + name: Indonesian + description: Selects the Lucene stemming tokenizer for Indonesian. + - value: irish + name: Irish + description: Selects the Lucene stemming tokenizer for Irish. + - value: italian + name: Italian + description: Selects the Lucene stemming tokenizer for Italian. + - value: lightItalian + name: LightItalian + description: Selects the Lucene stemming tokenizer for Italian that does light stemming. + - value: sorani + name: Sorani + description: Selects the Lucene stemming tokenizer for Sorani. + - value: latvian + name: Latvian + description: Selects the Lucene stemming tokenizer for Latvian. + - value: norwegian + name: Norwegian + description: Selects the Lucene stemming tokenizer for Norwegian (Bokmål). + - value: lightNorwegian + name: LightNorwegian + description: Selects the Lucene stemming tokenizer for Norwegian (Bokmål) that does light stemming. + - value: minimalNorwegian + name: MinimalNorwegian + description: Selects the Lucene stemming tokenizer for Norwegian (Bokmål) that does minimal stemming. + - value: lightNynorsk + name: LightNynorsk + description: Selects the Lucene stemming tokenizer for Norwegian (Nynorsk) that does light stemming. + - value: minimalNynorsk + name: MinimalNynorsk + description: Selects the Lucene stemming tokenizer for Norwegian (Nynorsk) that does minimal stemming. + - value: portuguese + name: Portuguese + description: Selects the Lucene stemming tokenizer for Portuguese. + - value: lightPortuguese + name: LightPortuguese + description: Selects the Lucene stemming tokenizer for Portuguese that does light stemming. + - value: minimalPortuguese + name: MinimalPortuguese + description: Selects the Lucene stemming tokenizer for Portuguese that does minimal stemming. + - value: portugueseRslp + name: PortugueseRslp + description: Selects the Lucene stemming tokenizer for Portuguese that uses the RSLP stemming algorithm. + - value: romanian + name: Romanian + description: Selects the Lucene stemming tokenizer for Romanian. + - value: russian + name: Russian + description: Selects the Lucene stemming tokenizer for Russian. + - value: lightRussian + name: LightRussian + description: Selects the Lucene stemming tokenizer for Russian that does light stemming. + - value: spanish + name: Spanish + description: Selects the Lucene stemming tokenizer for Spanish. + - value: lightSpanish + name: LightSpanish + description: Selects the Lucene stemming tokenizer for Spanish that does light stemming. + - value: swedish + name: Swedish + description: Selects the Lucene stemming tokenizer for Swedish. + - value: lightSwedish + name: LightSwedish + description: Selects the Lucene stemming tokenizer for Swedish that does light stemming. + - value: turkish + name: Turkish + description: Selects the Lucene stemming tokenizer for Turkish. + StemmerTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Language specific stemming filter. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StemmerTokenFilter' + StemmerOverrideTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Provides the ability to override other stemming filters with custom dictionary-based stemming. Any dictionary-stemmed terms will be marked as keywords so that they will not be stemmed with stemmers down the chain. Must be placed before any stemming filters. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StemmerOverrideTokenFilter' + StopwordsList: + enum: + - arabic + - armenian + - basque + - brazilian + - bulgarian + - catalan + - czech + - danish + - dutch + - english + - finnish + - french + - galician + - german + - greek + - hindi + - hungarian + - indonesian + - irish + - italian + - latvian + - norwegian + - persian + - portuguese + - romanian + - russian + - sorani + - spanish + - swedish + - thai + - turkish + type: string + description: Identifies a predefined list of language-specific stopwords. + x-ms-enum: + name: StopwordsList + modelAsString: false + values: + - value: arabic + name: Arabic + description: Selects the stopword list for Arabic. + - value: armenian + name: Armenian + description: Selects the stopword list for Armenian. + - value: basque + name: Basque + description: Selects the stopword list for Basque. + - value: brazilian + name: Brazilian + description: Selects the stopword list for Portuguese (Brazil). + - value: bulgarian + name: Bulgarian + description: Selects the stopword list for Bulgarian. + - value: catalan + name: Catalan + description: Selects the stopword list for Catalan. + - value: czech + name: Czech + description: Selects the stopword list for Czech. + - value: danish + name: Danish + description: Selects the stopword list for Danish. + - value: dutch + name: Dutch + description: Selects the stopword list for Dutch. + - value: english + name: English + description: Selects the stopword list for English. + - value: finnish + name: Finnish + description: Selects the stopword list for Finnish. + - value: french + name: French + description: Selects the stopword list for French. + - value: galician + name: Galician + description: Selects the stopword list for Galician. + - value: german + name: German + description: Selects the stopword list for German. + - value: greek + name: Greek + description: Selects the stopword list for Greek. + - value: hindi + name: Hindi + description: Selects the stopword list for Hindi. + - value: hungarian + name: Hungarian + description: Selects the stopword list for Hungarian. + - value: indonesian + name: Indonesian + description: Selects the stopword list for Indonesian. + - value: irish + name: Irish + description: Selects the stopword list for Irish. + - value: italian + name: Italian + description: Selects the stopword list for Italian. + - value: latvian + name: Latvian + description: Selects the stopword list for Latvian. + - value: norwegian + name: Norwegian + description: Selects the stopword list for Norwegian. + - value: persian + name: Persian + description: Selects the stopword list for Persian. + - value: portuguese + name: Portuguese + description: Selects the stopword list for Portuguese. + - value: romanian + name: Romanian + description: Selects the stopword list for Romanian. + - value: russian + name: Russian + description: Selects the stopword list for Russian. + - value: sorani + name: Sorani + description: Selects the stopword list for Sorani. + - value: spanish + name: Spanish + description: Selects the stopword list for Spanish. + - value: swedish + name: Swedish + description: Selects the stopword list for Swedish. + - value: thai + name: Thai + description: Selects the stopword list for Thai. + - value: turkish + name: Turkish + description: Selects the stopword list for Turkish. + StopwordsTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Removes stop words from a token stream. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.StopwordsTokenFilter' + SynonymTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Matches single or multi-word synonyms in a token stream. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.SynonymTokenFilter' + TruncateTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Truncates the terms to a specific length. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.TruncateTokenFilter' + UniqueTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Filters out tokens with same text as the previous token. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.UniqueTokenFilter' + WordDelimiterTokenFilter: + allOf: + - $ref: '#/components/schemas/TokenFilter' + description: Splits words into subwords and performs optional transformations on subword groups. This token filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.WordDelimiterTokenFilter' + CharFilter: + required: + - '@odata.type' + - name + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of char filter. + name: + type: string + description: 'The name of the char filter. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters.' + description: Base type for character filters. + discriminator: + propertyName: '@odata.type' + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search + MappingCharFilter: + allOf: + - $ref: '#/components/schemas/CharFilter' + description: A character filter that applies mappings defined with the mappings option. Matching is greedy (longest pattern matching at a given point wins). Replacement is allowed to be the empty string. This character filter is implemented using Apache Lucene. + x-ms-discriminator-value: '#Microsoft.Azure.Search.MappingCharFilter' + PatternReplaceCharFilter: + allOf: + - $ref: '#/components/schemas/CharFilter' + description: 'A character filter that replaces characters in the input string. It uses a regular expression to identify character sequences to preserve and a replacement pattern to identify characters to replace. For example, given the input text "aa bb aa bb", pattern "(aa)\s+(bb)", and replacement "$1#$2", the result would be "aa#bb aa#bb". This character filter is implemented using Apache Lucene.' + x-ms-discriminator-value: '#Microsoft.Azure.Search.PatternReplaceCharFilter' + Similarity: + required: + - '@odata.type' + type: object + properties: + '@odata.type': + type: string + description: 'Base type for similarity algorithms. Similarity algorithms are used to calculate scores that tie queries to documents. The higher the score, the more relevant the document is to that specific query. Those scores are used to rank the search results.' + discriminator: + propertyName: '@odata.type' + externalDocs: + url: https://learn.microsoft.com/azure/search/index-ranking-similarity + ClassicSimilarity: + allOf: + - $ref: '#/components/schemas/Similarity' + description: Legacy similarity algorithm which uses the Lucene TFIDFSimilarity implementation of TF-IDF. This variation of TF-IDF introduces static document length normalization as well as coordinating factors that penalize documents that only partially match the searched queries. + x-ms-discriminator-value: '#Microsoft.Azure.Search.ClassicSimilarity' + BM25Similarity: + allOf: + - $ref: '#/components/schemas/Similarity' + description: Ranking function based on the Okapi BM25 similarity algorithm. BM25 is a TF-IDF-like algorithm that includes length normalization (controlled by the 'b' parameter) as well as term frequency saturation (controlled by the 'k1' parameter). + x-ms-discriminator-value: '#Microsoft.Azure.Search.BM25Similarity' + VectorSearch: + type: object + properties: + profiles: + type: array + items: + $ref: '#/components/schemas/VectorSearchProfile' + description: Defines combinations of configurations to use with vector search. + algorithms: + type: array + items: + $ref: '#/components/schemas/VectorSearchAlgorithmConfiguration' + description: Contains configuration options specific to the algorithm used during indexing or querying. + description: Contains configuration options related to vector search. + VectorSearchProfile: + required: + - algorithm + - name + type: object + properties: + name: + type: string + description: The name to associate with this particular vector search profile. + algorithm: + type: string + description: The name of the vector search algorithm configuration that specifies the algorithm and optional parameters. + x-ms-client-name: algorithmConfigurationName + description: Defines a combination of configurations to use with vector search. + VectorSearchAlgorithmConfiguration: + required: + - kind + - name + type: object + properties: + name: + type: string + description: The name to associate with this particular configuration. + kind: + $ref: '#/components/schemas/VectorSearchAlgorithmKind' + description: Contains configuration options specific to the algorithm used during indexing or querying. + discriminator: + propertyName: kind + HnswVectorSearchAlgorithmConfiguration: + allOf: + - $ref: '#/components/schemas/VectorSearchAlgorithmConfiguration' + description: Contains configuration options specific to the HNSW approximate nearest neighbors algorithm used during indexing and querying. The HNSW algorithm offers a tunable trade-off between search speed and accuracy. + x-ms-client-name: HnswAlgorithmConfiguration + x-ms-discriminator-value: hnsw + HnswParameters: + type: object + properties: + m: + maximum: 10.0 + minimum: 4.0 + type: integer + description: The number of bi-directional links created for every new element during construction. Increasing this parameter value may improve recall and reduce retrieval times for datasets with high intrinsic dimensionality at the expense of increased memory consumption and longer indexing time. + format: int32 + default: 4 + nullable: true + efConstruction: + maximum: 1000.0 + minimum: 100.0 + type: integer + description: 'The size of the dynamic list containing the nearest neighbors, which is used during index time. Increasing this parameter may improve index quality, at the expense of increased indexing time. At a certain point, increasing this parameter leads to diminishing returns.' + format: int32 + default: 400 + nullable: true + efSearch: + maximum: 1000.0 + minimum: 100.0 + type: integer + description: 'The size of the dynamic list containing the nearest neighbors, which is used during search time. Increasing this parameter may improve search results, at the expense of slower search. At a certain point, increasing this parameter leads to diminishing returns.' + format: int32 + default: 500 + nullable: true + metric: + $ref: '#/components/schemas/VectorSearchAlgorithmMetric' + description: Contains the parameters specific to the HNSW algorithm. + ExhaustiveKnnVectorSearchAlgorithmConfiguration: + allOf: + - $ref: '#/components/schemas/VectorSearchAlgorithmConfiguration' + description: 'Contains configuration options specific to the exhaustive KNN algorithm used during querying, which will perform brute-force search across the entire vector index.' + x-ms-client-name: ExhaustiveKnnAlgorithmConfiguration + x-ms-discriminator-value: exhaustiveKnn + ExhaustiveKnnParameters: + type: object + properties: + metric: + $ref: '#/components/schemas/VectorSearchAlgorithmMetric' + description: Contains the parameters specific to exhaustive KNN algorithm. + VectorSearchAlgorithmMetric: + enum: + - cosine + - euclidean + - dotProduct + type: string + description: The similarity metric to use for vector comparisons. + x-ms-enum: + name: VectorSearchAlgorithmMetric + modelAsString: true + values: + - value: cosine + name: Cosine + - value: euclidean + name: Euclidean + - value: dotProduct + name: DotProduct + VectorSearchAlgorithmKind: + enum: + - hnsw + - exhaustiveKnn + type: string + description: The algorithm used for indexing and querying. + x-ms-enum: + name: VectorSearchAlgorithmKind + modelAsString: true + values: + - value: hnsw + name: Hnsw + description: 'HNSW (Hierarchical Navigable Small World), a type of approximate nearest neighbors algorithm.' + - value: exhaustiveKnn + name: ExhaustiveKnn + description: Exhaustive KNN algorithm which will perform brute-force search. + DataSourceCredentials: + type: object + properties: + connectionString: + type: string + description: The connection string for the datasource. Set to `` (with brackets) if you don't want the connection string updated. Set to `` if you want to remove the connection string value from the datasource. + description: Represents credentials that can be used to connect to a datasource. + SearchIndexerDataContainer: + required: + - name + type: object + properties: + name: + type: string + description: The name of the table or view (for Azure SQL data source) or collection (for CosmosDB data source) that will be indexed. + query: + type: string + description: A query that is applied to this data container. The syntax and meaning of this parameter is datasource-specific. Not supported by Azure SQL datasources. + description: Represents information about the entity (such as Azure SQL table or CosmosDB collection) that will be indexed. + DataChangeDetectionPolicy: + required: + - '@odata.type' + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of data change detection policy. + description: Base type for data change detection policies. + discriminator: + propertyName: '@odata.type' + HighWaterMarkChangeDetectionPolicy: + allOf: + - $ref: '#/components/schemas/DataChangeDetectionPolicy' + description: Defines a data change detection policy that captures changes based on the value of a high water mark column. + x-ms-discriminator-value: '#Microsoft.Azure.Search.HighWaterMarkChangeDetectionPolicy' + SqlIntegratedChangeTrackingPolicy: + allOf: + - $ref: '#/components/schemas/DataChangeDetectionPolicy' + description: Defines a data change detection policy that captures changes using the Integrated Change Tracking feature of Azure SQL Database. + x-ms-discriminator-value: '#Microsoft.Azure.Search.SqlIntegratedChangeTrackingPolicy' + DataDeletionDetectionPolicy: + required: + - '@odata.type' + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of data deletion detection policy. + description: Base type for data deletion detection policies. + discriminator: + propertyName: '@odata.type' + SoftDeleteColumnDeletionDetectionPolicy: + allOf: + - $ref: '#/components/schemas/DataDeletionDetectionPolicy' + description: Defines a data deletion detection policy that implements a soft-deletion strategy. It determines whether an item should be deleted based on the value of a designated 'soft delete' column. + x-ms-discriminator-value: '#Microsoft.Azure.Search.SoftDeleteColumnDeletionDetectionPolicy' + SearchIndexerDataSourceType: + enum: + - azuresql + - cosmosdb + - azureblob + - azuretable + - mysql + - adlsgen2 + type: string + description: Defines the type of a datasource. + x-ms-enum: + name: SearchIndexerDataSourceType + modelAsString: true + values: + - value: azuresql + name: AzureSql + description: Indicates an Azure SQL datasource. + - value: cosmosdb + name: CosmosDb + description: Indicates a CosmosDB datasource. + - value: azureblob + name: AzureBlob + description: Indicates an Azure Blob datasource. + - value: azuretable + name: AzureTable + description: Indicates an Azure Table datasource. + - value: mysql + name: MySql + description: Indicates a MySql datasource. + - value: adlsgen2 + name: AdlsGen2 + description: Indicates an ADLS Gen2 datasource. + SearchIndexerDataSource: + required: + - container + - credentials + - name + - type + type: object + properties: + name: + type: string + description: The name of the datasource. + description: + type: string + description: The description of the datasource. + type: + $ref: '#/components/schemas/SearchIndexerDataSourceType' + credentials: + $ref: '#/components/schemas/DataSourceCredentials' + container: + $ref: '#/components/schemas/SearchIndexerDataContainer' + dataChangeDetectionPolicy: + $ref: '#/components/schemas/DataChangeDetectionPolicy' + dataDeletionDetectionPolicy: + $ref: '#/components/schemas/DataDeletionDetectionPolicy' + '@odata.etag': + type: string + description: The ETag of the data source. + x-ms-client-name: ETag + encryptionKey: + $ref: '#/components/schemas/SearchResourceEncryptionKey' + description: 'Represents a datasource definition, which can be used to configure an indexer.' + ListDataSourcesResult: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/SearchIndexerDataSource' + description: The datasources in the Search service. + readOnly: true + x-ms-client-name: DataSources + description: 'Response from a List Datasources request. If successful, it includes the full definitions of all datasources.' + IndexingSchedule: + required: + - interval + type: object + properties: + interval: + type: string + description: The interval of time between indexer executions. + format: duration + startTime: + type: string + description: The time when an indexer should start running. + format: date-time + description: Represents a schedule for indexer execution. + IndexingParameters: + type: object + properties: + batchSize: + type: integer + description: The number of items that are read from the data source and indexed as a single batch in order to improve performance. The default depends on the data source type. + format: int32 + nullable: true + maxFailedItems: + type: integer + description: The maximum number of items that can fail indexing for indexer execution to still be considered successful. -1 means no limit. Default is 0. + format: int32 + default: 0 + nullable: true + maxFailedItemsPerBatch: + type: integer + description: The maximum number of items in a single batch that can fail indexing for the batch to still be considered successful. -1 means no limit. Default is 0. + format: int32 + default: 0 + nullable: true + configuration: + $ref: '#/components/schemas/IndexingParametersConfiguration' + description: Represents parameters for indexer execution. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/create-indexer#parameters + IndexingParametersConfiguration: + type: object + properties: + parsingMode: + $ref: '#/components/schemas/ParsingMode' + excludedFileNameExtensions: + type: string + description: 'Comma-delimited list of filename extensions to ignore when processing from Azure blob storage. For example, you could exclude ".png, .mp4" to skip over those files during indexing.' + default: '' + indexedFileNameExtensions: + type: string + description: 'Comma-delimited list of filename extensions to select when processing from Azure blob storage. For example, you could focus indexing on specific application files ".docx, .pptx, .msg" to specifically include those file types.' + default: '' + failOnUnsupportedContentType: + type: boolean + description: 'For Azure blobs, set to false if you want to continue indexing when an unsupported content type is encountered, and you don''t know all the content types (file extensions) in advance.' + default: false + failOnUnprocessableDocument: + type: boolean + description: 'For Azure blobs, set to false if you want to continue indexing if a document fails indexing.' + default: false + indexStorageMetadataOnlyForOversizedDocuments: + type: boolean + description: 'For Azure blobs, set this property to true to still index storage metadata for blob content that is too large to process. Oversized blobs are treated as errors by default. For limits on blob size, see https://docs.microsoft.com/azure/search/search-limits-quotas-capacity.' + default: false + delimitedTextHeaders: + type: string + description: 'For CSV blobs, specifies a comma-delimited list of column headers, useful for mapping source fields to destination fields in an index.' + delimitedTextDelimiter: + type: string + description: 'For CSV blobs, specifies the end-of-line single-character delimiter for CSV files where each line starts a new document (for example, "|").' + firstLineContainsHeaders: + type: boolean + description: 'For CSV blobs, indicates that the first (non-blank) line of each blob contains headers.' + default: true + documentRoot: + type: string + description: 'For JSON arrays, given a structured or semi-structured document, you can specify a path to the array using this property.' + dataToExtract: + $ref: '#/components/schemas/DataToExtract' + imageAction: + $ref: '#/components/schemas/ImageAction' + allowSkillsetToReadFileData: + type: boolean + description: 'If true, will create a path //document//file_data that is an object representing the original file data downloaded from your blob data source. This allows you to pass the original file data to a custom skill for processing within the enrichment pipeline, or to the Document Extraction skill.' + default: false + pdfTextRotationAlgorithm: + $ref: '#/components/schemas/PdfTextRotationAlgorithm' + executionEnvironment: + $ref: '#/components/schemas/ExecutionEnvironment' + queryTimeout: + type: string + description: 'Increases the timeout beyond the 5-minute default for Azure SQL database data sources, specified in the format "hh:mm:ss".' + default: '00:05:00' + description: A dictionary of indexer-specific configuration properties. Each name is the name of a specific property. Each value must be of a primitive type. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/create-indexer#parameters + ParsingMode: + enum: + - default + - text + - delimitedText + - json + - jsonArray + - jsonLines + type: string + description: Represents the parsing mode for indexing from an Azure blob data source. + default: default + x-ms-enum: + name: BlobIndexerParsingMode + modelAsString: true + values: + - value: default + name: Default + description: Set to default for normal file processing. + - value: text + name: Text + description: Set to text to improve indexing performance on plain text files in blob storage. + - value: delimitedText + name: DelimitedText + description: Set to delimitedText when blobs are plain CSV files. + - value: json + name: Json + description: Set to json to extract structured content from JSON files. + - value: jsonArray + name: JsonArray + description: Set to jsonArray to extract individual elements of a JSON array as separate documents. + - value: jsonLines + name: JsonLines + description: 'Set to jsonLines to extract individual JSON entities, separated by a new line, as separate documents.' + DataToExtract: + enum: + - storageMetadata + - allMetadata + - contentAndMetadata + type: string + description: 'Specifies the data to extract from Azure blob storage and tells the indexer which data to extract from image content when "imageAction" is set to a value other than "none". This applies to embedded image content in a .PDF or other application, or image files such as .jpg and .png, in Azure blobs.' + default: contentAndMetadata + x-ms-enum: + name: BlobIndexerDataToExtract + modelAsString: true + values: + - value: storageMetadata + name: StorageMetadata + description: Indexes just the standard blob properties and user-specified metadata. + - value: allMetadata + name: AllMetadata + description: 'Extracts metadata provided by the Azure blob storage subsystem and the content-type specific metadata (for example, metadata unique to just .png files are indexed).' + - value: contentAndMetadata + name: ContentAndMetadata + description: Extracts all metadata and textual content from each blob. + ImageAction: + enum: + - none + - generateNormalizedImages + - generateNormalizedImagePerPage + type: string + description: Determines how to process embedded images and image files in Azure blob storage. Setting the "imageAction" configuration to any value other than "none" requires that a skillset also be attached to that indexer. + default: none + x-ms-enum: + name: BlobIndexerImageAction + modelAsString: true + values: + - value: none + name: None + description: Ignores embedded images or image files in the data set. This is the default. + - value: generateNormalizedImages + name: GenerateNormalizedImages + description: 'Extracts text from images (for example, the word "STOP" from a traffic stop sign), and embeds it into the content field. This action requires that "dataToExtract" is set to "contentAndMetadata". A normalized image refers to additional processing resulting in uniform image output, sized and rotated to promote consistent rendering when you include images in visual search results. This information is generated for each image when you use this option.' + - value: generateNormalizedImagePerPage + name: GenerateNormalizedImagePerPage + description: 'Extracts text from images (for example, the word "STOP" from a traffic stop sign), and embeds it into the content field, but treats PDF files differently in that each page will be rendered as an image and normalized accordingly, instead of extracting embedded images. Non-PDF file types will be treated the same as if "generateNormalizedImages" was set.' + PdfTextRotationAlgorithm: + enum: + - none + - detectAngles + type: string + description: Determines algorithm for text extraction from PDF files in Azure blob storage. + default: none + x-ms-enum: + name: BlobIndexerPDFTextRotationAlgorithm + modelAsString: true + values: + - value: none + name: None + description: Leverages normal text extraction. This is the default. + - value: detectAngles + name: DetectAngles + description: 'May produce better and more readable text extraction from PDF files that have rotated text within them. Note that there may be a small performance speed impact when this parameter is used. This parameter only applies to PDF files, and only to PDFs with embedded text. If the rotated text appears within an embedded image in the PDF, this parameter does not apply.' + ExecutionEnvironment: + enum: + - standard + - private + type: string + description: Specifies the environment in which the indexer should execute. + default: standard + x-ms-enum: + name: IndexerExecutionEnvironment + modelAsString: true + values: + - value: standard + name: standard + description: Indicates that the search service can determine where the indexer should execute. This is the default environment when nothing is specified and is the recommended value. + - value: private + name: private + description: Indicates that the indexer should run with the environment provisioned specifically for the search service. This should only be specified as the execution environment if the indexer needs to access resources securely over shared private link resources. + FieldMappingFunction: + required: + - name + type: object + properties: + name: + type: string + description: The name of the field mapping function. + parameters: + type: object + description: A dictionary of parameter name/value pairs to pass to the function. Each value must be of a primitive type. + description: Represents a function that transforms a value from a data source before indexing. + externalDocs: + url: https://learn.microsoft.com/azure/search/search-indexer-field-mappings + FieldMapping: + required: + - sourceFieldName + type: object + properties: + sourceFieldName: + type: string + description: The name of the field in the data source. + targetFieldName: + type: string + description: The name of the target field in the index. Same as the source field name by default. + mappingFunction: + $ref: '#/components/schemas/FieldMappingFunction' + description: Defines a mapping between a field in a data source and a target field in an index. + externalDocs: + url: https://learn.microsoft.com/azure/search/search-indexer-field-mappings + SearchIndexer: + required: + - dataSourceName + - name + - targetIndexName + type: object + properties: + name: + type: string + description: The name of the indexer. + description: + type: string + description: The description of the indexer. + dataSourceName: + type: string + description: The name of the datasource from which this indexer reads data. + skillsetName: + type: string + description: The name of the skillset executing with this indexer. + targetIndexName: + type: string + description: The name of the index to which this indexer writes data. + schedule: + $ref: '#/components/schemas/IndexingSchedule' + parameters: + $ref: '#/components/schemas/IndexingParameters' + fieldMappings: + type: array + items: + $ref: '#/components/schemas/FieldMapping' + description: Defines mappings between fields in the data source and corresponding target fields in the index. + outputFieldMappings: + type: array + items: + $ref: '#/components/schemas/FieldMapping' + description: Output field mappings are applied after enrichment and immediately before indexing. + disabled: + type: boolean + description: A value indicating whether the indexer is disabled. Default is false. + default: false + nullable: true + x-ms-client-name: IsDisabled + '@odata.etag': + type: string + description: The ETag of the indexer. + x-ms-client-name: ETag + encryptionKey: + $ref: '#/components/schemas/SearchResourceEncryptionKey' + description: Represents an indexer. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Indexer-operations + ListIndexersResult: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/SearchIndexer' + description: The indexers in the Search service. + readOnly: true + x-ms-client-name: Indexers + description: 'Response from a List Indexers request. If successful, it includes the full definitions of all indexers.' + SearchIndexerError: + required: + - errorMessage + - statusCode + type: object + properties: + key: + type: string + description: The key of the item for which indexing failed. + readOnly: true + errorMessage: + type: string + description: The message describing the error that occurred while processing the item. + readOnly: true + statusCode: + type: integer + description: 'The status code indicating why the indexing operation failed. Possible values include: 400 for a malformed input document, 404 for document not found, 409 for a version conflict, 422 when the index is temporarily unavailable, or 503 for when the service is too busy.' + format: int32 + readOnly: true + name: + type: string + description: 'The name of the source at which the error originated. For example, this could refer to a particular skill in the attached skillset. This may not be always available.' + readOnly: true + details: + type: string + description: 'Additional, verbose details about the error to assist in debugging the indexer. This may not be always available.' + readOnly: true + documentationLink: + type: string + description: A link to a troubleshooting guide for these classes of errors. This may not be always available. + readOnly: true + description: Represents an item- or document-level indexing error. + SearchIndexerWarning: + required: + - message + type: object + properties: + key: + type: string + description: The key of the item which generated a warning. + readOnly: true + message: + type: string + description: The message describing the warning that occurred while processing the item. + readOnly: true + name: + type: string + description: 'The name of the source at which the warning originated. For example, this could refer to a particular skill in the attached skillset. This may not be always available.' + readOnly: true + details: + type: string + description: 'Additional, verbose details about the warning to assist in debugging the indexer. This may not be always available.' + readOnly: true + documentationLink: + type: string + description: A link to a troubleshooting guide for these classes of warnings. This may not be always available. + readOnly: true + description: Represents an item-level warning. + IndexerExecutionResult: + required: + - errors + - itemsFailed + - itemsProcessed + - status + - warnings + type: object + properties: + status: + $ref: '#/components/schemas/IndexerExecutionStatus' + errorMessage: + type: string + description: 'The error message indicating the top-level error, if any.' + readOnly: true + startTime: + type: string + description: The start time of this indexer execution. + format: date-time + readOnly: true + endTime: + type: string + description: 'The end time of this indexer execution, if the execution has already completed.' + format: date-time + nullable: true + readOnly: true + errors: + type: array + items: + $ref: '#/components/schemas/SearchIndexerError' + description: The item-level indexing errors. + readOnly: true + warnings: + type: array + items: + $ref: '#/components/schemas/SearchIndexerWarning' + description: The item-level indexing warnings. + readOnly: true + itemsProcessed: + type: integer + description: The number of items that were processed during this indexer execution. This includes both successfully processed items and items where indexing was attempted but failed. + format: int32 + readOnly: true + x-ms-client-name: ItemCount + itemsFailed: + type: integer + description: The number of items that failed to be indexed during this indexer execution. + format: int32 + readOnly: true + x-ms-client-name: FailedItemCount + initialTrackingState: + type: string + description: Change tracking state with which an indexer execution started. + readOnly: true + finalTrackingState: + type: string + description: Change tracking state with which an indexer execution finished. + readOnly: true + description: Represents the result of an individual indexer execution. + IndexerExecutionStatus: + enum: + - transientFailure + - success + - inProgress + - reset + type: string + description: Represents the status of an individual indexer execution. + x-ms-enum: + name: IndexerExecutionStatus + modelAsString: false + values: + - value: transientFailure + name: TransientFailure + description: 'An indexer invocation has failed, but the failure may be transient. Indexer invocations will continue per schedule.' + - value: success + name: Success + description: Indexer execution completed successfully. + - value: inProgress + name: InProgress + description: Indexer execution is in progress. + - value: reset + name: Reset + description: Indexer has been reset. + x-nullable: false + SearchIndexerStatus: + required: + - executionHistory + - limits + - status + type: object + properties: + status: + $ref: '#/components/schemas/IndexerStatus' + lastResult: + $ref: '#/components/schemas/IndexerExecutionResult' + executionHistory: + type: array + items: + $ref: '#/components/schemas/IndexerExecutionResult' + description: 'History of the recent indexer executions, sorted in reverse chronological order.' + readOnly: true + limits: + $ref: '#/components/schemas/SearchIndexerLimits' + description: Represents the current status and execution history of an indexer. + IndexerStatus: + enum: + - unknown + - error + - running + type: string + description: Represents the overall indexer status. + x-ms-enum: + name: IndexerStatus + modelAsString: false + values: + - value: unknown + name: Unknown + description: Indicates that the indexer is in an unknown state. + - value: error + name: Error + description: Indicates that the indexer experienced an error that cannot be corrected without human intervention. + - value: running + name: Running + description: Indicates that the indexer is running normally. + x-nullable: false + SearchIndexerLimits: + type: object + properties: + maxRunTime: + type: string + description: The maximum duration that the indexer is permitted to run for one execution. + format: duration + readOnly: true + maxDocumentExtractionSize: + type: number + description: 'The maximum size of a document, in bytes, which will be considered valid for indexing.' + format: int64 + readOnly: true + maxDocumentContentCharactersToExtract: + type: number + description: The maximum number of characters that will be extracted from a document picked up for indexing. + format: int64 + readOnly: true + SearchField: + required: + - name + - type + type: object + properties: + name: + type: string + description: 'The name of the field, which must be unique within the fields collection of the index or parent field.' + type: + $ref: '#/components/schemas/SearchFieldDataType' + key: + type: boolean + description: A value indicating whether the field uniquely identifies documents in the index. Exactly one top-level field in each index must be chosen as the key field and it must be of type Edm.String. Key fields can be used to look up documents directly and update or delete specific documents. Default is false for simple fields and null for complex fields. + retrievable: + type: boolean + description: 'A value indicating whether the field can be returned in a search result. You can disable this option if you want to use a field (for example, margin) as a filter, sorting, or scoring mechanism but do not want the field to be visible to the end user. This property must be true for key fields, and it must be null for complex fields. This property can be changed on existing fields. Enabling this property does not cause any increase in index storage requirements. Default is true for simple fields and null for complex fields.' + searchable: + type: boolean + description: 'A value indicating whether the field is full-text searchable. This means it will undergo analysis such as word-breaking during indexing. If you set a searchable field to a value like "sunny day", internally it will be split into the individual tokens "sunny" and "day". This enables full-text searches for these terms. Fields of type Edm.String or Collection(Edm.String) are searchable by default. This property must be false for simple fields of other non-string data types, and it must be null for complex fields. Note: searchable fields consume extra space in your index to accommodate additional tokenized versions of the field value for full-text searches. If you want to save space in your index and you don''t need a field to be included in searches, set searchable to false.' + filterable: + type: boolean + description: 'A value indicating whether to enable the field to be referenced in $filter queries. filterable differs from searchable in how strings are handled. Fields of type Edm.String or Collection(Edm.String) that are filterable do not undergo word-breaking, so comparisons are for exact matches only. For example, if you set such a field f to "sunny day", $filter=f eq ''sunny'' will find no matches, but $filter=f eq ''sunny day'' will. This property must be null for complex fields. Default is true for simple fields and null for complex fields.' + sortable: + type: boolean + description: 'A value indicating whether to enable the field to be referenced in $orderby expressions. By default, the search engine sorts results by score, but in many experiences users will want to sort by fields in the documents. A simple field can be sortable only if it is single-valued (it has a single value in the scope of the parent document). Simple collection fields cannot be sortable, since they are multi-valued. Simple sub-fields of complex collections are also multi-valued, and therefore cannot be sortable. This is true whether it''s an immediate parent field, or an ancestor field, that''s the complex collection. Complex fields cannot be sortable and the sortable property must be null for such fields. The default for sortable is true for single-valued simple fields, false for multi-valued simple fields, and null for complex fields.' + facetable: + type: boolean + description: 'A value indicating whether to enable the field to be referenced in facet queries. Typically used in a presentation of search results that includes hit count by category (for example, search for digital cameras and see hits by brand, by megapixels, by price, and so on). This property must be null for complex fields. Fields of type Edm.GeographyPoint or Collection(Edm.GeographyPoint) cannot be facetable. Default is true for all other simple fields.' + analyzer: + $ref: '#/components/schemas/LexicalAnalyzerName' + searchAnalyzer: + $ref: '#/components/schemas/LexicalAnalyzerName' + indexAnalyzer: + $ref: '#/components/schemas/LexicalAnalyzerName' + dimensions: + maximum: 2048.0 + minimum: 2.0 + type: integer + description: The dimensionality of the vector field. + format: int32 + nullable: true + x-ms-client-name: vectorSearchDimensions + vectorSearchProfile: + type: string + description: The name of the vector search profile that specifies the algorithm to use when searching the vector field. + nullable: true + x-ms-client-name: vectorSearchProfileName + synonymMaps: + type: array + items: + type: string + description: A list of the names of synonym maps to associate with this field. This option can be used only with searchable fields. Currently only one synonym map per field is supported. Assigning a synonym map to a field ensures that query terms targeting that field are expanded at query-time using the rules in the synonym map. This attribute can be changed on existing fields. Must be null or an empty collection for complex fields. + fields: + type: array + items: + $ref: '#/components/schemas/SearchField' + description: A list of sub-fields if this is a field of type Edm.ComplexType or Collection(Edm.ComplexType). Must be null or empty for simple fields. + description: 'Represents a field in an index definition, which describes the name, data type, and search behavior of a field.' + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Create-Index + TextWeights: + required: + - weights + type: object + properties: + weights: + type: object + additionalProperties: + type: number + format: double + description: The dictionary of per-field weights to boost document scoring. The keys are field names and the values are the weights for each field. + description: Defines weights on index fields for which matches should boost scoring in search queries. + ScoringFunction: + required: + - boost + - fieldName + - type + type: object + properties: + type: + type: string + description: 'Indicates the type of function to use. Valid values include magnitude, freshness, distance, and tag. The function type must be lower case.' + fieldName: + type: string + description: The name of the field used as input to the scoring function. + boost: + type: number + description: A multiplier for the raw score. Must be a positive number not equal to 1.0. + format: double + interpolation: + $ref: '#/components/schemas/ScoringFunctionInterpolation' + description: Base type for functions that can modify document scores during ranking. + discriminator: + propertyName: type + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index + DistanceScoringFunction: + allOf: + - $ref: '#/components/schemas/ScoringFunction' + description: Defines a function that boosts scores based on distance from a geographic location. + x-ms-discriminator-value: distance + DistanceScoringParameters: + required: + - boostingDistance + - referencePointParameter + type: object + properties: + referencePointParameter: + type: string + description: The name of the parameter passed in search queries to specify the reference location. + boostingDistance: + type: number + description: The distance in kilometers from the reference location where the boosting range ends. + format: double + description: Provides parameter values to a distance scoring function. + FreshnessScoringFunction: + allOf: + - $ref: '#/components/schemas/ScoringFunction' + description: Defines a function that boosts scores based on the value of a date-time field. + x-ms-discriminator-value: freshness + FreshnessScoringParameters: + required: + - boostingDuration + type: object + properties: + boostingDuration: + type: string + description: The expiration period after which boosting will stop for a particular document. + format: duration + description: Provides parameter values to a freshness scoring function. + MagnitudeScoringFunction: + allOf: + - $ref: '#/components/schemas/ScoringFunction' + description: Defines a function that boosts scores based on the magnitude of a numeric field. + x-ms-discriminator-value: magnitude + MagnitudeScoringParameters: + required: + - boostingRangeEnd + - boostingRangeStart + type: object + properties: + boostingRangeStart: + type: number + description: The field value at which boosting starts. + format: double + boostingRangeEnd: + type: number + description: The field value at which boosting ends. + format: double + constantBoostBeyondRange: + type: boolean + description: A value indicating whether to apply a constant boost for field values beyond the range end value; default is false. + x-ms-client-name: ShouldBoostBeyondRangeByConstant + description: Provides parameter values to a magnitude scoring function. + TagScoringFunction: + allOf: + - $ref: '#/components/schemas/ScoringFunction' + description: Defines a function that boosts scores of documents with string values matching a given list of tags. + x-ms-discriminator-value: tag + TagScoringParameters: + required: + - tagsParameter + type: object + properties: + tagsParameter: + type: string + description: The name of the parameter passed in search queries to specify the list of tags to compare against the target field. + description: Provides parameter values to a tag scoring function. + ScoringFunctionInterpolation: + enum: + - linear + - constant + - quadratic + - logarithmic + type: string + description: Defines the function used to interpolate score boosting across a range of documents. + x-ms-enum: + name: ScoringFunctionInterpolation + modelAsString: false + values: + - value: linear + name: Linear + description: Boosts scores by a linearly decreasing amount. This is the default interpolation for scoring functions. + - value: constant + name: Constant + description: Boosts scores by a constant factor. + - value: quadratic + name: Quadratic + description: 'Boosts scores by an amount that decreases quadratically. Boosts decrease slowly for higher scores, and more quickly as the scores decrease. This interpolation option is not allowed in tag scoring functions.' + - value: logarithmic + name: Logarithmic + description: 'Boosts scores by an amount that decreases logarithmically. Boosts decrease quickly for higher scores, and more slowly as the scores decrease. This interpolation option is not allowed in tag scoring functions.' + ScoringProfile: + required: + - name + type: object + properties: + name: + type: string + description: The name of the scoring profile. + text: + $ref: '#/components/schemas/TextWeights' + functions: + type: array + items: + $ref: '#/components/schemas/ScoringFunction' + description: The collection of functions that influence the scoring of documents. + functionAggregation: + $ref: '#/components/schemas/ScoringFunctionAggregation' + description: Defines parameters for a search index that influence scoring in search queries. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index + ScoringFunctionAggregation: + enum: + - sum + - average + - minimum + - maximum + - firstMatching + type: string + description: Defines the aggregation function used to combine the results of all the scoring functions in a scoring profile. + x-ms-enum: + name: ScoringFunctionAggregation + modelAsString: false + values: + - value: sum + name: Sum + description: Boost scores by the sum of all scoring function results. + - value: average + name: Average + description: Boost scores by the average of all scoring function results. + - value: minimum + name: Minimum + description: Boost scores by the minimum of all scoring function results. + - value: maximum + name: Maximum + description: Boost scores by the maximum of all scoring function results. + - value: firstMatching + name: FirstMatching + description: Boost scores using the first applicable scoring function in the scoring profile. + CorsOptions: + required: + - allowedOrigins + type: object + properties: + allowedOrigins: + type: array + items: + type: string + description: 'The list of origins from which JavaScript code will be granted access to your index. Can contain a list of hosts of the form {protocol}://{fully-qualified-domain-name}[:{port#}], or a single `*` to allow all origins (not recommended).' + maxAgeInSeconds: + type: integer + description: The duration for which browsers should cache CORS preflight responses. Defaults to 5 minutes. + format: int64 + nullable: true + description: Defines options to control Cross-Origin Resource Sharing (CORS) for an index. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Create-Index + Suggester: + required: + - name + - searchMode + - sourceFields + type: object + properties: + name: + type: string + description: The name of the suggester. + searchMode: + enum: + - analyzingInfixMatching + type: string + description: A value indicating the capabilities of the suggester. + x-ms-enum: + name: SuggesterSearchMode + modelAsString: false + values: + - value: analyzingInfixMatching + name: AnalyzingInfixMatching + description: 'Matches consecutive whole terms and prefixes in a field. For example, for the field ''The fastest brown fox'', the queries ''fast'' and ''fastest brow'' would both match.' + sourceFields: + type: array + items: + type: string + description: The list of field names to which the suggester applies. Each field must be searchable. + description: Defines how the Suggest API should apply to a group of fields in the index. + SearchIndex: + required: + - fields + - name + type: object + properties: + name: + type: string + description: The name of the index. + fields: + type: array + items: + $ref: '#/components/schemas/SearchField' + description: The fields of the index. + scoringProfiles: + type: array + items: + $ref: '#/components/schemas/ScoringProfile' + description: The scoring profiles for the index. + defaultScoringProfile: + type: string + description: 'The name of the scoring profile to use if none is specified in the query. If this property is not set and no scoring profile is specified in the query, then default scoring (tf-idf) will be used.' + corsOptions: + $ref: '#/components/schemas/CorsOptions' + suggesters: + type: array + items: + $ref: '#/components/schemas/Suggester' + description: The suggesters for the index. + analyzers: + type: array + items: + $ref: '#/components/schemas/LexicalAnalyzer' + description: The analyzers for the index. + tokenizers: + type: array + items: + $ref: '#/components/schemas/LexicalTokenizer' + description: The tokenizers for the index. + tokenFilters: + type: array + items: + $ref: '#/components/schemas/TokenFilter' + description: The token filters for the index. + charFilters: + type: array + items: + $ref: '#/components/schemas/CharFilter' + description: The character filters for the index. + encryptionKey: + $ref: '#/components/schemas/SearchResourceEncryptionKey' + similarity: + $ref: '#/components/schemas/Similarity' + semantic: + $ref: '#/components/schemas/SemanticSettings' + vectorSearch: + $ref: '#/components/schemas/VectorSearch' + '@odata.etag': + type: string + description: The ETag of the index. + x-ms-client-name: ETag + description: 'Represents a search index definition, which describes the fields and search behavior of an index.' + GetIndexStatisticsResult: + required: + - documentCount + - storageSize + - vectorIndexSize + type: object + properties: + documentCount: + type: integer + description: The number of documents in the index. + format: int64 + readOnly: true + storageSize: + type: integer + description: The amount of storage in bytes consumed by the index. + format: int64 + readOnly: true + vectorIndexSize: + type: integer + description: The amount of memory in bytes consumed by vectors in the index. + format: int64 + readOnly: true + description: Statistics for a given index. Statistics are collected periodically and are not guaranteed to always be up-to-date. + ListIndexesResult: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/SearchIndex' + description: The indexes in the Search service. + readOnly: true + x-ms-client-name: Indexes + description: 'Response from a List Indexes request. If successful, it includes the full definitions of all indexes.' + SearchIndexerSkillset: + required: + - name + - skills + type: object + properties: + name: + type: string + description: The name of the skillset. + description: + type: string + description: The description of the skillset. + skills: + type: array + items: + $ref: '#/components/schemas/SearchIndexerSkill' + description: A list of skills in the skillset. + cognitiveServices: + $ref: '#/components/schemas/CognitiveServicesAccount' + knowledgeStore: + $ref: '#/components/schemas/SearchIndexerKnowledgeStore' + '@odata.etag': + type: string + description: The ETag of the skillset. + x-ms-client-name: ETag + encryptionKey: + $ref: '#/components/schemas/SearchResourceEncryptionKey' + description: A list of skills. + externalDocs: + url: https://learn.microsoft.com/azure/search/cognitive-search-tutorial-blob + CognitiveServicesAccount: + required: + - '@odata.type' + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of Azure AI service resource attached to a skillset. + description: + type: string + description: Description of the Azure AI service resource attached to a skillset. + description: Base type for describing any Azure AI service resource attached to a skillset. + discriminator: + propertyName: '@odata.type' + SearchIndexerKnowledgeStore: + required: + - projections + - storageConnectionString + type: object + properties: + storageConnectionString: + type: string + description: The connection string to the storage account projections will be stored in. + projections: + type: array + items: + $ref: '#/components/schemas/SearchIndexerKnowledgeStoreProjection' + description: A list of additional projections to perform during indexing. + description: 'Definition of additional projections to azure blob, table, or files, of enriched data.' + externalDocs: + url: https://learn.microsoft.com/azure/search/knowledge-store-projection-overview + SearchIndexerKnowledgeStoreProjection: + type: object + properties: + tables: + type: array + items: + $ref: '#/components/schemas/SearchIndexerKnowledgeStoreTableProjectionSelector' + description: Projections to Azure Table storage. + objects: + type: array + items: + $ref: '#/components/schemas/SearchIndexerKnowledgeStoreObjectProjectionSelector' + description: Projections to Azure Blob storage. + files: + type: array + items: + $ref: '#/components/schemas/SearchIndexerKnowledgeStoreFileProjectionSelector' + description: Projections to Azure File storage. + description: Container object for various projection selectors. + SearchIndexerKnowledgeStoreProjectionSelector: + type: object + properties: + referenceKeyName: + type: string + description: Name of reference key to different projection. + generatedKeyName: + type: string + description: Name of generated key to store projection under. + source: + type: string + description: Source data to project. + sourceContext: + type: string + description: Source context for complex projections. + inputs: + type: array + items: + $ref: '#/components/schemas/InputFieldMappingEntry' + description: Nested inputs for complex projections. + description: Abstract class to share properties between concrete selectors. + SearchIndexerKnowledgeStoreBlobProjectionSelector: + allOf: + - $ref: '#/components/schemas/SearchIndexerKnowledgeStoreProjectionSelector' + description: Abstract class to share properties between concrete selectors. + SearchIndexerKnowledgeStoreTableProjectionSelector: + allOf: + - $ref: '#/components/schemas/SearchIndexerKnowledgeStoreProjectionSelector' + description: Description for what data to store in Azure Tables. + SearchIndexerKnowledgeStoreObjectProjectionSelector: + allOf: + - $ref: '#/components/schemas/SearchIndexerKnowledgeStoreBlobProjectionSelector' + description: Projection definition for what data to store in Azure Blob. + SearchIndexerKnowledgeStoreFileProjectionSelector: + allOf: + - $ref: '#/components/schemas/SearchIndexerKnowledgeStoreBlobProjectionSelector' + description: Projection definition for what data to store in Azure Files. + DefaultCognitiveServicesAccount: + allOf: + - $ref: '#/components/schemas/CognitiveServicesAccount' + description: An empty object that represents the default Azure AI service resource for a skillset. + x-ms-discriminator-value: '#Microsoft.Azure.Search.DefaultCognitiveServices' + CognitiveServicesAccountKey: + allOf: + - $ref: '#/components/schemas/CognitiveServicesAccount' + description: An Azure AI service resource provisioned with a key that is attached to a skillset. + x-ms-discriminator-value: '#Microsoft.Azure.Search.CognitiveServicesByKey' + SearchIndexerSkill: + required: + - '@odata.type' + - inputs + - outputs + type: object + properties: + '@odata.type': + type: string + description: A URI fragment specifying the type of skill. + name: + type: string + description: 'The name of the skill which uniquely identifies it within the skillset. A skill with no name defined will be given a default name of its 1-based index in the skills array, prefixed with the character `#`.' + description: + type: string + description: 'The description of the skill which describes the inputs, outputs, and usage of the skill.' + context: + type: string + description: 'Represents the level at which operations take place, such as the document root or document content (for example, /document or /document/content). The default is /document.' + inputs: + type: array + items: + $ref: '#/components/schemas/InputFieldMappingEntry' + description: 'Inputs of the skills could be a column in the source data set, or the output of an upstream skill.' + outputs: + type: array + items: + $ref: '#/components/schemas/OutputFieldMappingEntry' + description: 'The output of a skill is either a field in a search index, or a value that can be consumed as an input by another skill.' + description: Base type for skills. + discriminator: + propertyName: '@odata.type' + externalDocs: + url: https://learn.microsoft.com/azure/search/cognitive-search-predefined-skills + CustomEntity: + required: + - name + type: object + properties: + name: + type: string + description: 'The top-level entity descriptor. Matches in the skill output will be grouped by this name, and it should represent the "normalized" form of the text being found.' + description: + type: string + description: This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output. + nullable: true + type: + type: string + description: This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output. + nullable: true + subtype: + type: string + description: This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output. + nullable: true + id: + type: string + description: This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output. + nullable: true + caseSensitive: + type: boolean + description: 'Defaults to false. Boolean value denoting whether comparisons with the entity name should be sensitive to character casing. Sample case insensitive matches of "Microsoft" could be: microsoft, microSoft, MICROSOFT.' + nullable: true + accentSensitive: + type: boolean + description: Defaults to false. Boolean value denoting whether comparisons with the entity name should be sensitive to accent. + nullable: true + fuzzyEditDistance: + type: integer + description: 'Defaults to 0. Maximum value of 5. Denotes the acceptable number of divergent characters that would still constitute a match with the entity name. The smallest possible fuzziness for any given match is returned. For instance, if the edit distance is set to 3, "Windows10" would still match "Windows", "Windows10" and "Windows 7". When case sensitivity is set to false, case differences do NOT count towards fuzziness tolerance, but otherwise do.' + format: int32 + nullable: true + defaultCaseSensitive: + type: boolean + description: Changes the default case sensitivity value for this entity. It be used to change the default value of all aliases caseSensitive values. + nullable: true + defaultAccentSensitive: + type: boolean + description: Changes the default accent sensitivity value for this entity. It be used to change the default value of all aliases accentSensitive values. + nullable: true + defaultFuzzyEditDistance: + type: integer + description: Changes the default fuzzy edit distance value for this entity. It can be used to change the default value of all aliases fuzzyEditDistance values. + format: int32 + nullable: true + aliases: + type: array + items: + $ref: '#/components/schemas/CustomEntityAlias' + description: An array of complex objects that can be used to specify alternative spellings or synonyms to the root entity name. + nullable: true + description: 'An object that contains information about the matches that were found, and related metadata.' + CustomEntityAlias: + required: + - text + type: object + properties: + text: + type: string + description: The text of the alias. + caseSensitive: + type: boolean + description: Determine if the alias is case sensitive. + nullable: true + accentSensitive: + type: boolean + description: Determine if the alias is accent sensitive. + nullable: true + fuzzyEditDistance: + type: integer + description: Determine the fuzzy edit distance of the alias. + format: int32 + nullable: true + description: A complex object that can be used to specify alternative spellings or synonyms to the root entity name. + InputFieldMappingEntry: + required: + - name + type: object + properties: + name: + type: string + description: The name of the input. + source: + type: string + description: The source of the input. + sourceContext: + type: string + description: The source context used for selecting recursive inputs. + inputs: + type: array + items: + $ref: '#/components/schemas/InputFieldMappingEntry' + description: The recursive inputs used when creating a complex type. + description: Input field mapping for a skill. + OutputFieldMappingEntry: + required: + - name + type: object + properties: + name: + type: string + description: The name of the output defined by the skill. + targetName: + type: string + description: The target name of the output. It is optional and default to name. + description: Output field mapping for a skill. + externalDocs: + url: https://learn.microsoft.com/rest/api/searchservice/Naming-rules + ConditionalSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that enables scenarios that require a Boolean operation to determine the data to assign to an output. + x-ms-discriminator-value: '#Microsoft.Skills.Util.ConditionalSkill' + KeyPhraseExtractionSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that uses text analytics for key phrase extraction. + x-ms-discriminator-value: '#Microsoft.Skills.Text.KeyPhraseExtractionSkill' + OcrSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that extracts text from image files. + x-ms-discriminator-value: '#Microsoft.Skills.Vision.OcrSkill' + ImageAnalysisSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that analyzes image files. It extracts a rich set of visual features based on the image content. + x-ms-discriminator-value: '#Microsoft.Skills.Vision.ImageAnalysisSkill' + LanguageDetectionSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that detects the language of input text and reports a single language code for every document submitted on the request. The language code is paired with a score indicating the confidence of the analysis. + x-ms-discriminator-value: '#Microsoft.Skills.Text.LanguageDetectionSkill' + ShaperSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill for reshaping the outputs. It creates a complex type to support composite fields (also known as multipart fields). + x-ms-discriminator-value: '#Microsoft.Skills.Util.ShaperSkill' + MergeSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'A skill for merging two or more strings into a single unified string, with an optional user-defined delimiter separating each component part.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.MergeSkill' + EntityRecognitionSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: Text analytics entity recognition. + x-ms-discriminator-value: '#Microsoft.Skills.Text.EntityRecognitionSkill' + SentimentSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'Text analytics positive-negative sentiment analysis, scored as a floating point value in a range of zero to 1.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.SentimentSkill' + SentimentSkillV3: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'Using the Text Analytics API, evaluates unstructured text and for each record, provides sentiment labels (such as "negative", "neutral" and "positive") based on the highest confidence score found by the service at a sentence and document-level.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.V3.SentimentSkill' + EntityLinkingSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'Using the Text Analytics API, extracts linked entities from text.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.V3.EntityLinkingSkill' + EntityRecognitionSkillV3: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'Using the Text Analytics API, extracts entities of different types from text.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.V3.EntityRecognitionSkill' + PIIDetectionSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'Using the Text Analytics API, extracts personal information from an input text and gives you the option of masking it.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.PIIDetectionSkill' + SplitSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill to split a string into chunks of text. + x-ms-discriminator-value: '#Microsoft.Skills.Text.SplitSkill' + CustomEntityLookupSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'A skill looks for text from a custom, user-defined list of words and phrases.' + x-ms-discriminator-value: '#Microsoft.Skills.Text.CustomEntityLookupSkill' + TextTranslationSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill to translate text from one language to another. + x-ms-discriminator-value: '#Microsoft.Skills.Text.TranslationSkill' + DocumentExtractionSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: A skill that extracts content from a file within the enrichment pipeline. + x-ms-discriminator-value: '#Microsoft.Skills.Util.DocumentExtractionSkill' + WebApiSkill: + allOf: + - $ref: '#/components/schemas/SearchIndexerSkill' + description: 'A skill that can call a Web API endpoint, allowing you to extend a skillset by having it call your custom code.' + x-ms-discriminator-value: '#Microsoft.Skills.Custom.WebApiSkill' + WebApiHttpHeaders: + type: object + additionalProperties: + type: string + description: A dictionary of http request headers. + ListSkillsetsResult: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/SearchIndexerSkillset' + description: The skillsets defined in the Search service. + readOnly: true + x-ms-client-name: Skillsets + description: 'Response from a list skillset request. If successful, it includes the full definitions of all skillsets.' + TextSplitMode: + enum: + - pages + - sentences + type: string + description: A value indicating which split mode to perform. + x-ms-enum: + name: TextSplitMode + modelAsString: true + values: + - value: pages + name: Pages + description: Split the text into individual pages. + - value: sentences + name: Sentences + description: Split the text into individual sentences. + VisualFeature: + enum: + - adult + - brands + - categories + - description + - faces + - objects + - tags + type: string + description: The strings indicating what visual feature types to return. + x-ms-enum: + name: VisualFeature + modelAsString: true + values: + - value: adult + name: Adult + description: Visual features recognized as adult persons. + - value: brands + name: Brands + description: Visual features recognized as commercial brands. + - value: categories + name: Categories + description: Categories. + - value: description + name: Description + description: Description. + - value: faces + name: Faces + description: Visual features recognized as people faces. + - value: objects + name: Objects + description: Visual features recognized as objects. + - value: tags + name: Tags + description: Tags. + ImageDetail: + enum: + - celebrities + - landmarks + type: string + description: A string indicating which domain-specific details to return. + x-ms-enum: + name: ImageDetail + modelAsString: true + values: + - value: celebrities + name: Celebrities + description: Details recognized as celebrities. + - value: landmarks + name: Landmarks + description: Details recognized as landmarks. + EntityCategory: + enum: + - location + - organization + - person + - quantity + - datetime + - url + - email + type: string + description: A string indicating what entity categories to return. + x-ms-enum: + name: EntityCategory + modelAsString: true + values: + - value: location + name: Location + description: Entities describing a physical location. + - value: organization + name: Organization + description: Entities describing an organization. + - value: person + name: Person + description: Entities describing a person. + - value: quantity + name: Quantity + description: Entities describing a quantity. + - value: datetime + name: Datetime + description: Entities describing a date and time. + - value: url + name: Url + description: Entities describing a URL. + - value: email + name: Email + description: Entities describing an email address. + PIIDetectionSkillMaskingMode: + enum: + - none + - replace + type: string + description: A string indicating what maskingMode to use to mask the personal information detected in the input text. + x-ms-enum: + name: PIIDetectionSkillMaskingMode + modelAsString: true + values: + - value: none + name: None + description: No masking occurs and the maskedText output will not be returned. + - value: replace + name: Replace + description: Replaces the detected entities with the character given in the maskingCharacter parameter. The character will be repeated to the length of the detected entity so that the offsets will correctly correspond to both the input text as well as the output maskedText. + SentimentSkillLanguage: + enum: + - da + - nl + - en + - fi + - fr + - de + - el + - it + - no + - pl + - pt-PT + - ru + - es + - sv + - tr + type: string + description: The language codes supported for input text by SentimentSkill. + x-ms-enum: + name: SentimentSkillLanguage + modelAsString: true + values: + - value: da + description: Danish + - value: nl + description: Dutch + - value: en + description: English + - value: fi + description: Finnish + - value: fr + description: French + - value: de + description: German + - value: el + description: Greek + - value: it + description: Italian + - value: no + description: Norwegian (Bokmaal) + - value: pl + description: Polish + - value: pt-PT + description: Portuguese (Portugal) + - value: ru + description: Russian + - value: es + description: Spanish + - value: sv + description: Swedish + - value: tr + description: Turkish + KeyPhraseExtractionSkillLanguage: + enum: + - da + - nl + - en + - fi + - fr + - de + - it + - ja + - ko + - no + - pl + - pt-PT + - pt-BR + - ru + - es + - sv + type: string + description: The language codes supported for input text by KeyPhraseExtractionSkill. + x-ms-enum: + name: KeyPhraseExtractionSkillLanguage + modelAsString: true + values: + - value: da + description: Danish + - value: nl + description: Dutch + - value: en + description: English + - value: fi + description: Finnish + - value: fr + description: French + - value: de + description: German + - value: it + description: Italian + - value: ja + description: Japanese + - value: ko + description: Korean + - value: no + description: Norwegian (Bokmaal) + - value: pl + description: Polish + - value: pt-PT + description: Portuguese (Portugal) + - value: pt-BR + description: Portuguese (Brazil) + - value: ru + description: Russian + - value: es + description: Spanish + - value: sv + description: Swedish + OcrSkillLanguage: + enum: + - af + - sq + - anp + - ar + - ast + - awa + - az + - bfy + - eu + - be + - be-cyrl + - be-latn + - bho + - bi + - brx + - bs + - bra + - br + - bg + - bns + - bua + - ca + - ceb + - rab + - ch + - hne + - zh-Hans + - zh-Hant + - kw + - co + - crh + - hr + - cs + - da + - prs + - dhi + - doi + - nl + - en + - myv + - et + - fo + - fj + - fil + - fi + - fr + - fur + - gag + - gl + - de + - gil + - gon + - el + - kl + - gvr + - ht + - hlb + - hni + - bgc + - haw + - hi + - mww + - hoc + - hu + - is + - smn + - id + - ia + - iu + - ga + - it + - ja + - Jns + - jv + - kea + - kac + - xnr + - krc + - kaa-cyrl + - kaa + - csb + - kk-cyrl + - kk-latn + - klr + - kha + - quc + - ko + - kfq + - kpy + - kos + - kum + - ku-arab + - ku-latn + - kru + - ky + - lkt + - la + - lt + - dsb + - smj + - lb + - bfz + - ms + - mt + - kmj + - gv + - mi + - mr + - mn + - cnr-cyrl + - cnr-latn + - nap + - ne + - niu + - nog + - sme + - nb + - no + - oc + - os + - ps + - fa + - pl + - pt + - pa + - ksh + - ro + - rm + - ru + - sck + - sm + - sa + - sat + - sco + - gd + - sr + - sr-Cyrl + - sr-Latn + - xsr + - srx + - sms + - sk + - sl + - so + - sma + - es + - sw + - sv + - tg + - tt + - tet + - thf + - to + - tr + - tk + - tyv + - hsb + - ur + - ug + - uz-arab + - uz-cyrl + - uz + - vo + - wae + - cy + - fy + - yua + - za + - zu + - unk + type: string + description: The language codes supported for input by OcrSkill. + x-ms-enum: + name: OcrSkillLanguage + modelAsString: true + values: + - value: af + description: Afrikaans + - value: sq + description: Albanian + - value: anp + description: Angika (Devanagiri) + - value: ar + description: Arabic + - value: ast + description: Asturian + - value: awa + description: Awadhi-Hindi (Devanagiri) + - value: az + description: Azerbaijani (Latin) + - value: bfy + description: Bagheli + - value: eu + description: Basque + - value: be + description: Belarusian (Cyrillic and Latin) + - value: be-cyrl + description: Belarusian (Cyrillic) + - value: be-latn + description: Belarusian (Latin) + - value: bho + description: Bhojpuri-Hindi (Devanagiri) + - value: bi + description: Bislama + - value: brx + description: Bodo (Devanagiri) + - value: bs + description: Bosnian Latin + - value: bra + description: Brajbha + - value: br + description: Breton + - value: bg + description: Bulgarian + - value: bns + description: Bundeli + - value: bua + description: Buryat (Cyrillic) + - value: ca + description: Catalan + - value: ceb + description: Cebuano + - value: rab + description: Chamling + - value: ch + description: Chamorro + - value: hne + description: Chhattisgarhi (Devanagiri) + - value: zh-Hans + description: Chinese Simplified + - value: zh-Hant + description: Chinese Traditional + - value: kw + description: Cornish + - value: co + description: Corsican + - value: crh + description: Crimean Tatar (Latin) + - value: hr + description: Croatian + - value: cs + description: Czech + - value: da + description: Danish + - value: prs + description: Dari + - value: dhi + description: Dhimal (Devanagiri) + - value: doi + description: Dogri (Devanagiri) + - value: nl + description: Dutch + - value: en + description: English + - value: myv + description: Erzya (Cyrillic) + - value: et + description: Estonian + - value: fo + description: Faroese + - value: fj + description: Fijian + - value: fil + description: Filipino + - value: fi + description: Finnish + - value: fr + description: French + - value: fur + description: Frulian + - value: gag + description: Gagauz (Latin) + - value: gl + description: Galician + - value: de + description: German + - value: gil + description: Gilbertese + - value: gon + description: Gondi (Devanagiri) + - value: el + description: Greek + - value: kl + description: Greenlandic + - value: gvr + description: Gurung (Devanagiri) + - value: ht + description: Haitian Creole + - value: hlb + description: Halbi (Devanagiri) + - value: hni + description: Hani + - value: bgc + description: Haryanvi + - value: haw + description: Hawaiian + - value: hi + description: Hindi + - value: mww + description: Hmong Daw (Latin) + - value: hoc + description: Ho (Devanagiri) + - value: hu + description: Hungarian + - value: is + description: Icelandic + - value: smn + description: Inari Sami + - value: id + description: Indonesian + - value: ia + description: Interlingua + - value: iu + description: Inuktitut (Latin) + - value: ga + description: Irish + - value: it + description: Italian + - value: ja + description: Japanese + - value: Jns + description: Jaunsari (Devanagiri) + - value: jv + description: Javanese + - value: kea + description: Kabuverdianu + - value: kac + description: Kachin (Latin) + - value: xnr + description: Kangri (Devanagiri) + - value: krc + description: Karachay-Balkar + - value: kaa-cyrl + description: Kara-Kalpak (Cyrillic) + - value: kaa + description: Kara-Kalpak (Latin) + - value: csb + description: Kashubian + - value: kk-cyrl + description: Kazakh (Cyrillic) + - value: kk-latn + description: Kazakh (Latin) + - value: klr + description: Khaling + - value: kha + description: Khasi + - value: quc + description: K'iche' + - value: ko + description: Korean + - value: kfq + description: Korku + - value: kpy + description: Koryak + - value: kos + description: Kosraean + - value: kum + description: Kumyk (Cyrillic) + - value: ku-arab + description: Kurdish (Arabic) + - value: ku-latn + description: Kurdish (Latin) + - value: kru + description: Kurukh (Devanagiri) + - value: ky + description: Kyrgyz (Cyrillic) + - value: lkt + description: Lakota + - value: la + description: Latin + - value: lt + description: Lithuanian + - value: dsb + description: Lower Sorbian + - value: smj + description: Lule Sami + - value: lb + description: Luxembourgish + - value: bfz + description: Mahasu Pahari (Devanagiri) + - value: ms + description: Malay (Latin) + - value: mt + description: Maltese + - value: kmj + description: Malto (Devanagiri) + - value: gv + description: Manx + - value: mi + description: Maori + - value: mr + description: Marathi + - value: mn + description: Mongolian (Cyrillic) + - value: cnr-cyrl + description: Montenegrin (Cyrillic) + - value: cnr-latn + description: Montenegrin (Latin) + - value: nap + description: Neapolitan + - value: ne + description: Nepali + - value: niu + description: Niuean + - value: nog + description: Nogay + - value: sme + description: Northern Sami (Latin) + - value: nb + description: Norwegian + - value: no + description: Norwegian + - value: oc + description: Occitan + - value: os + description: Ossetic + - value: ps + description: Pashto + - value: fa + description: Persian + - value: pl + description: Polish + - value: pt + description: Portuguese + - value: pa + description: Punjabi (Arabic) + - value: ksh + description: Ripuarian + - value: ro + description: Romanian + - value: rm + description: Romansh + - value: ru + description: Russian + - value: sck + description: Sadri (Devanagiri) + - value: sm + description: Samoan (Latin) + - value: sa + description: Sanskrit (Devanagiri) + - value: sat + description: Santali (Devanagiri) + - value: sco + description: Scots + - value: gd + description: Scottish Gaelic + - value: sr + description: Serbian (Latin) + - value: sr-Cyrl + description: Serbian (Cyrillic) + - value: sr-Latn + description: Serbian (Latin) + - value: xsr + description: Sherpa (Devanagiri) + - value: srx + description: Sirmauri (Devanagiri) + - value: sms + description: Skolt Sami + - value: sk + description: Slovak + - value: sl + description: Slovenian + - value: so + description: Somali (Arabic) + - value: sma + description: Southern Sami + - value: es + description: Spanish + - value: sw + description: Swahili (Latin) + - value: sv + description: Swedish + - value: tg + description: Tajik (Cyrillic) + - value: tt + description: Tatar (Latin) + - value: tet + description: Tetum + - value: thf + description: Thangmi + - value: to + description: Tongan + - value: tr + description: Turkish + - value: tk + description: Turkmen (Latin) + - value: tyv + description: Tuvan + - value: hsb + description: Upper Sorbian + - value: ur + description: Urdu + - value: ug + description: Uyghur (Arabic) + - value: uz-arab + description: Uzbek (Arabic) + - value: uz-cyrl + description: Uzbek (Cyrillic) + - value: uz + description: Uzbek (Latin) + - value: vo + description: Volapük + - value: wae + description: Walser + - value: cy + description: Welsh + - value: fy + description: Western Frisian + - value: yua + description: Yucatec Maya + - value: za + description: Zhuang + - value: zu + description: Zulu + - value: unk + description: Unknown (All) + SplitSkillLanguage: + enum: + - am + - bs + - cs + - da + - de + - en + - es + - et + - fi + - fr + - he + - hi + - hr + - hu + - id + - is + - it + - ja + - ko + - lv + - nb + - nl + - pl + - pt + - pt-br + - ru + - sk + - sl + - sr + - sv + - tr + - ur + - zh + type: string + description: The language codes supported for input text by SplitSkill. + x-ms-enum: + name: SplitSkillLanguage + modelAsString: true + values: + - value: am + description: Amharic + - value: bs + description: Bosnian + - value: cs + description: Czech + - value: da + description: Danish + - value: de + description: German + - value: en + description: English + - value: es + description: Spanish + - value: et + description: Estonian + - value: fi + description: Finnish + - value: fr + description: French + - value: he + description: Hebrew + - value: hi + description: Hindi + - value: hr + description: Croatian + - value: hu + description: Hungarian + - value: id + description: Indonesian + - value: is + description: Icelandic + - value: it + description: Italian + - value: ja + description: Japanese + - value: ko + description: Korean + - value: lv + description: Latvian + - value: nb + description: Norwegian + - value: nl + description: Dutch + - value: pl + description: Polish + - value: pt + description: Portuguese (Portugal) + - value: pt-br + description: Portuguese (Brazil) + - value: ru + description: Russian + - value: sk + description: Slovak + - value: sl + description: Slovenian + - value: sr + description: Serbian + - value: sv + description: Swedish + - value: tr + description: Turkish + - value: ur + description: Urdu + - value: zh + description: Chinese (Simplified) + CustomEntityLookupSkillLanguage: + enum: + - da + - de + - en + - es + - fi + - fr + - it + - ko + - pt + type: string + description: The language codes supported for input text by CustomEntityLookupSkill. + x-ms-enum: + name: CustomEntityLookupSkillLanguage + modelAsString: true + values: + - value: da + description: Danish + - value: de + description: German + - value: en + description: English + - value: es + description: Spanish + - value: fi + description: Finnish + - value: fr + description: French + - value: it + description: Italian + - value: ko + description: Korean + - value: pt + description: Portuguese + EntityRecognitionSkillLanguage: + enum: + - ar + - cs + - zh-Hans + - zh-Hant + - da + - nl + - en + - fi + - fr + - de + - el + - hu + - it + - ja + - ko + - no + - pl + - pt-PT + - pt-BR + - ru + - es + - sv + - tr + type: string + description: The language codes supported for input text by EntityRecognitionSkill. + x-ms-enum: + name: EntityRecognitionSkillLanguage + modelAsString: true + values: + - value: ar + description: Arabic + - value: cs + description: Czech + - value: zh-Hans + description: Chinese-Simplified + - value: zh-Hant + description: Chinese-Traditional + - value: da + description: Danish + - value: nl + description: Dutch + - value: en + description: English + - value: fi + description: Finnish + - value: fr + description: French + - value: de + description: German + - value: el + description: Greek + - value: hu + description: Hungarian + - value: it + description: Italian + - value: ja + description: Japanese + - value: ko + description: Korean + - value: no + description: Norwegian (Bokmaal) + - value: pl + description: Polish + - value: pt-PT + description: Portuguese (Portugal) + - value: pt-BR + description: Portuguese (Brazil) + - value: ru + description: Russian + - value: es + description: Spanish + - value: sv + description: Swedish + - value: tr + description: Turkish + TextTranslationSkillLanguage: + enum: + - af + - ar + - bn + - bs + - bg + - yue + - ca + - zh-Hans + - zh-Hant + - hr + - cs + - da + - nl + - en + - et + - fj + - fil + - fi + - fr + - de + - el + - ht + - he + - hi + - mww + - hu + - is + - id + - it + - ja + - sw + - tlh + - ko + - lv + - lt + - mg + - ms + - mt + - nb + - fa + - pl + - pt + - otq + - ro + - ru + - sm + - sr-Cyrl + - sr-Latn + - sk + - sl + - es + - sv + - ty + - ta + - te + - th + - to + - tr + - uk + - ur + - vi + - cy + - yua + type: string + description: The language codes supported for input text by TextTranslationSkill. + x-ms-enum: + name: TextTranslationSkillLanguage + modelAsString: true + values: + - value: af + description: Afrikaans + - value: ar + description: Arabic + - value: bn + description: Bangla + - value: bs + description: Bosnian (Latin) + - value: bg + description: Bulgarian + - value: yue + description: Cantonese (Traditional) + - value: ca + description: Catalan + - value: zh-Hans + description: Chinese Simplified + - value: zh-Hant + description: Chinese Traditional + - value: hr + description: Croatian + - value: cs + description: Czech + - value: da + description: Danish + - value: nl + description: Dutch + - value: en + description: English + - value: et + description: Estonian + - value: fj + description: Fijian + - value: fil + description: Filipino + - value: fi + description: Finnish + - value: fr + description: French + - value: de + description: German + - value: el + description: Greek + - value: ht + description: Haitian Creole + - value: he + description: Hebrew + - value: hi + description: Hindi + - value: mww + description: Hmong Daw + - value: hu + description: Hungarian + - value: is + description: Icelandic + - value: id + description: Indonesian + - value: it + description: Italian + - value: ja + description: Japanese + - value: sw + description: Kiswahili + - value: tlh + description: Klingon + - value: ko + description: Korean + - value: lv + description: Latvian + - value: lt + description: Lithuanian + - value: mg + description: Malagasy + - value: ms + description: Malay + - value: mt + description: Maltese + - value: nb + description: Norwegian + - value: fa + description: Persian + - value: pl + description: Polish + - value: pt + description: Portuguese + - value: otq + description: Queretaro Otomi + - value: ro + description: Romanian + - value: ru + description: Russian + - value: sm + description: Samoan + - value: sr-Cyrl + description: Serbian (Cyrillic) + - value: sr-Latn + description: Serbian (Latin) + - value: sk + description: Slovak + - value: sl + description: Slovenian + - value: es + description: Spanish + - value: sv + description: Swedish + - value: ty + description: Tahitian + - value: ta + description: Tamil + - value: te + description: Telugu + - value: th + description: Thai + - value: to + description: Tongan + - value: tr + description: Turkish + - value: uk + description: Ukrainian + - value: ur + description: Urdu + - value: vi + description: Vietnamese + - value: cy + description: Welsh + - value: yua + description: Yucatec Maya + ImageAnalysisSkillLanguage: + enum: + - ar + - az + - bg + - bs + - ca + - cs + - cy + - da + - de + - el + - en + - es + - et + - eu + - fi + - fr + - ga + - gl + - he + - hi + - hr + - hu + - id + - it + - ja + - kk + - ko + - lt + - lv + - mk + - ms + - nb + - nl + - pl + - prs + - pt-BR + - pt + - pt-PT + - ro + - ru + - sk + - sl + - sr-Cyrl + - sr-Latn + - sv + - th + - tr + - uk + - vi + - zh + - zh-Hans + - zh-Hant + type: string + description: The language codes supported for input by ImageAnalysisSkill. + x-ms-enum: + name: ImageAnalysisSkillLanguage + modelAsString: true + values: + - value: ar + description: Arabic + - value: az + description: Azerbaijani + - value: bg + description: Bulgarian + - value: bs + description: Bosnian Latin + - value: ca + description: Catalan + - value: cs + description: Czech + - value: cy + description: Welsh + - value: da + description: Danish + - value: de + description: German + - value: el + description: Greek + - value: en + description: English + - value: es + description: Spanish + - value: et + description: Estonian + - value: eu + description: Basque + - value: fi + description: Finnish + - value: fr + description: French + - value: ga + description: Irish + - value: gl + description: Galician + - value: he + description: Hebrew + - value: hi + description: Hindi + - value: hr + description: Croatian + - value: hu + description: Hungarian + - value: id + description: Indonesian + - value: it + description: Italian + - value: ja + description: Japanese + - value: kk + description: Kazakh + - value: ko + description: Korean + - value: lt + description: Lithuanian + - value: lv + description: Latvian + - value: mk + description: Macedonian + - value: ms + description: Malay Malaysia + - value: nb + description: Norwegian (Bokmal) + - value: nl + description: Dutch + - value: pl + description: Polish + - value: prs + description: Dari + - value: pt-BR + description: Portuguese-Brazil + - value: pt + description: Portuguese-Portugal + - value: pt-PT + description: Portuguese-Portugal + - value: ro + description: Romanian + - value: ru + description: Russian + - value: sk + description: Slovak + - value: sl + description: Slovenian + - value: sr-Cyrl + description: Serbian - Cyrillic RS + - value: sr-Latn + description: Serbian - Latin RS + - value: sv + description: Swedish + - value: th + description: Thai + - value: tr + description: Turkish + - value: uk + description: Ukrainian + - value: vi + description: Vietnamese + - value: zh + description: Chinese Simplified + - value: zh-Hans + description: Chinese Simplified + - value: zh-Hant + description: Chinese Traditional + SynonymMap: + required: + - format + - name + - synonyms + type: object + properties: + name: + type: string + description: The name of the synonym map. + format: + enum: + - solr + type: string + description: The format of the synonym map. Only the 'solr' format is currently supported. + x-ms-enum: + name: SynonymMapFormat + modelAsString: false + values: + - value: solr + name: Solr + description: Selects the SOLR format for synonym maps. + synonyms: + type: string + description: A series of synonym rules in the specified synonym map format. The rules must be separated by newlines. + encryptionKey: + $ref: '#/components/schemas/SearchResourceEncryptionKey' + '@odata.etag': + type: string + description: The ETag of the synonym map. + x-ms-client-name: ETag + description: Represents a synonym map definition. + ListSynonymMapsResult: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/SynonymMap' + description: The synonym maps in the Search service. + readOnly: true + x-ms-client-name: SynonymMaps + description: 'Response from a List SynonymMaps request. If successful, it includes the full definitions of all synonym maps.' + SearchResourceEncryptionKey: + required: + - keyVaultKeyName + - keyVaultKeyVersion + - keyVaultUri + type: object + properties: + keyVaultKeyName: + type: string + description: The name of your Azure Key Vault key to be used to encrypt your data at rest. + x-ms-client-name: keyName + keyVaultKeyVersion: + type: string + description: The version of your Azure Key Vault key to be used to encrypt your data at rest. + x-ms-client-name: keyVersion + keyVaultUri: + type: string + description: 'The URI of your Azure Key Vault, also referred to as DNS name, that contains the key to be used to encrypt your data at rest. An example URI might be `https://my-keyvault-name.vault.azure.net`.' + x-ms-client-name: vaultUri + accessCredentials: + $ref: '#/components/schemas/AzureActiveDirectoryApplicationCredentials' + description: 'A customer-managed encryption key in Azure Key Vault. Keys that you create and manage can be used to encrypt or decrypt data-at-rest on your search service, such as indexes and synonym maps.' + AzureActiveDirectoryApplicationCredentials: + required: + - applicationId + type: object + properties: + applicationId: + type: string + description: An AAD Application ID that was granted the required access permissions to the Azure Key Vault that is to be used when encrypting your data at rest. The Application ID should not be confused with the Object ID for your AAD Application. + applicationSecret: + type: string + description: The authentication key of the specified AAD application. + description: 'Credentials of a registered application created for your search service, used for authenticated access to the encryption keys stored in Azure Key Vault.' + ServiceStatistics: + required: + - counters + - limits + type: object + properties: + counters: + $ref: '#/components/schemas/ServiceCounters' + limits: + $ref: '#/components/schemas/ServiceLimits' + description: 'Response from a get service statistics request. If successful, it includes service level counters and limits.' + ServiceCounters: + required: + - dataSourcesCount + - documentCount + - indexersCount + - indexesCount + - storageSize + - synonymMaps + - vectorIndexSize + type: object + properties: + documentCount: + $ref: '#/components/schemas/ResourceCounter' + indexesCount: + $ref: '#/components/schemas/ResourceCounter' + indexersCount: + $ref: '#/components/schemas/ResourceCounter' + dataSourcesCount: + $ref: '#/components/schemas/ResourceCounter' + storageSize: + $ref: '#/components/schemas/ResourceCounter' + synonymMaps: + $ref: '#/components/schemas/ResourceCounter' + skillsetCount: + $ref: '#/components/schemas/ResourceCounter' + vectorIndexSize: + $ref: '#/components/schemas/ResourceCounter' + description: Represents service-level resource counters and quotas. + ServiceLimits: + type: object + properties: + maxFieldsPerIndex: + type: integer + description: The maximum allowed fields per index. + format: int32 + nullable: true + maxFieldNestingDepthPerIndex: + type: integer + description: 'The maximum depth which you can nest sub-fields in an index, including the top-level complex field. For example, a/b/c has a nesting depth of 3.' + format: int32 + nullable: true + maxComplexCollectionFieldsPerIndex: + type: integer + description: The maximum number of fields of type Collection(Edm.ComplexType) allowed in an index. + format: int32 + nullable: true + maxComplexObjectsInCollectionsPerDocument: + type: integer + description: The maximum number of objects in complex collections allowed per document. + format: int32 + nullable: true + description: Represents various service level limits. + ResourceCounter: + required: + - usage + type: object + properties: + usage: + type: integer + description: The resource usage amount. + format: int64 + quota: + type: integer + description: The resource amount quota. + format: int64 + nullable: true + description: Represents a resource's usage and quota. + SearchError: + required: + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + readOnly: true + message: + type: string + description: A human-readable representation of the error. + readOnly: true + details: + type: array + items: + $ref: '#/components/schemas/SearchError' + description: An array of details about specific errors that led to this reported error. + readOnly: true + description: Describes an error condition for the API. + SemanticSettings: + type: object + properties: + defaultConfiguration: + type: string + description: 'Allows you to set the name of a default semantic configuration in your index, making it optional to pass it on as a query parameter every time.' + x-ms-client-name: defaultConfigurationName + configurations: + type: array + items: + $ref: '#/components/schemas/SemanticConfiguration' + description: The semantic configurations for the index. + description: Defines parameters for a search index that influence semantic capabilities. + externalDocs: + url: https://learn.microsoft.com/azure/search/semantic-search-overview + x-ms-client-name: SemanticSearch + SemanticConfiguration: + required: + - name + - prioritizedFields + type: object + properties: + name: + type: string + description: The name of the semantic configuration. + prioritizedFields: + $ref: '#/components/schemas/PrioritizedFields' + description: Defines a specific configuration to be used in the context of semantic capabilities. + externalDocs: + url: https://learn.microsoft.com/azure/search/semantic-search-overview + PrioritizedFields: + type: object + properties: + titleField: + $ref: '#/components/schemas/SemanticField' + prioritizedContentFields: + type: array + items: + $ref: '#/components/schemas/SemanticField' + description: 'Defines the content fields to be used for semantic ranking, captions, highlights, and answers. For the best result, the selected fields should contain text in natural language form. The order of the fields in the array represents their priority. Fields with lower priority may get truncated if the content is long.' + x-ms-client-name: contentFields + prioritizedKeywordsFields: + type: array + items: + $ref: '#/components/schemas/SemanticField' + description: 'Defines the keyword fields to be used for semantic ranking, captions, highlights, and answers. For the best result, the selected fields should contain a list of keywords. The order of the fields in the array represents their priority. Fields with lower priority may get truncated if the content is long.' + x-ms-client-name: keywordsFields + description: 'Describes the title, content, and keywords fields to be used for semantic ranking, captions, highlights, and answers.' + externalDocs: + url: https://learn.microsoft.com/azure/search/semantic-search-overview + x-ms-client-name: SemanticPrioritizedFields + SemanticField: + required: + - fieldName + type: object + properties: + fieldName: + type: string + description: '' + description: A field that is used as part of the semantic configuration. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-api-spec.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-api-spec.yaml new file mode 100644 index 000000000..36fb6950a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-api-spec.yaml @@ -0,0 +1,1639 @@ +openapi: 3.0.1 +info: + title: Azure Search Index + description: 'Client that can be used to query an index and upload, merge, or delete documents.' + version: '1.0' +servers: + - url: https://REPLACED-BY-POLICY/ai-search +paths: + '/indexes/{index-name}/docs/$count': + get: + tags: + - Documents + summary: Documents_Count + description: Queries the number of documents in the index. + operationId: Documents_Count + parameters: + - name: index-name + in: path + required: true + schema: + enum: + - product-info + type: string + default: product-info + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + enum: + - '2023-11-01' + type: string + default: '2023-11-01' + - name: x-ms-client-request-id + in: header + description: Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/Docs-countGet200ApplicationJsonResponse' + example: 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs': + get: + tags: + - Documents + summary: Documents_SearchGet + description: Searches for documents in the index. + operationId: Documents_SearchGet + parameters: + - name: index-name + in: path + required: true + schema: + enum: + - product-info + type: string + default: product-info + - name: api-version + in: query + required: true + schema: + enum: + - 2023-10-01-Preview + - '2023-11-01' + type: string + default: '2023-11-01' + - name: search + in: query + description: A full-text search query expression; Use "*" or omit this parameter to match all documents. + schema: + type: string + - name: $count + in: query + description: A value that specifies whether to fetch the total count of results. Default is false. Setting this value to true may have a performance impact. Note that the count returned is an approximation. + schema: + type: boolean + - name: facet + in: query + description: 'The list of facet expressions to apply to the search query. Each facet expression contains a field name, optionally followed by a comma-separated list of name:value pairs.' + schema: + type: array + items: + type: string + - name: $filter + in: query + description: The OData $filter expression to apply to the search query. + schema: + type: string + - name: highlight + in: query + description: The list of field names to use for hit highlights. Only searchable fields can be used for hit highlighting. + schema: + type: array + items: + type: string + - name: highlightPostTag + in: query + description: A string tag that is appended to hit highlights. Must be set with highlightPreTag. Default is </em>. + schema: + type: string + - name: highlightPreTag + in: query + description: A string tag that is prepended to hit highlights. Must be set with highlightPostTag. Default is <em>. + schema: + type: string + - name: minimumCoverage + in: query + description: Format - double. Format - double. A number between 0 and 100 indicating the percentage of the index that must be covered by a search query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 100. + schema: + type: number + format: double + - name: $orderby + in: query + description: 'The list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, and desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no OrderBy is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.' + schema: + type: array + items: + type: string + - name: queryType + in: query + description: A value that specifies the syntax of the search query. The default is 'simple'. Use 'full' if your query uses the Lucene query syntax. + schema: + enum: + - simple + - full + - semantic + type: string + - name: scoringParameter + in: query + description: 'The list of parameter values to be used in scoring functions (for example, referencePointParameter) using the format name-values. For example, if the scoring profile defines a function with a parameter called ''mylocation'' the parameter string would be "mylocation--122.2,44.8" (without the quotes).' + schema: + type: array + items: + type: string + - name: scoringProfile + in: query + description: The name of a scoring profile to evaluate match scores for matching documents in order to sort the results. + schema: + type: string + - name: searchFields + in: query + description: 'The list of field names to which to scope the full-text search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of each fielded search expression take precedence over any field names listed in this parameter.' + schema: + type: array + items: + type: string + - name: searchMode + in: query + description: A value that specifies whether any or all of the search terms must be matched in order to count the document as a match. + schema: + enum: + - any + - all + type: string + - name: scoringStatistics + in: query + description: 'A value that specifies whether we want to calculate scoring statistics (such as document frequency) globally for more consistent scoring, or locally, for lower latency.' + schema: + enum: + - local + - global + type: string + - name: sessionId + in: query + description: 'A value to be used to create a sticky session, which can help to get more consistent results. As long as the same sessionId is used, a best-effort attempt will be made to target the same replica set. Be wary that reusing the same sessionID values repeatedly can interfere with the load balancing of the requests across replicas and adversely affect the performance of the search service. The value used as sessionId cannot start with a ''_'' character.' + schema: + type: string + - name: $select + in: query + description: 'The list of fields to retrieve. If unspecified, all fields marked as retrievable in the schema are included.' + schema: + type: array + items: + type: string + - name: $skip + in: query + description: 'Format - int32. Format - int32. The number of search results to skip. This value cannot be greater than 100,000. If you need to scan documents in sequence, but cannot use $skip due to this limitation, consider using $orderby on a totally-ordered key and $filter with a range query instead.' + schema: + type: integer + format: int32 + - name: $top + in: query + description: 'Format - int32. Format - int32. The number of search results to retrieve. This can be used in conjunction with $skip to implement client-side paging of search results. If results are truncated due to server-side paging, the response will include a continuation token that can be used to issue another Search request for the next page of results.' + schema: + type: integer + format: int32 + - name: semanticConfiguration + in: query + description: 'The name of the semantic configuration that lists which fields should be used for semantic ranking, captions, highlights, and answers' + schema: + type: string + - name: semanticErrorHandling + in: query + description: 'Allows the user to choose whether a semantic call should fail completely, or to return partial results.' + schema: + enum: + - partial + - fail + type: string + - name: semanticMaxWaitInMilliseconds + in: query + description: Format - int32. Format - int32. Allows the user to set an upper bound on the amount of time it takes for semantic enrichment to finish processing before the request fails. + schema: + minimum: 700 + type: integer + format: int32 + - name: answers + in: query + description: 'This parameter is only valid if the query type is `semantic`. If set, the query returns answers extracted from key passages in the highest ranked documents. The number of answers returned can be configured by appending the pipe character `|` followed by the `count-` option after the answers parameter value, such as `extractive|count-3`. Default count is 1. The confidence threshold can be configured by appending the pipe character `|` followed by the `threshold-` option after the answers parameter value, such as `extractive|threshold-0.9`. Default threshold is 0.7.' + schema: + enum: + - none + - extractive + type: string + - name: captions + in: query + description: 'This parameter is only valid if the query type is `semantic`. If set, the query returns captions extracted from key passages in the highest ranked documents. When Captions is set to `extractive`, highlighting is enabled by default, and can be configured by appending the pipe character `|` followed by the `highlight-` option, such as `extractive|highlight-true`. Defaults to `None`.' + schema: + enum: + - none + - extractive + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + responses: + '200': + description: Response containing documents that match the search criteria. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchDocumentsResult' + example: + '@odata.count': 0 + '@search.coverage': 0 + '@search.facets': { } + '@search.answers': + - score: 0 + key: string + text: string + highlights: string + '@search.nextPageParameters': + count: true + facets: + - string + filter: string + highlight: string + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + queryType: simple + scoringStatistics: local + sessionId: string + scoringParameters: + - string + scoringProfile: string + search: string + searchFields: string + searchMode: any + select: string + skip: 0 + top: 0 + semanticConfiguration: string + semanticErrorHandling: partial + semanticMaxWaitInMilliseconds: 0 + answers: none + captions: none + vectorQueries: + - kind: vector + k: 0 + fields: string + exhaustive: true + vectorFilterMode: postFilter + value: + - '@search.score': 0 + '@search.rerankerScore': 0 + '@search.highlights': { } + '@search.captions': + - text: string + highlights: string + '@odata.nextLink': string + '@search.semanticPartialResponseReason': maxWaitExceeded + '@search.semanticPartialResponseType': baseResults + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.post.search': + post: + tags: + - Documents + summary: Documents_SearchPost + description: Searches for documents in the index. + operationId: Documents_SearchPost + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + requestBody: + description: The definition of the Search request. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchRequest' + example: + count: true + facets: + - string + filter: string + highlight: string + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + queryType: simple + scoringStatistics: local + sessionId: string + scoringParameters: + - string + scoringProfile: string + search: string + searchFields: string + searchMode: any + select: string + skip: 0 + top: 0 + semanticConfiguration: string + semanticErrorHandling: partial + semanticMaxWaitInMilliseconds: 0 + answers: none + captions: none + vectorQueries: + - kind: vector + k: 0 + fields: string + exhaustive: true + vectorFilterMode: postFilter + responses: + '200': + description: Response containing documents that match the search criteria. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchDocumentsResult' + example: + '@odata.count': 0 + '@search.coverage': 0 + '@search.facets': { } + '@search.answers': + - score: 0 + key: string + text: string + highlights: string + '@search.nextPageParameters': + count: true + facets: + - string + filter: string + highlight: string + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + queryType: simple + scoringStatistics: local + sessionId: string + scoringParameters: + - string + scoringProfile: string + search: string + searchFields: string + searchMode: any + select: string + skip: 0 + top: 0 + semanticConfiguration: string + semanticErrorHandling: partial + semanticMaxWaitInMilliseconds: 0 + answers: none + captions: none + vectorQueries: + - kind: vector + k: 0 + fields: string + exhaustive: true + vectorFilterMode: postFilter + value: + - '@search.score': 0 + '@search.rerankerScore': 0 + '@search.highlights': { } + '@search.captions': + - text: string + highlights: string + '@odata.nextLink': string + '@search.semanticPartialResponseReason': maxWaitExceeded + '@search.semanticPartialResponseType': baseResults + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs(''{key}'')': + get: + tags: + - Documents + summary: Documents_Get + description: Retrieves a document from the index. + operationId: Documents_Get + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: key + in: path + description: The key of the document to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: $select + in: query + description: List of field names to retrieve for the document; Any field not retrieved will be missing from the returned document. + schema: + type: array + items: + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + responses: + '200': + description: Response containing the requested document. + content: + application/json: + schema: + $ref: '#/components/schemas/LookupDocument' + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.suggest': + get: + tags: + - Documents + summary: Documents_SuggestGet + description: Suggests documents in the index that match the given partial query text. + operationId: Documents_SuggestGet + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: search + in: query + description: 'The search text to use to suggest documents. Must be at least 1 character, and no more than 100 characters.' + required: true + schema: + type: string + - name: suggesterName + in: query + description: The name of the suggester as specified in the suggesters collection that's part of the index definition. + required: true + schema: + type: string + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: $filter + in: query + description: An OData expression that filters the documents considered for suggestions. + schema: + type: string + - name: fuzzy + in: query + description: 'A value indicating whether to use fuzzy matching for the suggestions query. Default is false. When set to true, the query will find terms even if there''s a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy suggestions queries are slower and consume more resources.' + schema: + type: boolean + - name: highlightPostTag + in: query + description: 'A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting of suggestions is disabled.' + schema: + type: string + - name: highlightPreTag + in: query + description: 'A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting of suggestions is disabled.' + schema: + type: string + - name: minimumCoverage + in: query + description: Format - double. Format - double. A number between 0 and 100 indicating the percentage of the index that must be covered by a suggestions query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80. + schema: + type: number + format: double + - name: $orderby + in: query + description: 'The list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.' + schema: + type: array + items: + type: string + - name: searchFields + in: query + description: The list of field names to search for the specified search text. Target fields must be included in the specified suggester. + schema: + type: array + items: + type: string + - name: $select + in: query + description: 'The list of fields to retrieve. If unspecified, only the key field will be included in the results.' + schema: + type: array + items: + type: string + - name: $top + in: query + description: Format - int32. Format - int32. The number of suggestions to retrieve. The value must be a number between 1 and 100. The default is 5. + schema: + type: integer + format: int32 + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + responses: + '200': + description: Response containing suggested documents that match the partial input. + content: + application/json: + schema: + $ref: '#/components/schemas/SuggestDocumentsResult' + example: + value: + - '@search.text': string + '@search.coverage': 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.post.suggest': + post: + tags: + - Documents + summary: Documents_SuggestPost + description: Suggests documents in the index that match the given partial query text. + operationId: Documents_SuggestPost + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + requestBody: + description: The Suggest request. + content: + application/json: + schema: + $ref: '#/components/schemas/SuggestRequest' + example: + filter: string + fuzzy: true + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + search: string + searchFields: string + select: string + suggesterName: string + top: 0 + responses: + '200': + description: Response containing suggested documents that match the partial input. + content: + application/json: + schema: + $ref: '#/components/schemas/SuggestDocumentsResult' + example: + value: + - '@search.text': string + '@search.coverage': 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.index': + post: + tags: + - Documents + summary: Documents_Index + description: Sends a batch of document write actions to the index. + operationId: Documents_Index + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + requestBody: + description: The batch of index actions. + content: + application/json: + schema: + $ref: '#/components/schemas/IndexBatch' + example: + value: + - '@search.action': upload + responses: + '200': + description: Response containing the status of operations for all actions in the batch. + content: + application/json: + schema: + $ref: '#/components/schemas/IndexDocumentsResult' + example: + value: + - key: string + errorMessage: string + status: true + statusCode: 0 + '207': + description: Response containing the status of operations for all actions in the batch. + content: + application/json: + schema: + $ref: '#/components/schemas/IndexDocumentsResult' + example: + value: + - key: string + errorMessage: string + status: true + statusCode: 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.autocomplete': + get: + tags: + - Documents + summary: Documents_AutocompleteGet + description: Autocompletes incomplete query terms based on input text and matching terms in the index. + operationId: Documents_AutocompleteGet + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: search + in: query + description: The incomplete term which should be auto-completed. + required: true + schema: + type: string + - name: suggesterName + in: query + description: The name of the suggester as specified in the suggesters collection that's part of the index definition. + required: true + schema: + type: string + - name: autocompleteMode + in: query + description: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing auto-completed terms. + schema: + enum: + - oneTerm + - twoTerms + - oneTermWithContext + type: string + - name: $filter + in: query + description: An OData expression that filters the documents used to produce completed terms for the Autocomplete result. + schema: + type: string + - name: fuzzy + in: query + description: 'A value indicating whether to use fuzzy matching for the autocomplete query. Default is false. When set to true, the query will find terms even if there''s a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are slower and consume more resources.' + schema: + type: boolean + - name: highlightPostTag + in: query + description: 'A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting is disabled.' + schema: + type: string + - name: highlightPreTag + in: query + description: 'A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting is disabled.' + schema: + type: string + - name: minimumCoverage + in: query + description: Format - double. Format - double. A number between 0 and 100 indicating the percentage of the index that must be covered by an autocomplete query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80. + schema: + type: number + format: double + - name: searchFields + in: query + description: The list of field names to consider when querying for auto-completed terms. Target fields must be included in the specified suggester. + schema: + type: array + items: + type: string + - name: $top + in: query + description: Format - int32. Format - int32. The number of auto-completed terms to retrieve. This must be a value between 1 and 100. The default is 5. + schema: + type: integer + format: int32 + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + responses: + '200': + description: Response containing suggested query terms that complete the partial input. + content: + application/json: + schema: + $ref: '#/components/schemas/AutocompleteResult' + example: + '@search.coverage': 0 + value: + - text: string + queryPlusText: string + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' + '/indexes(''{index-name}'')/docs/search.post.autocomplete': + post: + tags: + - Documents + summary: Documents_AutocompletePost + description: Autocompletes incomplete query terms based on input text and matching terms in the index. + operationId: Documents_AutocompletePost + parameters: + - name: index-name + in: path + required: true + schema: + type: '' + - name: api-version + in: query + description: Client Api Version. + required: true + schema: + type: string + - name: x-ms-client-request-id + in: header + description: Format - uuid. Format - uuid. The tracking ID sent with the request to help with debugging. + schema: + type: string + requestBody: + description: The definition of the Autocomplete request. + content: + application/json: + schema: + $ref: '#/components/schemas/AutocompleteRequest' + example: + search: string + autocompleteMode: oneTerm + filter: string + fuzzy: true + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + searchFields: string + suggesterName: string + top: 0 + responses: + '200': + description: Response containing suggested query terms that complete the partial input. + content: + application/json: + schema: + $ref: '#/components/schemas/AutocompleteResult' + example: + '@search.coverage': 0 + value: + - text: string + queryPlusText: string + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/SearchError' +components: + schemas: + SuggestDocumentsResult: + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/SuggestResult' + description: The sequence of results returned by the query. + readOnly: true + x-ms-client-name: Results + '@search.coverage': + type: number + description: 'A value indicating the percentage of the index that was included in the query, or null if minimumCoverage was not set in the request.' + format: double + readOnly: true + x-ms-client-name: Coverage + description: Response containing suggestion query results from an index. + SuggestResult: + required: + - '@search.text' + properties: + '@search.text': + type: string + description: The text of the suggestion result. + readOnly: true + x-ms-client-name: Text + description: 'A result containing a document found by a suggestion query, plus associated metadata.' + FacetResult: + properties: + count: + type: integer + description: The approximate count of documents falling within the bucket described by this facet. + format: int64 + readOnly: true + description: A single bucket of a facet query result. Reports the number of documents with a field value falling within a particular range or having a particular value or interval. + SearchDocumentsResult: + required: + - value + properties: + '@odata.count': + type: integer + description: 'The total count of results found by the search operation, or null if the count was not requested. If present, the count may be greater than the number of results in this response. This can happen if you use the $top or $skip parameters, or if the query can''t return all the requested documents in a single response.' + format: int64 + readOnly: true + x-ms-client-name: Count + '@search.coverage': + type: number + description: 'A value indicating the percentage of the index that was included in the query, or null if minimumCoverage was not specified in the request.' + format: double + readOnly: true + x-ms-client-name: Coverage + '@search.facets': + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/FacetResult' + description: 'The facet query results for the search operation, organized as a collection of buckets for each faceted field; null if the query did not include any facet expressions.' + readOnly: true + x-ms-client-name: Facets + '@search.answers': + type: array + items: + $ref: '#/components/schemas/AnswerResult' + description: The answers query results for the search operation; null if the answers query parameter was not specified or set to 'none'. + readOnly: true + x-ms-client-name: Answers + x-nullable: true + '@search.nextPageParameters': + $ref: '#/components/schemas/SearchRequest' + value: + type: array + items: + $ref: '#/components/schemas/SearchResult' + description: The sequence of results returned by the query. + readOnly: true + x-ms-client-name: Results + '@odata.nextLink': + type: string + description: Continuation URL returned when the query can't return all the requested results in a single response. You can use this URL to formulate another GET or POST Search request to get the next part of the search response. Make sure to use the same verb (GET or POST) as the request that produced this response. + readOnly: true + x-ms-client-name: NextLink + '@search.semanticPartialResponseReason': + $ref: '#/components/schemas/SemanticPartialResponseReason' + '@search.semanticPartialResponseType': + $ref: '#/components/schemas/SemanticPartialResponseType' + description: Response containing search results from an index. + SearchResult: + required: + - '@search.score' + properties: + '@search.score': + type: number + description: The relevance score of the document compared to other documents returned by the query. + format: double + readOnly: true + x-ms-client-name: Score + x-nullable: false + '@search.rerankerScore': + type: number + description: The relevance score computed by the semantic ranker for the top search results. Search results are sorted by the RerankerScore first and then by the Score. RerankerScore is only returned for queries of type `semantic`. + format: double + readOnly: true + x-ms-client-name: RerankerScore + x-nullable: true + '@search.highlights': + type: object + additionalProperties: + type: array + items: + type: string + description: 'Text fragments from the document that indicate the matching search terms, organized by each applicable field; null if hit highlighting was not enabled for the query.' + readOnly: true + x-ms-client-name: Highlights + '@search.captions': + type: array + items: + $ref: '#/components/schemas/CaptionResult' + description: Captions are the most representative passages from the document relatively to the search query. They are often used as document summary. Captions are only returned for queries of type `semantic`. + readOnly: true + x-ms-client-name: Captions + x-nullable: true + description: 'Contains a document found by a search query, plus associated metadata.' + LookupDocument: + type: object + description: A document retrieved via a document lookup operation. + IndexBatch: + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/IndexAction' + description: The actions in the batch. + x-ms-client-name: Actions + description: Contains a batch of document write actions to send to the index. + example: + value: + - '@search.action': upload + IndexAction: + properties: + '@search.action': + enum: + - upload + - merge + - mergeOrUpload + - delete + type: string + description: The operation to perform on a document in an indexing batch. + x-ms-enum: + name: IndexActionType + modelAsString: false + values: + - value: upload + name: Upload + description: Inserts the document into the index if it is new and updates it if it exists. All fields are replaced in the update case. + - value: merge + name: Merge + description: 'Merges the specified field values with an existing document. If the document does not exist, the merge will fail. Any field you specify in a merge will replace the existing field in the document. This also applies to collections of primitive and complex types.' + - value: mergeOrUpload + name: MergeOrUpload + description: 'Behaves like merge if a document with the given key already exists in the index. If the document does not exist, it behaves like upload with a new document.' + - value: delete + name: Delete + description: 'Removes the specified document from the index. Any field you specify in a delete operation other than the key field will be ignored. If you want to remove an individual field from a document, use merge instead and set the field explicitly to null.' + x-ms-client-name: ActionType + x-nullable: false + description: Represents an index action that operates on a document. + IndexingResult: + required: + - key + - status + - statusCode + properties: + key: + type: string + description: The key of a document that was in the indexing request. + readOnly: true + errorMessage: + type: string + description: The error message explaining why the indexing operation failed for the document identified by the key; null if indexing succeeded. + readOnly: true + status: + type: boolean + description: A value indicating whether the indexing operation succeeded for the document identified by the key. + readOnly: true + x-ms-client-name: Succeeded + x-nullable: false + statusCode: + type: integer + description: 'The status code of the indexing operation. Possible values include: 200 for a successful update or delete, 201 for successful document creation, 400 for a malformed input document, 404 for document not found, 409 for a version conflict, 422 when the index is temporarily unavailable, or 503 for when the service is too busy.' + format: int32 + readOnly: true + x-nullable: false + description: Status of an indexing operation for a single document. + IndexDocumentsResult: + required: + - value + properties: + value: + type: array + items: + $ref: '#/components/schemas/IndexingResult' + description: The list of status information for each document in the indexing request. + readOnly: true + x-ms-client-name: Results + description: Response containing the status of operations for all documents in the indexing request. + SearchMode: + enum: + - any + - all + type: string + description: Specifies whether any or all of the search terms must be matched in order to count the document as a match. + x-ms-enum: + name: SearchMode + modelAsString: false + values: + - value: any + name: Any + description: Any of the search terms must be matched in order to count the document as a match. + - value: all + name: All + description: All of the search terms must be matched in order to count the document as a match. + QueryType: + enum: + - simple + - full + - semantic + type: string + description: Specifies the syntax of the search query. The default is 'simple'. Use 'full' if your query uses the Lucene query syntax. + x-ms-enum: + name: QueryType + modelAsString: false + values: + - value: simple + name: Simple + description: 'Uses the simple query syntax for searches. Search text is interpreted using a simple query language that allows for symbols such as +, * and "". Queries are evaluated across all searchable fields by default, unless the searchFields parameter is specified.' + - value: full + name: Full + description: 'Uses the full Lucene query syntax for searches. Search text is interpreted using the Lucene query language which allows field-specific and weighted searches, as well as other advanced features.' + - value: semantic + name: Semantic + description: Best suited for queries expressed in natural language as opposed to keywords. Improves precision of search results by re-ranking the top search results using a ranking model trained on the Web corpus. + VectorQuery: + required: + - kind + type: object + properties: + kind: + $ref: '#/components/schemas/VectorQueryKind' + k: + type: integer + description: Number of nearest neighbors to return as top hits. + format: int32 + fields: + type: string + description: Vector Fields of type Collection(Edm.Single) to be included in the vector searched. + exhaustive: + type: boolean + description: 'When true, triggers an exhaustive k-nearest neighbor search across all vectors within the vector index. Useful for scenarios where exact matches are critical, such as determining ground truth values.' + description: The query parameters for vector and hybrid search queries. + discriminator: + propertyName: kind + RawVectorQuery: + required: + - vector + type: object + allOf: + - $ref: '#/components/schemas/VectorQuery' + properties: + vector: + type: array + items: + type: number + format: float + description: The vector representation of a search query. + description: The query parameters to use for vector search when a raw vector value is provided. + x-ms-discriminator-value: vector + VectorQueryKind: + enum: + - vector + type: string + description: The kind of vector query being performed. + x-ms-enum: + name: VectorQueryKind + modelAsString: true + values: + - value: vector + name: Vector + description: Vector query where a raw vector value is provided. + VectorFilterMode: + enum: + - postFilter + - preFilter + type: string + description: Determines whether or not filters are applied before or after the vector search is performed. + x-ms-enum: + name: VectorFilterMode + modelAsString: true + values: + - value: postFilter + name: PostFilter + description: 'The filter will be applied after the candidate set of vector results is returned. Depending on the filter selectivity, this can result in fewer results than requested by the parameter ''k''.' + - value: preFilter + name: PreFilter + description: The filter will be applied before the search query. + ScoringStatistics: + enum: + - local + - global + type: string + description: 'A value that specifies whether we want to calculate scoring statistics (such as document frequency) globally for more consistent scoring, or locally, for lower latency. The default is ''local''. Use ''global'' to aggregate scoring statistics globally before scoring. Using global scoring statistics can increase latency of search queries.' + x-ms-enum: + name: ScoringStatistics + modelAsString: false + values: + - value: local + name: Local + description: The scoring statistics will be calculated locally for lower latency. + - value: global + name: Global + description: The scoring statistics will be calculated globally for more consistent scoring. + AutocompleteMode: + enum: + - oneTerm + - twoTerms + - oneTermWithContext + type: string + description: Specifies the mode for Autocomplete. The default is 'oneTerm'. Use 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context in producing autocomplete terms. + x-ms-enum: + name: AutocompleteMode + modelAsString: false + values: + - value: oneTerm + name: OneTerm + description: 'Only one term is suggested. If the query has two terms, only the last term is completed. For example, if the input is ''washington medic'', the suggested terms could include ''medicaid'', ''medicare'', and ''medicine''.' + - value: twoTerms + name: TwoTerms + description: 'Matching two-term phrases in the index will be suggested. For example, if the input is ''medic'', the suggested terms could include ''medicare coverage'' and ''medical assistant''.' + - value: oneTermWithContext + name: OneTermWithContext + description: 'Completes the last term in a query with two or more terms, where the last two terms are a phrase that exists in the index. For example, if the input is ''washington medic'', the suggested terms could include ''washington medicaid'' and ''washington medical''.' + SearchRequest: + properties: + count: + type: boolean + description: A value that specifies whether to fetch the total count of results. Default is false. Setting this value to true may have a performance impact. Note that the count returned is an approximation. + x-ms-client-name: IncludeTotalResultCount + facets: + type: array + items: + type: string + description: 'The list of facet expressions to apply to the search query. Each facet expression contains a field name, optionally followed by a comma-separated list of name:value pairs.' + externalDocs: + url: https://docs.microsoft.com/rest/api/searchservice/Search-Documents + filter: + type: string + description: The OData $filter expression to apply to the search query. + externalDocs: + url: https://docs.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search + highlight: + type: string + description: The comma-separated list of field names to use for hit highlights. Only searchable fields can be used for hit highlighting. + x-ms-client-name: HighlightFields + highlightPostTag: + type: string + description: A string tag that is appended to hit highlights. Must be set with highlightPreTag. Default is </em>. + highlightPreTag: + type: string + description: A string tag that is prepended to hit highlights. Must be set with highlightPostTag. Default is <em>. + minimumCoverage: + type: number + description: A number between 0 and 100 indicating the percentage of the index that must be covered by a search query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 100. + format: double + orderby: + type: string + description: 'The comma-separated list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.' + x-ms-client-name: OrderBy + queryType: + $ref: '#/components/schemas/QueryType' + scoringStatistics: + $ref: '#/components/schemas/ScoringStatistics' + sessionId: + type: string + description: 'A value to be used to create a sticky session, which can help getting more consistent results. As long as the same sessionId is used, a best-effort attempt will be made to target the same replica set. Be wary that reusing the same sessionID values repeatedly can interfere with the load balancing of the requests across replicas and adversely affect the performance of the search service. The value used as sessionId cannot start with a ''_'' character.' + scoringParameters: + type: array + items: + type: string + description: 'The list of parameter values to be used in scoring functions (for example, referencePointParameter) using the format name-values. For example, if the scoring profile defines a function with a parameter called ''mylocation'' the parameter string would be "mylocation--122.2,44.8" (without the quotes).' + scoringProfile: + type: string + description: The name of a scoring profile to evaluate match scores for matching documents in order to sort the results. + search: + type: string + description: A full-text search query expression; Use "*" or omit this parameter to match all documents. + x-ms-client-name: SearchText + searchFields: + type: string + description: 'The comma-separated list of field names to which to scope the full-text search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of each fielded search expression take precedence over any field names listed in this parameter.' + searchMode: + $ref: '#/components/schemas/SearchMode' + select: + type: string + description: 'The comma-separated list of fields to retrieve. If unspecified, all fields marked as retrievable in the schema are included.' + skip: + type: integer + description: 'The number of search results to skip. This value cannot be greater than 100,000. If you need to scan documents in sequence, but cannot use skip due to this limitation, consider using orderby on a totally-ordered key and filter with a range query instead.' + format: int32 + top: + type: integer + description: 'The number of search results to retrieve. This can be used in conjunction with $skip to implement client-side paging of search results. If results are truncated due to server-side paging, the response will include a continuation token that can be used to issue another Search request for the next page of results.' + format: int32 + semanticConfiguration: + type: string + description: The name of a semantic configuration that will be used when processing documents for queries of type semantic. + semanticErrorHandling: + $ref: '#/components/schemas/SemanticErrorHandling' + semanticMaxWaitInMilliseconds: + minimum: 700 + type: integer + description: Allows the user to set an upper bound on the amount of time it takes for semantic enrichment to finish processing before the request fails. + format: int32 + x-nullable: true + answers: + $ref: '#/components/schemas/Answers' + captions: + $ref: '#/components/schemas/Captions' + vectorQueries: + type: array + items: + $ref: '#/components/schemas/VectorQuery' + description: The query parameters for vector and hybrid search queries. + vectorFilterMode: + $ref: '#/components/schemas/VectorFilterMode' + description: 'Parameters for filtering, sorting, faceting, paging, and other search query behaviors.' + example: + count: true + facets: + - string + filter: string + highlight: string + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + queryType: simple + scoringStatistics: local + sessionId: string + scoringParameters: + - string + scoringProfile: string + search: string + searchFields: string + searchMode: any + select: string + skip: 0 + top: 0 + semanticConfiguration: string + semanticErrorHandling: partial + semanticMaxWaitInMilliseconds: 0 + answers: none + captions: none + vectorQueries: + - kind: vector + k: 0 + fields: string + exhaustive: true + vectorFilterMode: postFilter + SuggestRequest: + required: + - search + - suggesterName + properties: + filter: + type: string + description: An OData expression that filters the documents considered for suggestions. + externalDocs: + url: https://docs.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search + fuzzy: + type: boolean + description: 'A value indicating whether to use fuzzy matching for the suggestion query. Default is false. When set to true, the query will find suggestions even if there''s a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy suggestion searches are slower and consume more resources.' + x-ms-client-name: UseFuzzyMatching + highlightPostTag: + type: string + description: 'A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting of suggestions is disabled.' + highlightPreTag: + type: string + description: 'A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting of suggestions is disabled.' + minimumCoverage: + type: number + description: A number between 0 and 100 indicating the percentage of the index that must be covered by a suggestion query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80. + format: double + orderby: + type: string + description: 'The comma-separated list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.' + x-ms-client-name: OrderBy + search: + type: string + description: 'The search text to use to suggest documents. Must be at least 1 character, and no more than 100 characters.' + x-ms-client-name: SearchText + searchFields: + type: string + description: The comma-separated list of field names to search for the specified search text. Target fields must be included in the specified suggester. + select: + type: string + description: 'The comma-separated list of fields to retrieve. If unspecified, only the key field will be included in the results.' + suggesterName: + type: string + description: The name of the suggester as specified in the suggesters collection that's part of the index definition. + top: + type: integer + description: The number of suggestions to retrieve. This must be a value between 1 and 100. The default is 5. + format: int32 + description: 'Parameters for filtering, sorting, fuzzy matching, and other suggestions query behaviors.' + example: + filter: string + fuzzy: true + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + orderby: string + search: string + searchFields: string + select: string + suggesterName: string + top: 0 + AutocompleteRequest: + required: + - search + - suggesterName + properties: + search: + type: string + description: The search text on which to base autocomplete results. + x-ms-client-name: SearchText + autocompleteMode: + $ref: '#/components/schemas/AutocompleteMode' + filter: + type: string + description: An OData expression that filters the documents used to produce completed terms for the Autocomplete result. + externalDocs: + url: https://docs.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search + fuzzy: + type: boolean + description: 'A value indicating whether to use fuzzy matching for the autocomplete query. Default is false. When set to true, the query will autocomplete terms even if there''s a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are slower and consume more resources.' + x-ms-client-name: UseFuzzyMatching + highlightPostTag: + type: string + description: 'A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting is disabled.' + highlightPreTag: + type: string + description: 'A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting is disabled.' + minimumCoverage: + type: number + description: A number between 0 and 100 indicating the percentage of the index that must be covered by an autocomplete query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80. + format: double + searchFields: + type: string + description: The comma-separated list of field names to consider when querying for auto-completed terms. Target fields must be included in the specified suggester. + suggesterName: + type: string + description: The name of the suggester as specified in the suggesters collection that's part of the index definition. + top: + type: integer + description: The number of auto-completed terms to retrieve. This must be a value between 1 and 100. The default is 5. + format: int32 + description: 'Parameters for fuzzy matching, and other autocomplete query behaviors.' + example: + search: string + autocompleteMode: oneTerm + filter: string + fuzzy: true + highlightPostTag: string + highlightPreTag: string + minimumCoverage: 0 + searchFields: string + suggesterName: string + top: 0 + AutocompleteResult: + required: + - value + properties: + '@search.coverage': + type: number + description: 'A value indicating the percentage of the index that was considered by the autocomplete request, or null if minimumCoverage was not specified in the request.' + format: double + readOnly: true + x-ms-client-name: Coverage + value: + type: array + items: + $ref: '#/components/schemas/AutocompleteItem' + description: The list of returned Autocompleted items. + readOnly: true + x-ms-client-name: Results + description: The result of Autocomplete query. + AutocompleteItem: + required: + - text + - queryPlusText + properties: + text: + type: string + description: The completed term. + readOnly: true + queryPlusText: + type: string + description: The query along with the completed term. + readOnly: true + description: The result of Autocomplete requests. + SearchError: + required: + - message + properties: + code: + type: string + description: One of a server-defined set of error codes. + readOnly: true + message: + type: string + description: A human-readable representation of the error. + readOnly: true + details: + type: array + items: + $ref: '#/components/schemas/SearchError' + description: An array of details about specific errors that led to this reported error. + readOnly: true + description: Describes an error condition for the API. + AnswerResult: + properties: + score: + type: number + description: The score value represents how relevant the answer is to the query relative to other answers returned for the query. + format: double + readOnly: true + key: + type: string + description: The key of the document the answer was extracted from. + readOnly: true + text: + type: string + description: The text passage extracted from the document contents as the answer. + readOnly: true + highlights: + type: string + description: Same text passage as in the Text property with highlighted text phrases most relevant to the query. + readOnly: true + x-nullable: true + description: An answer is a text passage extracted from the contents of the most relevant documents that matched the query. Answers are extracted from the top search results. Answer candidates are scored and the top answers are selected. + x-ms-client-name: QueryAnswerResult + CaptionResult: + properties: + text: + type: string + description: A representative text passage extracted from the document most relevant to the search query. + readOnly: true + highlights: + type: string + description: Same text passage as in the Text property with highlighted phrases most relevant to the query. + readOnly: true + x-nullable: true + description: Captions are the most representative passages from the document relatively to the search query. They are often used as document summary. Captions are only returned for queries of type `semantic`. + x-ms-client-name: QueryCaptionResult + SemanticPartialResponseReason: + enum: + - maxWaitExceeded + - capacityOverloaded + - transient + type: string + description: Reason that a partial response was returned for a semantic ranking request. + x-ms-enum: + name: SemanticErrorReason + modelAsString: true + values: + - value: maxWaitExceeded + name: MaxWaitExceeded + description: If `semanticMaxWaitInMilliseconds` was set and the semantic processing duration exceeded that value. Only the base results were returned. + - value: capacityOverloaded + name: CapacityOverloaded + description: The request was throttled. Only the base results were returned. + - value: transient + name: Transient + description: At least one step of the semantic process failed. + SemanticPartialResponseType: + enum: + - baseResults + - rerankedResults + type: string + description: Type of partial response that was returned for a semantic ranking request. + x-ms-enum: + name: SemanticSearchResultsType + modelAsString: true + values: + - value: baseResults + name: BaseResults + description: Results without any semantic enrichment or reranking. + - value: rerankedResults + name: RerankedResults + description: 'Results have been reranked with the reranker model and will include semantic captions. They will not include any answers, answers highlights or caption highlights.' + SemanticErrorHandling: + enum: + - partial + - fail + type: string + description: 'Allows the user to choose whether a semantic call should fail completely, or to return partial results.' + x-ms-enum: + name: SemanticErrorMode + modelAsString: true + values: + - value: partial + name: Partial + description: 'If the semantic processing fails, partial results still return. The definition of partial results depends on what semantic step failed and what was the reason for failure.' + - value: fail + name: Fail + description: 'If there is an exception during the semantic processing step, the query will fail and return the appropriate HTTP code depending on the error.' + Answers: + enum: + - none + - extractive + type: string + description: 'This parameter is only valid if the query type is `semantic`. If set, the query returns answers extracted from key passages in the highest ranked documents. The number of answers returned can be configured by appending the pipe character `|` followed by the `count-` option after the answers parameter value, such as `extractive|count-3`. Default count is 1. The confidence threshold can be configured by appending the pipe character `|` followed by the `threshold-` option after the answers parameter value, such as `extractive|threshold-0.9`. Default threshold is 0.7.' + x-ms-enum: + name: QueryAnswerType + modelAsString: true + values: + - value: none + name: None + description: Do not return answers for the query. + - value: extractive + name: Extractive + description: Extracts answer candidates from the contents of the documents returned in response to a query expressed as a question in natural language. + Captions: + enum: + - none + - extractive + type: string + description: 'This parameter is only valid if the query type is `semantic`. If set, the query returns captions extracted from key passages in the highest ranked documents. When Captions is set to `extractive`, highlighting is enabled by default, and can be configured by appending the pipe character `|` followed by the `highlight-` option, such as `extractive|highlight-true`. Defaults to `None`.' + x-ms-enum: + name: QueryCaptionType + modelAsString: true + values: + - value: none + name: None + description: Do not return captions for the query. + - value: extractive + name: Extractive + description: Extracts captions from the matching documents that contain passages relevant to the search query. + Docs-countGet200ApplicationJsonResponse: + type: integer + format: int64 + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-index-2024-07-01-api-spec.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-index-2024-07-01-api-spec.json new file mode 100644 index 000000000..59d5e55a9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-index-2024-07-01-api-spec.json @@ -0,0 +1,2112 @@ +{ + "swagger": "2.0", + "info": { + "title": "SearchIndexClient", + "description": "Client that can be used to query an index and upload, merge, or delete documents.", + "version": "2024-07-01", + "x-ms-code-generation-settings": { + "useDateTimeOffset": true, + "syncMethods": "None" + } + }, + "x-ms-parameterized-host": { + "hostTemplate": "{endpoint}/indexes('{indexName}')", + "useSchemePrefix": false, + "parameters": [ + { + "$ref": "#/parameters/EndpointParameter" + }, + { + "$ref": "#/parameters/IndexNameParameter" + } + ] + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/docs/$count": { + "get": { + "tags": [ + "Documents" + ], + "operationId": "Documents_Count", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Count-Documents" + }, + "x-ms-examples": { + "SearchIndexCountDocuments": { + "$ref": "./examples/SearchIndexCountDocuments.json" + } + }, + "description": "Queries the number of documents in the index.", + "parameters": [ + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "type": "integer", + "format": "int64" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs": { + "get": { + "tags": [ + "Documents" + ], + "operationId": "Documents_SearchGet", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Search-Documents" + }, + "x-ms-examples": { + "SearchIndexSearchDocumentsGet": { + "$ref": "./examples/SearchIndexSearchDocumentsGet.json" + }, + "SearchIndexSearchDocumentsSemanticGet": { + "$ref": "./examples/SearchIndexSearchDocumentsSemanticGet.json" + } + }, + "description": "Searches for documents in the index.", + "parameters": [ + { + "name": "search", + "in": "query", + "type": "string", + "description": "A full-text search query expression; Use \"*\" or omit this parameter to match all documents.", + "x-ms-client-name": "SearchText" + }, + { + "name": "$count", + "in": "query", + "type": "boolean", + "description": "A value that specifies whether to fetch the total count of results. Default is false. Setting this value to true may have a performance impact. Note that the count returned is an approximation.", + "x-nullable": false, + "x-ms-client-name": "IncludeTotalResultCount", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "facet", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "The list of facet expressions to apply to the search query. Each facet expression contains a field name, optionally followed by a comma-separated list of name:value pairs.", + "x-ms-client-name": "Facets", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "$filter", + "in": "query", + "type": "string", + "description": "The OData $filter expression to apply to the search query.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "highlight", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of field names to use for hit highlights. Only searchable fields can be used for hit highlighting.", + "x-ms-client-name": "HighlightFields", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "highlightPostTag", + "in": "query", + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. Default is </em>.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "highlightPreTag", + "in": "query", + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. Default is <em>.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "minimumCoverage", + "in": "query", + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by a search query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 100.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "$orderby", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, and desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no OrderBy is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.", + "x-ms-client-name": "OrderBy", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "queryType", + "in": "query", + "type": "string", + "enum": [ + "simple", + "full", + "semantic" + ], + "x-ms-enum": { + "name": "QueryType", + "modelAsString": false, + "values": [ + { + "value": "simple", + "name": "Simple", + "description": "Uses the simple query syntax for searches. Search text is interpreted using a simple query language that allows for symbols such as +, * and \"\". Queries are evaluated across all searchable fields by default, unless the searchFields parameter is specified." + }, + { + "value": "full", + "name": "Full", + "description": "Uses the full Lucene query syntax for searches. Search text is interpreted using the Lucene query language which allows field-specific and weighted searches, as well as other advanced features." + }, + { + "value": "semantic", + "name": "Semantic", + "description": "Best suited for queries expressed in natural language as opposed to keywords. Improves precision of search results by re-ranking the top search results using a ranking model trained on the Web corpus." + } + ] + }, + "x-nullable": false, + "description": "A value that specifies the syntax of the search query. The default is 'simple'. Use 'full' if your query uses the Lucene query syntax.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "scoringParameter", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "x-ms-client-name": "ScoringParameters", + "description": "The list of parameter values to be used in scoring functions (for example, referencePointParameter) using the format name-values. For example, if the scoring profile defines a function with a parameter called 'mylocation' the parameter string would be \"mylocation--122.2,44.8\" (without the quotes).", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "scoringProfile", + "in": "query", + "type": "string", + "description": "The name of a scoring profile to evaluate match scores for matching documents in order to sort the results.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "searchFields", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of field names to which to scope the full-text search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of each fielded search expression take precedence over any field names listed in this parameter.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "searchMode", + "in": "query", + "type": "string", + "enum": [ + "any", + "all" + ], + "x-ms-enum": { + "name": "SearchMode", + "modelAsString": false, + "values": [ + { + "value": "any", + "name": "Any", + "description": "Any of the search terms must be matched in order to count the document as a match." + }, + { + "value": "all", + "name": "All", + "description": "All of the search terms must be matched in order to count the document as a match." + } + ] + }, + "x-nullable": false, + "description": "A value that specifies whether any or all of the search terms must be matched in order to count the document as a match.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "scoringStatistics", + "in": "query", + "type": "string", + "enum": [ + "local", + "global" + ], + "x-ms-enum": { + "name": "ScoringStatistics", + "modelAsString": false, + "values": [ + { + "value": "local", + "name": "Local", + "description": "The scoring statistics will be calculated locally for lower latency." + }, + { + "value": "global", + "name": "Global", + "description": "The scoring statistics will be calculated globally for more consistent scoring." + } + ] + }, + "x-nullable": false, + "description": "A value that specifies whether we want to calculate scoring statistics (such as document frequency) globally for more consistent scoring, or locally, for lower latency.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "sessionId", + "in": "query", + "type": "string", + "description": "A value to be used to create a sticky session, which can help to get more consistent results. As long as the same sessionId is used, a best-effort attempt will be made to target the same replica set. Be wary that reusing the same sessionID values repeatedly can interfere with the load balancing of the requests across replicas and adversely affect the performance of the search service. The value used as sessionId cannot start with a '_' character.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "$select", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of fields to retrieve. If unspecified, all fields marked as retrievable in the schema are included.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "$skip", + "in": "query", + "type": "integer", + "format": "int32", + "description": "The number of search results to skip. This value cannot be greater than 100,000. If you need to scan documents in sequence, but cannot use $skip due to this limitation, consider using $orderby on a totally-ordered key and $filter with a range query instead.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "$top", + "in": "query", + "type": "integer", + "format": "int32", + "description": "The number of search results to retrieve. This can be used in conjunction with $skip to implement client-side paging of search results. If results are truncated due to server-side paging, the response will include a continuation token that can be used to issue another Search request for the next page of results.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "name": "semanticConfiguration", + "in": "query", + "type": "string", + "description": "The name of the semantic configuration that lists which fields should be used for semantic ranking, captions, highlights, and answers", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "semanticErrorHandling", + "in": "query", + "type": "string", + "enum": [ + "partial", + "fail" + ], + "x-ms-enum": { + "name": "SemanticErrorMode", + "modelAsString": true, + "values": [ + { + "value": "partial", + "name": "Partial", + "description": "If the semantic processing fails, partial results still return. The definition of partial results depends on what semantic step failed and what was the reason for failure." + }, + { + "value": "fail", + "name": "Fail", + "description": "If there is an exception during the semantic processing step, the query will fail and return the appropriate HTTP code depending on the error." + } + ] + }, + "description": "Allows the user to choose whether a semantic call should fail completely, or to return partial results (default).", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "semanticMaxWaitInMilliseconds", + "in": "query", + "type": "integer", + "format": "int32", + "minimum": 700, + "description": "Allows the user to set an upper bound on the amount of time it takes for semantic enrichment to finish processing before the request fails.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "answers", + "in": "query", + "type": "string", + "enum": [ + "none", + "extractive" + ], + "x-ms-enum": { + "name": "QueryAnswerType", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Do not return answers for the query." + }, + { + "value": "extractive", + "name": "Extractive", + "description": "Extracts answer candidates from the contents of the documents returned in response to a query expressed as a question in natural language." + } + ] + }, + "description": "This parameter is only valid if the query type is `semantic`. If set, the query returns answers extracted from key passages in the highest ranked documents. The number of answers returned can be configured by appending the pipe character `|` followed by the `count-` option after the answers parameter value, such as `extractive|count-3`. Default count is 1. The confidence threshold can be configured by appending the pipe character `|` followed by the `threshold-` option after the answers parameter value, such as `extractive|threshold-0.9`. Default threshold is 0.7.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "captions", + "in": "query", + "type": "string", + "enum": [ + "none", + "extractive" + ], + "x-ms-enum": { + "name": "QueryCaptionType", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Do not return captions for the query." + }, + { + "value": "extractive", + "name": "Extractive", + "description": "Extracts captions from the matching documents that contain passages relevant to the search query." + } + ] + }, + "description": "This parameter is only valid if the query type is `semantic`. If set, the query returns captions extracted from key passages in the highest ranked documents. When Captions is set to `extractive`, highlighting is enabled by default, and can be configured by appending the pipe character `|` followed by the `highlight-` option, such as `extractive|highlight-true`. Defaults to `None`.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + }, + { + "name": "semanticQuery", + "in": "query", + "type": "string", + "description": "Allows setting a separate search query that will be solely used for semantic reranking, semantic captions and semantic answers. Is useful for scenarios where there is a need to use different queries between the base retrieval and ranking phase, and the L2 semantic phase.", + "x-ms-parameter-grouping": { + "name": "SearchOptions" + } + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing documents that match the search criteria.", + "schema": { + "$ref": "#/definitions/SearchDocumentsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.post.search": { + "post": { + "tags": [ + "Documents" + ], + "operationId": "Documents_SearchPost", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Search-Documents" + }, + "x-ms-examples": { + "SearchIndexSearchDocumentsPost": { + "$ref": "./examples/SearchIndexSearchDocumentsPost.json" + }, + "SearchIndexSearchDocumentsSemanticPost": { + "$ref": "./examples/SearchIndexSearchDocumentsSemanticPost.json" + } + }, + "description": "Searches for documents in the index.", + "parameters": [ + { + "name": "searchRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchRequest", + "description": "The Search request." + }, + "description": "The definition of the Search request." + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing documents that match the search criteria.", + "schema": { + "$ref": "#/definitions/SearchDocumentsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs('{key}')": { + "get": { + "tags": [ + "Documents" + ], + "operationId": "Documents_Get", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/lookup-document" + }, + "x-ms-examples": { + "SearchIndexGetDocument": { + "$ref": "./examples/SearchIndexGetDocument.json" + } + }, + "description": "Retrieves a document from the index.", + "parameters": [ + { + "name": "key", + "in": "path", + "required": true, + "description": "The key of the document to retrieve.", + "type": "string" + }, + { + "name": "$select", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "List of field names to retrieve for the document; Any field not retrieved will be missing from the returned document.", + "x-ms-client-name": "SelectedFields" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + } + ], + "responses": { + "200": { + "description": "Response containing the requested document.", + "schema": { + "$ref": "#/definitions/LookupDocument" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.suggest": { + "get": { + "tags": [ + "Documents" + ], + "operationId": "Documents_SuggestGet", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/suggestions" + }, + "x-ms-examples": { + "SearchIndexSuggestDocumentsGet": { + "$ref": "./examples/SearchIndexSuggestDocumentsGet.json" + } + }, + "description": "Suggests documents in the index that match the given partial query text.", + "parameters": [ + { + "name": "search", + "in": "query", + "required": true, + "type": "string", + "description": "The search text to use to suggest documents. Must be at least 1 character, and no more than 100 characters.", + "x-ms-client-name": "SearchText" + }, + { + "name": "suggesterName", + "in": "query", + "required": true, + "type": "string", + "description": "The name of the suggester as specified in the suggesters collection that's part of the index definition." + }, + { + "name": "$filter", + "in": "query", + "type": "string", + "description": "An OData expression that filters the documents considered for suggestions.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "fuzzy", + "in": "query", + "type": "boolean", + "description": "A value indicating whether to use fuzzy matching for the suggestions query. Default is false. When set to true, the query will find terms even if there's a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy suggestions queries are slower and consume more resources.", + "x-ms-client-name": "UseFuzzyMatching", + "x-nullable": false, + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "highlightPostTag", + "in": "query", + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting of suggestions is disabled.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "highlightPreTag", + "in": "query", + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting of suggestions is disabled.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "minimumCoverage", + "in": "query", + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by a suggestions query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "$orderby", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "x-ms-client-name": "OrderBy", + "description": "The list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "searchFields", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of field names to search for the specified search text. Target fields must be included in the specified suggester.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "$select", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of fields to retrieve. If unspecified, only the key field will be included in the results.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "name": "$top", + "in": "query", + "type": "integer", + "format": "int32", + "description": "The number of suggestions to retrieve. The value must be a number between 1 and 100. The default is 5.", + "x-ms-parameter-grouping": { + "name": "SuggestOptions" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing suggested documents that match the partial input.", + "schema": { + "$ref": "#/definitions/SuggestDocumentsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.post.suggest": { + "post": { + "tags": [ + "Documents" + ], + "operationId": "Documents_SuggestPost", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/suggestions" + }, + "x-ms-examples": { + "SearchIndexSuggestDocumentsPost": { + "$ref": "./examples/SearchIndexSuggestDocumentsPost.json" + } + }, + "description": "Suggests documents in the index that match the given partial query text.", + "parameters": [ + { + "name": "suggestRequest", + "in": "body", + "required": true, + "description": "The Suggest request.", + "schema": { + "$ref": "#/definitions/SuggestRequest" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing suggested documents that match the partial input.", + "schema": { + "$ref": "#/definitions/SuggestDocumentsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.index": { + "post": { + "tags": [ + "Documents" + ], + "operationId": "Documents_Index", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/addupdate-or-delete-documents" + }, + "x-ms-examples": { + "SearchIndexIndexDocuments": { + "$ref": "./examples/SearchIndexIndexDocuments.json" + } + }, + "description": "Sends a batch of document write actions to the index.", + "parameters": [ + { + "name": "batch", + "in": "body", + "description": "The batch of index actions.", + "required": true, + "schema": { + "$ref": "#/definitions/IndexBatch" + } + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing the status of operations for all actions in the batch.", + "schema": { + "$ref": "#/definitions/IndexDocumentsResult" + } + }, + "207": { + "description": "Response containing the status of operations for all actions in the batch.", + "schema": { + "$ref": "#/definitions/IndexDocumentsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.autocomplete": { + "get": { + "tags": [ + "Documents" + ], + "operationId": "Documents_AutocompleteGet", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/autocomplete" + }, + "x-ms-examples": { + "SearchIndexAutocompleteDocumentsGet": { + "$ref": "./examples/SearchIndexAutocompleteDocumentsGet.json" + } + }, + "description": "Autocompletes incomplete query terms based on input text and matching terms in the index.", + "parameters": [ + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "name": "search", + "in": "query", + "type": "string", + "required": true, + "description": "The incomplete term which should be auto-completed.", + "x-ms-client-name": "SearchText" + }, + { + "name": "suggesterName", + "in": "query", + "type": "string", + "required": true, + "description": "The name of the suggester as specified in the suggesters collection that's part of the index definition." + }, + { + "name": "autocompleteMode", + "in": "query", + "type": "string", + "x-nullable": false, + "enum": [ + "oneTerm", + "twoTerms", + "oneTermWithContext" + ], + "x-ms-enum": { + "name": "AutocompleteMode", + "modelAsString": false, + "values": [ + { + "value": "oneTerm", + "name": "OneTerm", + "description": "Only one term is suggested. If the query has two terms, only the last term is completed. For example, if the input is 'washington medic', the suggested terms could include 'medicaid', 'medicare', and 'medicine'." + }, + { + "value": "twoTerms", + "name": "TwoTerms", + "description": "Matching two-term phrases in the index will be suggested. For example, if the input is 'medic', the suggested terms could include 'medicare coverage' and 'medical assistant'." + }, + { + "value": "oneTermWithContext", + "name": "OneTermWithContext", + "description": "Completes the last term in a query with two or more terms, where the last two terms are a phrase that exists in the index. For example, if the input is 'washington medic', the suggested terms could include 'washington medicaid' and 'washington medical'." + } + ] + }, + "description": "Specifies the mode for Autocomplete. The default is 'oneTerm'. Use 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing auto-completed terms.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "$filter", + "in": "query", + "type": "string", + "description": "An OData expression that filters the documents used to produce completed terms for the Autocomplete result.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "fuzzy", + "in": "query", + "type": "boolean", + "description": "A value indicating whether to use fuzzy matching for the autocomplete query. Default is false. When set to true, the query will find terms even if there's a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are slower and consume more resources.", + "x-ms-client-name": "UseFuzzyMatching", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "highlightPostTag", + "in": "query", + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting is disabled.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "highlightPreTag", + "in": "query", + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting is disabled.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "minimumCoverage", + "in": "query", + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by an autocomplete query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "searchFields", + "in": "query", + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of field names to consider when querying for auto-completed terms. Target fields must be included in the specified suggester.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + }, + { + "name": "$top", + "in": "query", + "type": "integer", + "format": "int32", + "description": "The number of auto-completed terms to retrieve. This must be a value between 1 and 100. The default is 5.", + "x-ms-parameter-grouping": { + "name": "AutocompleteOptions" + } + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing suggested query terms that complete the partial input.", + "schema": { + "$ref": "#/definitions/AutocompleteResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/docs/search.post.autocomplete": { + "post": { + "tags": [ + "Documents" + ], + "operationId": "Documents_AutocompletePost", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/autocomplete" + }, + "x-ms-examples": { + "SearchIndexAutocompleteDocumentsPost": { + "$ref": "./examples/SearchIndexAutocompleteDocumentsPost.json" + } + }, + "description": "Autocompletes incomplete query terms based on input text and matching terms in the index.", + "parameters": [ + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + }, + { + "name": "autocompleteRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/AutocompleteRequest" + }, + "description": "The definition of the Autocomplete request." + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "Response containing suggested query terms that complete the partial input.", + "schema": { + "$ref": "#/definitions/AutocompleteResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "SuggestDocumentsResult": { + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/SuggestResult" + }, + "readOnly": true, + "x-ms-client-name": "Results", + "description": "The sequence of results returned by the query." + }, + "@search.coverage": { + "type": "number", + "readOnly": true, + "format": "double", + "x-ms-client-name": "Coverage", + "description": "A value indicating the percentage of the index that was included in the query, or null if minimumCoverage was not set in the request." + } + }, + "required": [ + "value" + ], + "description": "Response containing suggestion query results from an index." + }, + "SuggestResult": { + "properties": { + "@search.text": { + "type": "string", + "readOnly": true, + "description": "The text of the suggestion result.", + "x-ms-client-name": "Text" + } + }, + "required": [ + "@search.text" + ], + "additionalProperties": true, + "description": "A result containing a document found by a suggestion query, plus associated metadata." + }, + "FacetResult": { + "properties": { + "count": { + "type": "integer", + "format": "int64", + "readOnly": true, + "description": "The approximate count of documents falling within the bucket described by this facet." + } + }, + "additionalProperties": true, + "description": "A single bucket of a facet query result. Reports the number of documents with a field value falling within a particular range or having a particular value or interval." + }, + "SearchDocumentsResult": { + "properties": { + "@odata.count": { + "type": "integer", + "format": "int64", + "readOnly": true, + "x-ms-client-name": "Count", + "description": "The total count of results found by the search operation, or null if the count was not requested. If present, the count may be greater than the number of results in this response. This can happen if you use the $top or $skip parameters, or if the query can't return all the requested documents in a single response." + }, + "@search.coverage": { + "type": "number", + "format": "double", + "readOnly": true, + "x-ms-client-name": "Coverage", + "description": "A value indicating the percentage of the index that was included in the query, or null if minimumCoverage was not specified in the request." + }, + "@search.facets": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/FacetResult" + } + }, + "readOnly": true, + "x-ms-client-name": "Facets", + "description": "The facet query results for the search operation, organized as a collection of buckets for each faceted field; null if the query did not include any facet expressions." + }, + "@search.answers": { + "type": "array", + "items": { + "$ref": "#/definitions/AnswerResult" + }, + "readOnly": true, + "x-ms-client-name": "Answers", + "x-nullable": true, + "description": "The answers query results for the search operation; null if the answers query parameter was not specified or set to 'none'." + }, + "@search.nextPageParameters": { + "$ref": "#/definitions/SearchRequest", + "readOnly": true, + "x-ms-client-name": "NextPageParameters", + "description": "Continuation JSON payload returned when the query can't return all the requested results in a single response. You can use this JSON along with @odata.nextLink to formulate another POST Search request to get the next part of the search response." + }, + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchResult" + }, + "readOnly": true, + "x-ms-client-name": "Results", + "description": "The sequence of results returned by the query." + }, + "@odata.nextLink": { + "type": "string", + "readOnly": true, + "x-ms-client-name": "NextLink", + "description": "Continuation URL returned when the query can't return all the requested results in a single response. You can use this URL to formulate another GET or POST Search request to get the next part of the search response. Make sure to use the same verb (GET or POST) as the request that produced this response." + }, + "@search.semanticPartialResponseReason": { + "$ref": "#/definitions/SemanticPartialResponseReason", + "readOnly": true, + "x-ms-client-name": "SemanticPartialResponseReason", + "description": "Reason that a partial response was returned for a semantic ranking request." + }, + "@search.semanticPartialResponseType": { + "$ref": "#/definitions/SemanticPartialResponseType", + "readOnly": true, + "x-ms-client-name": "SemanticPartialResponseType", + "description": "Type of partial response that was returned for a semantic ranking request." + } + }, + "required": [ + "value" + ], + "description": "Response containing search results from an index." + }, + "SearchResult": { + "properties": { + "@search.score": { + "type": "number", + "format": "double", + "readOnly": true, + "x-ms-client-name": "Score", + "x-nullable": false, + "description": "The relevance score of the document compared to other documents returned by the query." + }, + "@search.rerankerScore": { + "type": "number", + "format": "double", + "readOnly": true, + "x-ms-client-name": "RerankerScore", + "x-nullable": true, + "description": "The relevance score computed by the semantic ranker for the top search results. Search results are sorted by the RerankerScore first and then by the Score. RerankerScore is only returned for queries of type 'semantic'." + }, + "@search.highlights": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "readOnly": true, + "x-ms-client-name": "Highlights", + "description": "Text fragments from the document that indicate the matching search terms, organized by each applicable field; null if hit highlighting was not enabled for the query." + }, + "@search.captions": { + "type": "array", + "items": { + "$ref": "#/definitions/CaptionResult" + }, + "readOnly": true, + "x-ms-client-name": "Captions", + "x-nullable": true, + "description": "Captions are the most representative passages from the document relatively to the search query. They are often used as document summary. Captions are only returned for queries of type 'semantic'." + } + }, + "required": [ + "@search.score" + ], + "additionalProperties": true, + "description": "Contains a document found by a search query, plus associated metadata." + }, + "LookupDocument": { + "type": "object", + "additionalProperties": true, + "description": "A document retrieved via a document lookup operation." + }, + "IndexBatch": { + "properties": { + "value": { + "type": "array", + "items": { + "$ref": "#/definitions/IndexAction" + }, + "description": "The actions in the batch.", + "x-ms-client-name": "Actions" + } + }, + "required": [ + "value" + ], + "description": "Contains a batch of document write actions to send to the index." + }, + "IndexAction": { + "properties": { + "@search.action": { + "type": "string", + "enum": [ + "upload", + "merge", + "mergeOrUpload", + "delete" + ], + "x-ms-enum": { + "name": "IndexActionType", + "modelAsString": false, + "values": [ + { + "value": "upload", + "name": "Upload", + "description": "Inserts the document into the index if it is new and updates it if it exists. All fields are replaced in the update case." + }, + { + "value": "merge", + "name": "Merge", + "description": "Merges the specified field values with an existing document. If the document does not exist, the merge will fail. Any field you specify in a merge will replace the existing field in the document. This also applies to collections of primitive and complex types." + }, + { + "value": "mergeOrUpload", + "name": "MergeOrUpload", + "description": "Behaves like merge if a document with the given key already exists in the index. If the document does not exist, it behaves like upload with a new document." + }, + { + "value": "delete", + "name": "Delete", + "description": "Removes the specified document from the index. Any field you specify in a delete operation other than the key field will be ignored. If you want to remove an individual field from a document, use merge instead and set the field explicitly to null." + } + ] + }, + "x-ms-client-name": "ActionType", + "x-nullable": false, + "description": "The operation to perform on a document in an indexing batch." + } + }, + "additionalProperties": true, + "description": "Represents an index action that operates on a document." + }, + "IndexingResult": { + "properties": { + "key": { + "type": "string", + "readOnly": true, + "description": "The key of a document that was in the indexing request." + }, + "errorMessage": { + "type": "string", + "readOnly": true, + "description": "The error message explaining why the indexing operation failed for the document identified by the key; null if indexing succeeded." + }, + "status": { + "x-ms-client-name": "Succeeded", + "type": "boolean", + "x-nullable": false, + "readOnly": true, + "description": "A value indicating whether the indexing operation succeeded for the document identified by the key." + }, + "statusCode": { + "type": "integer", + "format": "int32", + "x-nullable": false, + "readOnly": true, + "description": "The status code of the indexing operation. Possible values include: 200 for a successful update or delete, 201 for successful document creation, 400 for a malformed input document, 404 for document not found, 409 for a version conflict, 422 when the index is temporarily unavailable, or 503 for when the service is too busy." + } + }, + "required": [ + "key", + "status", + "statusCode" + ], + "description": "Status of an indexing operation for a single document." + }, + "IndexDocumentsResult": { + "properties": { + "value": { + "x-ms-client-name": "Results", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/IndexingResult" + }, + "description": "The list of status information for each document in the indexing request." + } + }, + "required": [ + "value" + ], + "description": "Response containing the status of operations for all documents in the indexing request." + }, + "SearchMode": { + "type": "string", + "enum": [ + "any", + "all" + ], + "x-ms-enum": { + "name": "SearchMode", + "modelAsString": false, + "values": [ + { + "value": "any", + "name": "Any", + "description": "Any of the search terms must be matched in order to count the document as a match." + }, + { + "value": "all", + "name": "All", + "description": "All of the search terms must be matched in order to count the document as a match." + } + ] + }, + "description": "Specifies whether any or all of the search terms must be matched in order to count the document as a match." + }, + "QueryType": { + "type": "string", + "enum": [ + "simple", + "full", + "semantic" + ], + "x-ms-enum": { + "name": "QueryType", + "modelAsString": false, + "values": [ + { + "value": "simple", + "name": "Simple", + "description": "Uses the simple query syntax for searches. Search text is interpreted using a simple query language that allows for symbols such as +, * and \"\". Queries are evaluated across all searchable fields by default, unless the searchFields parameter is specified." + }, + { + "value": "full", + "name": "Full", + "description": "Uses the full Lucene query syntax for searches. Search text is interpreted using the Lucene query language which allows field-specific and weighted searches, as well as other advanced features." + }, + { + "value": "semantic", + "name": "Semantic", + "description": "Best suited for queries expressed in natural language as opposed to keywords. Improves precision of search results by re-ranking the top search results using a ranking model trained on the Web corpus." + } + ] + }, + "description": "Specifies the syntax of the search query. The default is 'simple'. Use 'full' if your query uses the Lucene query syntax." + }, + "VectorQuery": { + "type": "object", + "discriminator": "kind", + "properties": { + "kind": { + "$ref": "#/definitions/VectorQueryKind", + "description": "The kind of vector query being performed.", + "x-nullable": false + }, + "k": { + "type": "integer", + "format": "int32", + "description": "Number of nearest neighbors to return as top hits." + }, + "fields": { + "type": "string", + "description": "Vector Fields of type Collection(Edm.Single) to be included in the vector searched." + }, + "exhaustive": { + "type": "boolean", + "description": "When true, triggers an exhaustive k-nearest neighbor search across all vectors within the vector index. Useful for scenarios where exact matches are critical, such as determining ground truth values." + }, + "oversampling": { + "type": "number", + "format": "double", + "description": "Oversampling factor. Minimum value is 1. It overrides the 'defaultOversampling' parameter configured in the index definition. It can be set only when 'rerankWithOriginalVectors' is true. This parameter is only permitted when a compression method is used on the underlying vector field." + }, + "weight": { + "type": "number", + "format": "float", + "description": "Relative weight of the vector query when compared to other vector query and/or the text query within the same search request. This value is used when combining the results of multiple ranking lists produced by the different vector queries and/or the results retrieved through the text query. The higher the weight, the higher the documents that matched that query will be in the final ranking. Default is 1.0 and the value needs to be a positive number larger than zero. " + } + }, + "required": [ + "kind" + ], + "description": "The query parameters for vector and hybrid search queries." + }, + "RawVectorQuery": { + "type": "object", + "x-ms-client-name": "VectorizedQuery", + "x-ms-discriminator-value": "vector", + "allOf": [ + { + "$ref": "#/definitions/VectorQuery" + } + ], + "properties": { + "vector": { + "type": "array", + "items": { + "type": "number", + "format": "float" + }, + "description": "The vector representation of a search query." + } + }, + "required": [ + "vector" + ], + "description": "The query parameters to use for vector search when a raw vector value is provided." + }, + "VectorizableTextQuery": { + "type": "object", + "x-ms-discriminator-value": "text", + "allOf": [ + { + "$ref": "#/definitions/VectorQuery" + } + ], + "properties": { + "text": { + "type": "string", + "description": "The text to be vectorized to perform a vector search query." + } + }, + "required": [ + "text" + ], + "description": "The query parameters to use for vector search when a text value that needs to be vectorized is provided." + }, + "VectorQueryKind": { + "type": "string", + "enum": [ + "vector", + "text" + ], + "x-ms-enum": { + "name": "VectorQueryKind", + "modelAsString": true, + "values": [ + { + "value": "vector", + "name": "Vector", + "description": "Vector query where a raw vector value is provided." + }, + { + "value": "text", + "name": "Text", + "description": "Vector query where a text value that needs to be vectorized is provided." + } + ] + }, + "description": "The kind of vector query being performed." + }, + "VectorFilterMode": { + "type": "string", + "enum": [ + "postFilter", + "preFilter" + ], + "x-ms-enum": { + "name": "VectorFilterMode", + "modelAsString": true, + "values": [ + { + "value": "postFilter", + "name": "PostFilter", + "description": "The filter will be applied after the candidate set of vector results is returned. Depending on the filter selectivity, this can result in fewer results than requested by the parameter 'k'." + }, + { + "value": "preFilter", + "name": "PreFilter", + "description": "The filter will be applied before the search query." + } + ] + }, + "description": "Determines whether or not filters are applied before or after the vector search is performed." + }, + "ScoringStatistics": { + "type": "string", + "enum": [ + "local", + "global" + ], + "x-ms-enum": { + "name": "ScoringStatistics", + "modelAsString": false, + "values": [ + { + "value": "local", + "name": "Local", + "description": "The scoring statistics will be calculated locally for lower latency." + }, + { + "value": "global", + "name": "Global", + "description": "The scoring statistics will be calculated globally for more consistent scoring." + } + ] + }, + "description": "A value that specifies whether we want to calculate scoring statistics (such as document frequency) globally for more consistent scoring, or locally, for lower latency. The default is 'local'. Use 'global' to aggregate scoring statistics globally before scoring. Using global scoring statistics can increase latency of search queries." + }, + "AutocompleteMode": { + "type": "string", + "enum": [ + "oneTerm", + "twoTerms", + "oneTermWithContext" + ], + "x-ms-enum": { + "name": "AutocompleteMode", + "modelAsString": false, + "values": [ + { + "value": "oneTerm", + "name": "OneTerm", + "description": "Only one term is suggested. If the query has two terms, only the last term is completed. For example, if the input is 'washington medic', the suggested terms could include 'medicaid', 'medicare', and 'medicine'." + }, + { + "value": "twoTerms", + "name": "TwoTerms", + "description": "Matching two-term phrases in the index will be suggested. For example, if the input is 'medic', the suggested terms could include 'medicare coverage' and 'medical assistant'." + }, + { + "value": "oneTermWithContext", + "name": "OneTermWithContext", + "description": "Completes the last term in a query with two or more terms, where the last two terms are a phrase that exists in the index. For example, if the input is 'washington medic', the suggested terms could include 'washington medicaid' and 'washington medical'." + } + ] + }, + "description": "Specifies the mode for Autocomplete. The default is 'oneTerm'. Use 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context in producing autocomplete terms." + }, + "SearchRequest": { + "properties": { + "count": { + "type": "boolean", + "description": "A value that specifies whether to fetch the total count of results. Default is false. Setting this value to true may have a performance impact. Note that the count returned is an approximation.", + "x-ms-client-name": "IncludeTotalResultCount" + }, + "facets": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Search-Documents" + }, + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of facet expressions to apply to the search query. Each facet expression contains a field name, optionally followed by a comma-separated list of name:value pairs." + }, + "filter": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search" + }, + "type": "string", + "description": "The OData $filter expression to apply to the search query." + }, + "highlight": { + "type": "string", + "description": "The comma-separated list of field names to use for hit highlights. Only searchable fields can be used for hit highlighting.", + "x-ms-client-name": "HighlightFields" + }, + "highlightPostTag": { + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. Default is </em>." + }, + "highlightPreTag": { + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. Default is <em>." + }, + "minimumCoverage": { + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by a search query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 100." + }, + "orderby": { + "x-ms-client-name": "OrderBy", + "type": "string", + "description": "The comma-separated list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses." + }, + "queryType": { + "$ref": "#/definitions/QueryType", + "description": "A value that specifies the syntax of the search query. The default is 'simple'. Use 'full' if your query uses the Lucene query syntax." + }, + "scoringStatistics": { + "$ref": "#/definitions/ScoringStatistics", + "description": "A value that specifies whether we want to calculate scoring statistics (such as document frequency) globally for more consistent scoring, or locally, for lower latency. The default is 'local'. Use 'global' to aggregate scoring statistics globally before scoring. Using global scoring statistics can increase latency of search queries." + }, + "sessionId": { + "type": "string", + "description": "A value to be used to create a sticky session, which can help getting more consistent results. As long as the same sessionId is used, a best-effort attempt will be made to target the same replica set. Be wary that reusing the same sessionID values repeatedly can interfere with the load balancing of the requests across replicas and adversely affect the performance of the search service. The value used as sessionId cannot start with a '_' character." + }, + "scoringParameters": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of parameter values to be used in scoring functions (for example, referencePointParameter) using the format name-values. For example, if the scoring profile defines a function with a parameter called 'mylocation' the parameter string would be \"mylocation--122.2,44.8\" (without the quotes)." + }, + "scoringProfile": { + "type": "string", + "description": "The name of a scoring profile to evaluate match scores for matching documents in order to sort the results." + }, + "search": { + "type": "string", + "description": "A full-text search query expression; Use \"*\" or omit this parameter to match all documents.", + "x-ms-client-name": "SearchText" + }, + "searchFields": { + "type": "string", + "description": "The comma-separated list of field names to which to scope the full-text search. When using fielded search (fieldName:searchExpression) in a full Lucene query, the field names of each fielded search expression take precedence over any field names listed in this parameter." + }, + "searchMode": { + "$ref": "#/definitions/SearchMode", + "description": "A value that specifies whether any or all of the search terms must be matched in order to count the document as a match." + }, + "select": { + "type": "string", + "description": "The comma-separated list of fields to retrieve. If unspecified, all fields marked as retrievable in the schema are included." + }, + "skip": { + "type": "integer", + "format": "int32", + "description": "The number of search results to skip. This value cannot be greater than 100,000. If you need to scan documents in sequence, but cannot use skip due to this limitation, consider using orderby on a totally-ordered key and filter with a range query instead." + }, + "top": { + "type": "integer", + "format": "int32", + "description": "The number of search results to retrieve. This can be used in conjunction with $skip to implement client-side paging of search results. If results are truncated due to server-side paging, the response will include a continuation token that can be used to issue another Search request for the next page of results." + }, + "semanticConfiguration": { + "type": "string", + "description": "The name of a semantic configuration that will be used when processing documents for queries of type semantic." + }, + "semanticErrorHandling": { + "$ref": "#/definitions/SemanticErrorHandling", + "description": "Allows the user to choose whether a semantic call should fail completely (default / current behavior), or to return partial results." + }, + "semanticMaxWaitInMilliseconds": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "minimum": 700, + "description": "Allows the user to set an upper bound on the amount of time it takes for semantic enrichment to finish processing before the request fails." + }, + "semanticQuery": { + "type": "string", + "description": "Allows setting a separate search query that will be solely used for semantic reranking, semantic captions and semantic answers. Is useful for scenarios where there is a need to use different queries between the base retrieval and ranking phase, and the L2 semantic phase." + }, + "answers": { + "$ref": "#/definitions/Answers", + "description": "A value that specifies whether answers should be returned as part of the search response." + }, + "captions": { + "$ref": "#/definitions/Captions", + "description": "A value that specifies whether captions should be returned as part of the search response." + }, + "vectorQueries": { + "type": "array", + "items": { + "$ref": "#/definitions/VectorQuery" + }, + "description": "The query parameters for vector and hybrid search queries." + }, + "vectorFilterMode": { + "$ref": "#/definitions/VectorFilterMode", + "description": "Determines whether or not filters are applied before or after the vector search is performed. Default is 'preFilter' for new indexes." + } + }, + "description": "Parameters for filtering, sorting, faceting, paging, and other search query behaviors." + }, + "SuggestRequest": { + "properties": { + "filter": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search" + }, + "type": "string", + "description": "An OData expression that filters the documents considered for suggestions." + }, + "fuzzy": { + "type": "boolean", + "description": "A value indicating whether to use fuzzy matching for the suggestion query. Default is false. When set to true, the query will find suggestions even if there's a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy suggestion searches are slower and consume more resources.", + "x-ms-client-name": "UseFuzzyMatching" + }, + "highlightPostTag": { + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting of suggestions is disabled." + }, + "highlightPreTag": { + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting of suggestions is disabled." + }, + "minimumCoverage": { + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by a suggestion query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80." + }, + "orderby": { + "x-ms-client-name": "OrderBy", + "type": "string", + "description": "The comma-separated list of OData $orderby expressions by which to sort the results. Each expression can be either a field name or a call to either the geo.distance() or the search.score() functions. Each expression can be followed by asc to indicate ascending, or desc to indicate descending. The default is ascending order. Ties will be broken by the match scores of documents. If no $orderby is specified, the default sort order is descending by document match score. There can be at most 32 $orderby clauses." + }, + "search": { + "type": "string", + "description": "The search text to use to suggest documents. Must be at least 1 character, and no more than 100 characters.", + "x-ms-client-name": "SearchText" + }, + "searchFields": { + "type": "string", + "description": "The comma-separated list of field names to search for the specified search text. Target fields must be included in the specified suggester." + }, + "select": { + "type": "string", + "description": "The comma-separated list of fields to retrieve. If unspecified, only the key field will be included in the results." + }, + "suggesterName": { + "type": "string", + "description": "The name of the suggester as specified in the suggesters collection that's part of the index definition." + }, + "top": { + "type": "integer", + "format": "int32", + "description": "The number of suggestions to retrieve. This must be a value between 1 and 100. The default is 5." + } + }, + "required": [ + "search", + "suggesterName" + ], + "description": "Parameters for filtering, sorting, fuzzy matching, and other suggestions query behaviors." + }, + "AutocompleteRequest": { + "properties": { + "search": { + "type": "string", + "description": "The search text on which to base autocomplete results.", + "x-ms-client-name": "SearchText" + }, + "autocompleteMode": { + "$ref": "#/definitions/AutocompleteMode", + "description": "Specifies the mode for Autocomplete. The default is 'oneTerm'. Use 'twoTerms' to get shingles and 'oneTermWithContext' to use the current context while producing auto-completed terms." + }, + "filter": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/OData-Expression-Syntax-for-Azure-Search" + }, + "type": "string", + "description": "An OData expression that filters the documents used to produce completed terms for the Autocomplete result." + }, + "fuzzy": { + "type": "boolean", + "description": "A value indicating whether to use fuzzy matching for the autocomplete query. Default is false. When set to true, the query will autocomplete terms even if there's a substituted or missing character in the search text. While this provides a better experience in some scenarios, it comes at a performance cost as fuzzy autocomplete queries are slower and consume more resources.", + "x-ms-client-name": "UseFuzzyMatching" + }, + "highlightPostTag": { + "type": "string", + "description": "A string tag that is appended to hit highlights. Must be set with highlightPreTag. If omitted, hit highlighting is disabled." + }, + "highlightPreTag": { + "type": "string", + "description": "A string tag that is prepended to hit highlights. Must be set with highlightPostTag. If omitted, hit highlighting is disabled." + }, + "minimumCoverage": { + "type": "number", + "format": "double", + "description": "A number between 0 and 100 indicating the percentage of the index that must be covered by an autocomplete query in order for the query to be reported as a success. This parameter can be useful for ensuring search availability even for services with only one replica. The default is 80." + }, + "searchFields": { + "type": "string", + "description": "The comma-separated list of field names to consider when querying for auto-completed terms. Target fields must be included in the specified suggester." + }, + "suggesterName": { + "type": "string", + "description": "The name of the suggester as specified in the suggesters collection that's part of the index definition." + }, + "top": { + "type": "integer", + "format": "int32", + "description": "The number of auto-completed terms to retrieve. This must be a value between 1 and 100. The default is 5." + } + }, + "required": [ + "search", + "suggesterName" + ], + "description": "Parameters for fuzzy matching, and other autocomplete query behaviors." + }, + "AutocompleteResult": { + "properties": { + "@search.coverage": { + "type": "number", + "format": "double", + "readOnly": true, + "x-ms-client-name": "Coverage", + "description": "A value indicating the percentage of the index that was considered by the autocomplete request, or null if minimumCoverage was not specified in the request." + }, + "value": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/AutocompleteItem" + }, + "description": "The list of returned Autocompleted items.", + "x-ms-client-name": "Results" + } + }, + "required": [ + "value" + ], + "description": "The result of Autocomplete query." + }, + "AutocompleteItem": { + "properties": { + "text": { + "type": "string", + "readOnly": true, + "description": "The completed term." + }, + "queryPlusText": { + "type": "string", + "readOnly": true, + "description": "The query along with the completed term." + } + }, + "required": [ + "text", + "queryPlusText" + ], + "description": "The result of Autocomplete requests." + }, + "AnswerResult": { + "x-ms-client-name": "QueryAnswerResult", + "properties": { + "score": { + "type": "number", + "format": "double", + "readOnly": true, + "description": "The score value represents how relevant the answer is to the query relative to other answers returned for the query." + }, + "key": { + "type": "string", + "readOnly": true, + "description": "The key of the document the answer was extracted from." + }, + "text": { + "type": "string", + "readOnly": true, + "description": "The text passage extracted from the document contents as the answer." + }, + "highlights": { + "type": "string", + "readOnly": true, + "x-nullable": true, + "description": "Same text passage as in the Text property with highlighted text phrases most relevant to the query." + } + }, + "additionalProperties": true, + "description": "An answer is a text passage extracted from the contents of the most relevant documents that matched the query. Answers are extracted from the top search results. Answer candidates are scored and the top answers are selected." + }, + "CaptionResult": { + "x-ms-client-name": "QueryCaptionResult", + "properties": { + "text": { + "type": "string", + "readOnly": true, + "description": "A representative text passage extracted from the document most relevant to the search query." + }, + "highlights": { + "type": "string", + "readOnly": true, + "x-nullable": true, + "description": "Same text passage as in the Text property with highlighted phrases most relevant to the query." + } + }, + "additionalProperties": true, + "description": "Captions are the most representative passages from the document relatively to the search query. They are often used as document summary. Captions are only returned for queries of type `semantic`." + }, + "SemanticPartialResponseReason": { + "type": "string", + "enum": [ + "maxWaitExceeded", + "capacityOverloaded", + "transient" + ], + "x-ms-enum": { + "name": "SemanticErrorReason", + "modelAsString": true, + "values": [ + { + "value": "maxWaitExceeded", + "name": "MaxWaitExceeded", + "description": "If `semanticMaxWaitInMilliseconds` was set and the semantic processing duration exceeded that value. Only the base results were returned." + }, + { + "value": "capacityOverloaded", + "name": "CapacityOverloaded", + "description": "The request was throttled. Only the base results were returned." + }, + { + "value": "transient", + "name": "Transient", + "description": "At least one step of the semantic process failed." + } + ] + }, + "description": "Reason that a partial response was returned for a semantic ranking request." + }, + "SemanticPartialResponseType": { + "type": "string", + "enum": [ + "baseResults", + "rerankedResults" + ], + "x-ms-enum": { + "name": "SemanticSearchResultsType", + "modelAsString": true, + "values": [ + { + "value": "baseResults", + "name": "BaseResults", + "description": "Results without any semantic enrichment or reranking." + }, + { + "value": "rerankedResults", + "name": "RerankedResults", + "description": "Results have been reranked with the reranker model and will include semantic captions. They will not include any answers, answers highlights or caption highlights." + } + ] + }, + "description": "Type of partial response that was returned for a semantic ranking request." + }, + "SemanticErrorHandling": { + "type": "string", + "enum": [ + "partial", + "fail" + ], + "x-ms-enum": { + "name": "SemanticErrorMode", + "modelAsString": true, + "values": [ + { + "value": "partial", + "name": "Partial", + "description": "If the semantic processing fails, partial results still return. The definition of partial results depends on what semantic step failed and what was the reason for failure." + }, + { + "value": "fail", + "name": "Fail", + "description": "If there is an exception during the semantic processing step, the query will fail and return the appropriate HTTP code depending on the error." + } + ] + }, + "description": "Allows the user to choose whether a semantic call should fail completely, or to return partial results." + }, + "Answers": { + "type": "string", + "enum": [ + "none", + "extractive" + ], + "x-ms-enum": { + "name": "QueryAnswerType", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Do not return answers for the query." + }, + { + "value": "extractive", + "name": "Extractive", + "description": "Extracts answer candidates from the contents of the documents returned in response to a query expressed as a question in natural language." + } + ] + }, + "description": "This parameter is only valid if the query type is `semantic`. If set, the query returns answers extracted from key passages in the highest ranked documents. The number of answers returned can be configured by appending the pipe character `|` followed by the `count-` option after the answers parameter value, such as `extractive|count-3`. Default count is 1. The confidence threshold can be configured by appending the pipe character `|` followed by the `threshold-` option after the answers parameter value, such as `extractive|threshold-0.9`. Default threshold is 0.7." + }, + "Captions": { + "type": "string", + "enum": [ + "none", + "extractive" + ], + "x-ms-enum": { + "name": "QueryCaptionType", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Do not return captions for the query." + }, + { + "value": "extractive", + "name": "Extractive", + "description": "Extracts captions from the matching documents that contain passages relevant to the search query." + } + ] + }, + "description": "This parameter is only valid if the query type is `semantic`. If set, the query returns captions extracted from key passages in the highest ranked documents. When Captions is set to `extractive`, highlighting is enabled by default, and can be configured by appending the pipe character `|` followed by the `highlight-` option, such as `extractive|highlight-true`. Defaults to `None`." + } + }, + "parameters": { + "ApiVersionParameter": { + "name": "api-version", + "in": "query", + "required": true, + "type": "string", + "description": "Client Api Version." + }, + "ClientRequestIdParameter": { + "name": "x-ms-client-request-id", + "in": "header", + "required": false, + "type": "string", + "format": "uuid", + "description": "The tracking ID sent with the request to help with debugging.", + "x-ms-client-request-id": true, + "x-ms-parameter-grouping": { + "name": "request-options" + }, + "x-ms-parameter-location": "method" + }, + "EndpointParameter": { + "name": "endpoint", + "in": "path", + "required": true, + "type": "string", + "x-ms-skip-url-encoding": true, + "description": "The endpoint URL of the search service.", + "x-ms-parameter-location": "client" + }, + "IndexNameParameter": { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "x-ms-skip-url-encoding": false, + "description": "The name of the index.", + "x-ms-parameter-location": "client" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-service-2024-07-01-api-spec.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-service-2024-07-01-api-spec.json new file mode 100644 index 000000000..9a4bf9126 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/ai-search-api/ai-search-service-2024-07-01-api-spec.json @@ -0,0 +1,11547 @@ +{ + "swagger": "2.0", + "info": { + "title": "SearchServiceClient", + "description": "Client that can be used to manage and query indexes and documents, as well as manage other resources, on a search service.", + "version": "2024-07-01", + "x-ms-code-generation-settings": { + "useDateTimeOffset": true + } + }, + "x-ms-parameterized-host": { + "hostTemplate": "{endpoint}", + "useSchemePrefix": false, + "parameters": [ + { + "$ref": "#/parameters/EndpointParameter" + } + ] + }, + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/datasources('{dataSourceName}')": { + "put": { + "tags": [ + "DataSources" + ], + "operationId": "DataSources_CreateOrUpdate", + "x-ms-examples": { + "SearchServiceCreateOrUpdateDataSource": { + "$ref": "./examples/SearchServiceCreateOrUpdateDataSource.json" + } + }, + "description": "Creates a new datasource or updates a datasource if it already exists.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Update-Data-Source" + }, + "parameters": [ + { + "name": "dataSourceName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the datasource to create or update." + }, + { + "name": "dataSource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + }, + "description": "The definition of the datasource to create or update." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/PreferHeaderParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + } + }, + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "delete": { + "tags": [ + "DataSources" + ], + "operationId": "DataSources_Delete", + "x-ms-examples": { + "SearchServiceDeleteDataSource": { + "$ref": "./examples/SearchServiceDeleteDataSource.json" + } + }, + "description": "Deletes a datasource.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Delete-Data-Source" + }, + "parameters": [ + { + "name": "dataSourceName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the datasource to delete." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "204": { + "description": "" + }, + "404": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "DataSources" + ], + "operationId": "DataSources_Get", + "x-ms-examples": { + "SearchServiceGetDataSource": { + "$ref": "./examples/SearchServiceGetDataSource.json" + } + }, + "description": "Retrieves a datasource definition.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Data-Source" + }, + "parameters": [ + { + "name": "dataSourceName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the datasource to retrieve." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/datasources": { + "get": { + "tags": [ + "DataSources" + ], + "operationId": "DataSources_List", + "x-ms-examples": { + "SearchServiceListDataSources": { + "$ref": "./examples/SearchServiceListDataSources.json" + } + }, + "description": "Lists all datasources available for a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/List-Data-Sources" + }, + "parameters": [ + { + "name": "$select", + "in": "query", + "required": false, + "type": "string", + "description": "Selects which top-level properties of the data sources to retrieve. Specified as a comma-separated list of JSON property names, or '*' for all properties. The default is all properties." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/ListDataSourcesResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "post": { + "tags": [ + "DataSources" + ], + "operationId": "DataSources_Create", + "x-ms-examples": { + "SearchServiceCreateDataSource": { + "$ref": "./examples/SearchServiceCreateDataSource.json" + } + }, + "description": "Creates a new datasource.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Data-Source" + }, + "parameters": [ + { + "name": "dataSource", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + }, + "description": "The definition of the datasource to create." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexerDataSource" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexers('{indexerName}')/search.reset": { + "post": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_Reset", + "x-ms-examples": { + "SearchServiceResetIndexer": { + "$ref": "./examples/SearchServiceResetIndexer.json" + } + }, + "description": "Resets the change tracking state associated with an indexer.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Reset-Indexer" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer to reset." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "204": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexers('{indexerName}')/search.run": { + "post": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_Run", + "x-ms-examples": { + "SearchServiceRunIndexer": { + "$ref": "./examples/SearchServiceRunIndexer.json" + } + }, + "description": "Runs an indexer on-demand.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Run-Indexer" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer to run." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "202": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexers('{indexerName}')": { + "put": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_CreateOrUpdate", + "x-ms-examples": { + "SearchServiceCreateOrUpdateIndexer": { + "$ref": "./examples/SearchServiceCreateOrUpdateIndexer.json" + } + }, + "description": "Creates a new indexer or updates an indexer if it already exists.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Indexer" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer to create or update." + }, + { + "name": "indexer", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexer" + }, + "description": "The definition of the indexer to create or update." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/PreferHeaderParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexer" + } + }, + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexer" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "delete": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_Delete", + "x-ms-examples": { + "SearchServiceDeleteIndexer": { + "$ref": "./examples/SearchServiceDeleteIndexer.json" + } + }, + "description": "Deletes an indexer.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Delete-Indexer" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer to delete." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "404": { + "description": "" + }, + "204": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_Get", + "x-ms-examples": { + "SearchServiceGetIndexer": { + "$ref": "./examples/SearchServiceGetIndexer.json" + } + }, + "description": "Retrieves an indexer definition.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Indexer" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer to retrieve." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexer" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexers": { + "get": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_List", + "x-ms-examples": { + "SearchServiceListIndexers": { + "$ref": "./examples/SearchServiceListIndexers.json" + } + }, + "description": "Lists all indexers available for a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/List-Indexers" + }, + "parameters": [ + { + "name": "$select", + "in": "query", + "required": false, + "type": "string", + "description": "Selects which top-level properties of the indexers to retrieve. Specified as a comma-separated list of JSON property names, or '*' for all properties. The default is all properties." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/ListIndexersResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "post": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_Create", + "x-ms-examples": { + "SearchServiceCreateIndexer": { + "$ref": "./examples/SearchServiceCreateIndexer.json" + } + }, + "description": "Creates a new indexer.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Indexer" + }, + "parameters": [ + { + "name": "indexer", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexer" + }, + "description": "The definition of the indexer to create." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexer" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexers('{indexerName}')/search.status": { + "get": { + "tags": [ + "Indexers" + ], + "operationId": "Indexers_GetStatus", + "x-ms-examples": { + "SearchServiceGetIndexerStatus": { + "$ref": "./examples/SearchServiceGetIndexerStatus.json" + } + }, + "description": "Returns the current status and execution history of an indexer.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Indexer-Status" + }, + "parameters": [ + { + "name": "indexerName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the indexer for which to retrieve status." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndexerStatus" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/skillsets('{skillsetName}')": { + "put": { + "tags": [ + "Skillsets" + ], + "operationId": "Skillsets_CreateOrUpdate", + "x-ms-examples": { + "SearchServiceCreateOrUpdateSkillset": { + "$ref": "./examples/SearchServiceCreateOrUpdateSkillset.json" + } + }, + "description": "Creates a new skillset in a search service or updates the skillset if it already exists.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/update-skillset" + }, + "parameters": [ + { + "name": "skillsetName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the skillset to create or update." + }, + { + "name": "skillset", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + }, + "description": "The skillset containing one or more skills to create or update in a search service." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/PreferHeaderParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "The skillset is successfully updated.", + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + } + }, + "201": { + "description": "The skillset is successfully created.", + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "delete": { + "tags": [ + "Skillsets" + ], + "operationId": "Skillsets_Delete", + "x-ms-examples": { + "SearchServiceDeleteSkillset": { + "$ref": "./examples/SearchServiceDeleteSkillset.json" + } + }, + "description": "Deletes a skillset in a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/delete-skillset" + }, + "parameters": [ + { + "name": "skillsetName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the skillset to delete." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "204": { + "description": "The skillset is successfully deleted." + }, + "404": { + "description": "The provided skillset name is not found." + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "Skillsets" + ], + "operationId": "Skillsets_Get", + "x-ms-examples": { + "SearchServiceGetSkillset": { + "$ref": "./examples/SearchServiceGetSkillset.json" + } + }, + "description": "Retrieves a skillset in a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/get-skillset" + }, + "parameters": [ + { + "name": "skillsetName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the skillset to retrieve." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "The skillset is successfully returned.", + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/skillsets": { + "get": { + "tags": [ + "Skillsets" + ], + "operationId": "Skillsets_List", + "x-ms-examples": { + "SearchServiceListSkillsets": { + "$ref": "./examples/SearchServiceListSkillsets.json" + } + }, + "description": "List all skillsets in a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/list-skillset" + }, + "parameters": [ + { + "name": "$select", + "in": "query", + "required": false, + "type": "string", + "description": "Selects which top-level properties of the skillsets to retrieve. Specified as a comma-separated list of JSON property names, or '*' for all properties. The default is all properties." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "The list is successfully returned.", + "schema": { + "$ref": "#/definitions/ListSkillsetsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "post": { + "tags": [ + "Skillsets" + ], + "operationId": "Skillsets_Create", + "x-ms-examples": { + "SearchServiceCreateSkillset": { + "$ref": "./examples/SearchServiceCreateSkillset.json" + } + }, + "description": "Creates a new skillset in a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/create-skillset" + }, + "parameters": [ + { + "name": "skillset", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + }, + "description": "The skillset containing one or more skills to create in a search service." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "The skillset is successfully created.", + "schema": { + "$ref": "#/definitions/SearchIndexerSkillset" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/synonymmaps('{synonymMapName}')": { + "put": { + "tags": [ + "SynonymMaps" + ], + "operationId": "SynonymMaps_CreateOrUpdate", + "x-ms-examples": { + "SearchServiceCreateOrUpdateSynonymMap": { + "$ref": "./examples/SearchServiceCreateOrUpdateSynonymMap.json" + } + }, + "description": "Creates a new synonym map or updates a synonym map if it already exists.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Update-Synonym-Map" + }, + "parameters": [ + { + "name": "synonymMapName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the synonym map to create or update." + }, + { + "name": "synonymMap", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SynonymMap" + }, + "description": "The definition of the synonym map to create or update." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/PreferHeaderParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SynonymMap" + } + }, + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SynonymMap" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "delete": { + "tags": [ + "SynonymMaps" + ], + "operationId": "SynonymMaps_Delete", + "x-ms-examples": { + "SearchServiceDeleteSynonymMap": { + "$ref": "./examples/SearchServiceDeleteSynonymMap.json" + } + }, + "description": "Deletes a synonym map.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Delete-Synonym-Map" + }, + "parameters": [ + { + "name": "synonymMapName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the synonym map to delete." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "204": { + "description": "" + }, + "404": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "SynonymMaps" + ], + "operationId": "SynonymMaps_Get", + "x-ms-examples": { + "SearchServiceGetSynonymMap": { + "$ref": "./examples/SearchServiceGetSynonymMap.json" + } + }, + "description": "Retrieves a synonym map definition.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Synonym-Map" + }, + "parameters": [ + { + "name": "synonymMapName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the synonym map to retrieve." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SynonymMap" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/synonymmaps": { + "get": { + "tags": [ + "SynonymMaps" + ], + "operationId": "SynonymMaps_List", + "x-ms-examples": { + "SearchServiceListSynonymMaps": { + "$ref": "./examples/SearchServiceListSynonymMaps.json" + } + }, + "description": "Lists all synonym maps available for a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/List-Synonym-Maps" + }, + "parameters": [ + { + "name": "$select", + "in": "query", + "required": false, + "type": "string", + "description": "Selects which top-level properties of the synonym maps to retrieve. Specified as a comma-separated list of JSON property names, or '*' for all properties. The default is all properties." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/ListSynonymMapsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "post": { + "tags": [ + "SynonymMaps" + ], + "operationId": "SynonymMaps_Create", + "x-ms-examples": { + "SearchServiceCreateSynonymMap": { + "$ref": "./examples/SearchServiceCreateSynonymMap.json" + } + }, + "description": "Creates a new synonym map.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Synonym-Map" + }, + "parameters": [ + { + "name": "synonymMap", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SynonymMap" + }, + "description": "The definition of the synonym map to create." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SynonymMap" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexes": { + "post": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_Create", + "x-ms-examples": { + "SearchServiceCreateIndex": { + "$ref": "./examples/SearchServiceCreateIndex.json" + } + }, + "description": "Creates a new search index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Index" + }, + "parameters": [ + { + "name": "index", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndex" + }, + "description": "The definition of the index to create." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndex" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_List", + "x-ms-examples": { + "SearchServiceListIndexes": { + "$ref": "./examples/SearchServiceListIndexes.json" + } + }, + "description": "Lists all indexes available for a search service.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/List-Indexes" + }, + "parameters": [ + { + "name": "$select", + "in": "query", + "required": false, + "type": "string", + "description": "Selects which top-level properties of the index definitions to retrieve. Specified as a comma-separated list of JSON property names, or '*' for all properties. The default is all properties." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/ListIndexesResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + }, + "x-ms-pageable": { + "nextLinkName": null + } + } + }, + "/indexes('{indexName}')": { + "put": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_CreateOrUpdate", + "x-ms-examples": { + "SearchServiceCreateOrUpdateIndex": { + "$ref": "./examples/SearchServiceCreateOrUpdateIndex.json" + } + }, + "description": "Creates a new search index or updates an index if it already exists.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Update-Index" + }, + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "description": "The definition of the index to create or update." + }, + { + "name": "index", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/SearchIndex" + }, + "description": "The definition of the index to create or update." + }, + { + "name": "allowIndexDowntime", + "in": "query", + "required": false, + "type": "boolean", + "description": "Allows new analyzers, tokenizers, token filters, or char filters to be added to an index by taking the index offline for at least a few seconds. This temporarily causes indexing and query requests to fail. Performance and write availability of the index can be impaired for several minutes after the index is updated, or longer for very large indexes." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/PreferHeaderParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndex" + } + }, + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndex" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "delete": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_Delete", + "x-ms-examples": { + "SearchServiceDeleteIndex": { + "$ref": "./examples/SearchServiceDeleteIndex.json" + } + }, + "description": "Deletes a search index and all the documents it contains. This operation is permanent, with no recovery option. Make sure you have a master copy of your index definition, data ingestion code, and a backup of the primary data source in case you need to re-build the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Delete-Index" + }, + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the index to delete." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/IfMatchParameter" + }, + { + "$ref": "#/parameters/IfNoneMatchParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "204": { + "description": "" + }, + "404": { + "description": "" + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + }, + "get": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_Get", + "x-ms-examples": { + "SearchServiceGetIndex": { + "$ref": "./examples/SearchServiceGetIndex.json" + } + }, + "description": "Retrieves an index definition.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Index" + }, + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the index to retrieve." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchIndex" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexes('{indexName}')/search.stats": { + "get": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_GetStatistics", + "x-ms-examples": { + "SearchServiceGetIndexStatistics": { + "$ref": "./examples/SearchServiceGetIndexStatistics.json" + } + }, + "description": "Returns statistics for the given index, including a document count and storage usage.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Get-Index-Statistics" + }, + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the index for which to retrieve statistics." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/GetIndexStatisticsResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/indexes('{indexName}')/search.analyze": { + "post": { + "tags": [ + "Indexes" + ], + "operationId": "Indexes_Analyze", + "x-ms-examples": { + "SearchServiceIndexAnalyze": { + "$ref": "./examples/SearchServiceIndexAnalyze.json" + } + }, + "description": "Shows how an analyzer breaks text into tokens.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/test-analyzer" + }, + "parameters": [ + { + "name": "indexName", + "in": "path", + "required": true, + "type": "string", + "description": "The name of the index for which to test an analyzer." + }, + { + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/AnalyzeRequest" + }, + "description": "The text and analyzer or analysis components to test." + }, + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/AnalyzeResult" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + }, + "/servicestats": { + "get": { + "tags": [ + "Service" + ], + "operationId": "GetServiceStatistics", + "x-ms-examples": { + "SearchServiceGetServiceStatistics": { + "$ref": "./examples/SearchServiceGetServiceStatistics.json" + } + }, + "description": "Gets service level statistics for a search service.", + "parameters": [ + { + "$ref": "#/parameters/ClientRequestIdParameter" + }, + { + "$ref": "#/parameters/ApiVersionParameter" + } + ], + "x-ms-request-id": "request-id", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/ServiceStatistics" + } + }, + "default": { + "description": "Error response.", + "schema": { + "$ref": "../../../../../common-types/data-plane/v1/types.json#/definitions/ErrorResponse" + } + } + } + } + } + }, + "definitions": { + "AnalyzeRequest": { + "properties": { + "text": { + "type": "string", + "description": "The text to break into tokens." + }, + "analyzer": { + "$ref": "#/definitions/LexicalAnalyzerName", + "description": "The name of the analyzer to use to break the given text. If this parameter is not specified, you must specify a tokenizer instead. The tokenizer and analyzer parameters are mutually exclusive." + }, + "tokenizer": { + "$ref": "#/definitions/LexicalTokenizerName", + "description": "The name of the tokenizer to use to break the given text. If this parameter is not specified, you must specify an analyzer instead. The tokenizer and analyzer parameters are mutually exclusive." + }, + "tokenFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/TokenFilterName", + "x-nullable": false + }, + "description": "An optional list of token filters to use when breaking the given text. This parameter can only be set when using the tokenizer parameter." + }, + "charFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/CharFilterName", + "x-nullable": false + }, + "description": "An optional list of character filters to use when breaking the given text. This parameter can only be set when using the tokenizer parameter." + } + }, + "required": [ + "text" + ], + "description": "Specifies some text and analysis components used to break that text into tokens." + }, + "AnalyzeResult": { + "properties": { + "tokens": { + "type": "array", + "items": { + "$ref": "#/definitions/AnalyzedTokenInfo" + }, + "description": "The list of tokens returned by the analyzer specified in the request." + } + }, + "required": [ + "tokens" + ], + "description": "The result of testing an analyzer on text." + }, + "AnalyzedTokenInfo": { + "properties": { + "token": { + "type": "string", + "readOnly": true, + "description": "The token returned by the analyzer." + }, + "startOffset": { + "type": "integer", + "format": "int32", + "readOnly": true, + "x-nullable": false, + "description": "The index of the first character of the token in the input text." + }, + "endOffset": { + "type": "integer", + "format": "int32", + "readOnly": true, + "x-nullable": false, + "description": "The index of the last character of the token in the input text." + }, + "position": { + "type": "integer", + "format": "int32", + "readOnly": true, + "x-nullable": false, + "description": "The position of the token in the input text relative to other tokens. The first token in the input text has position 0, the next has position 1, and so on. Depending on the analyzer used, some tokens might have the same position, for example if they are synonyms of each other." + } + }, + "required": [ + "token", + "startOffset", + "endOffset", + "position" + ], + "description": "Information about a token returned by an analyzer." + }, + "LexicalAnalyzerName": { + "type": "string", + "enum": [ + "ar.microsoft", + "ar.lucene", + "hy.lucene", + "bn.microsoft", + "eu.lucene", + "bg.microsoft", + "bg.lucene", + "ca.microsoft", + "ca.lucene", + "zh-Hans.microsoft", + "zh-Hans.lucene", + "zh-Hant.microsoft", + "zh-Hant.lucene", + "hr.microsoft", + "cs.microsoft", + "cs.lucene", + "da.microsoft", + "da.lucene", + "nl.microsoft", + "nl.lucene", + "en.microsoft", + "en.lucene", + "et.microsoft", + "fi.microsoft", + "fi.lucene", + "fr.microsoft", + "fr.lucene", + "gl.lucene", + "de.microsoft", + "de.lucene", + "el.microsoft", + "el.lucene", + "gu.microsoft", + "he.microsoft", + "hi.microsoft", + "hi.lucene", + "hu.microsoft", + "hu.lucene", + "is.microsoft", + "id.microsoft", + "id.lucene", + "ga.lucene", + "it.microsoft", + "it.lucene", + "ja.microsoft", + "ja.lucene", + "kn.microsoft", + "ko.microsoft", + "ko.lucene", + "lv.microsoft", + "lv.lucene", + "lt.microsoft", + "ml.microsoft", + "ms.microsoft", + "mr.microsoft", + "nb.microsoft", + "no.lucene", + "fa.lucene", + "pl.microsoft", + "pl.lucene", + "pt-BR.microsoft", + "pt-BR.lucene", + "pt-PT.microsoft", + "pt-PT.lucene", + "pa.microsoft", + "ro.microsoft", + "ro.lucene", + "ru.microsoft", + "ru.lucene", + "sr-cyrillic.microsoft", + "sr-latin.microsoft", + "sk.microsoft", + "sl.microsoft", + "es.microsoft", + "es.lucene", + "sv.microsoft", + "sv.lucene", + "ta.microsoft", + "te.microsoft", + "th.microsoft", + "th.lucene", + "tr.microsoft", + "tr.lucene", + "uk.microsoft", + "ur.microsoft", + "vi.microsoft", + "standard.lucene", + "standardasciifolding.lucene", + "keyword", + "pattern", + "simple", + "stop", + "whitespace" + ], + "x-ms-enum": { + "name": "LexicalAnalyzerName", + "modelAsString": true, + "values": [ + { + "value": "ar.microsoft", + "name": "ArMicrosoft", + "description": "Microsoft analyzer for Arabic." + }, + { + "value": "ar.lucene", + "name": "ArLucene", + "description": "Lucene analyzer for Arabic." + }, + { + "value": "hy.lucene", + "name": "HyLucene", + "description": "Lucene analyzer for Armenian." + }, + { + "value": "bn.microsoft", + "name": "BnMicrosoft", + "description": "Microsoft analyzer for Bangla." + }, + { + "value": "eu.lucene", + "name": "EuLucene", + "description": "Lucene analyzer for Basque." + }, + { + "value": "bg.microsoft", + "name": "BgMicrosoft", + "description": "Microsoft analyzer for Bulgarian." + }, + { + "value": "bg.lucene", + "name": "BgLucene", + "description": "Lucene analyzer for Bulgarian." + }, + { + "value": "ca.microsoft", + "name": "CaMicrosoft", + "description": "Microsoft analyzer for Catalan." + }, + { + "value": "ca.lucene", + "name": "CaLucene", + "description": "Lucene analyzer for Catalan." + }, + { + "value": "zh-Hans.microsoft", + "name": "ZhHansMicrosoft", + "description": "Microsoft analyzer for Chinese (Simplified)." + }, + { + "value": "zh-Hans.lucene", + "name": "ZhHansLucene", + "description": "Lucene analyzer for Chinese (Simplified)." + }, + { + "value": "zh-Hant.microsoft", + "name": "ZhHantMicrosoft", + "description": "Microsoft analyzer for Chinese (Traditional)." + }, + { + "value": "zh-Hant.lucene", + "name": "ZhHantLucene", + "description": "Lucene analyzer for Chinese (Traditional)." + }, + { + "value": "hr.microsoft", + "name": "HrMicrosoft", + "description": "Microsoft analyzer for Croatian." + }, + { + "value": "cs.microsoft", + "name": "CsMicrosoft", + "description": "Microsoft analyzer for Czech." + }, + { + "value": "cs.lucene", + "name": "CsLucene", + "description": "Lucene analyzer for Czech." + }, + { + "value": "da.microsoft", + "name": "DaMicrosoft", + "description": "Microsoft analyzer for Danish." + }, + { + "value": "da.lucene", + "name": "DaLucene", + "description": "Lucene analyzer for Danish." + }, + { + "value": "nl.microsoft", + "name": "NlMicrosoft", + "description": "Microsoft analyzer for Dutch." + }, + { + "value": "nl.lucene", + "name": "NlLucene", + "description": "Lucene analyzer for Dutch." + }, + { + "value": "en.microsoft", + "name": "EnMicrosoft", + "description": "Microsoft analyzer for English." + }, + { + "value": "en.lucene", + "name": "EnLucene", + "description": "Lucene analyzer for English." + }, + { + "value": "et.microsoft", + "name": "EtMicrosoft", + "description": "Microsoft analyzer for Estonian." + }, + { + "value": "fi.microsoft", + "name": "FiMicrosoft", + "description": "Microsoft analyzer for Finnish." + }, + { + "value": "fi.lucene", + "name": "FiLucene", + "description": "Lucene analyzer for Finnish." + }, + { + "value": "fr.microsoft", + "name": "FrMicrosoft", + "description": "Microsoft analyzer for French." + }, + { + "value": "fr.lucene", + "name": "FrLucene", + "description": "Lucene analyzer for French." + }, + { + "value": "gl.lucene", + "name": "GlLucene", + "description": "Lucene analyzer for Galician." + }, + { + "value": "de.microsoft", + "name": "DeMicrosoft", + "description": "Microsoft analyzer for German." + }, + { + "value": "de.lucene", + "name": "DeLucene", + "description": "Lucene analyzer for German." + }, + { + "value": "el.microsoft", + "name": "ElMicrosoft", + "description": "Microsoft analyzer for Greek." + }, + { + "value": "el.lucene", + "name": "ElLucene", + "description": "Lucene analyzer for Greek." + }, + { + "value": "gu.microsoft", + "name": "GuMicrosoft", + "description": "Microsoft analyzer for Gujarati." + }, + { + "value": "he.microsoft", + "name": "HeMicrosoft", + "description": "Microsoft analyzer for Hebrew." + }, + { + "value": "hi.microsoft", + "name": "HiMicrosoft", + "description": "Microsoft analyzer for Hindi." + }, + { + "value": "hi.lucene", + "name": "HiLucene", + "description": "Lucene analyzer for Hindi." + }, + { + "value": "hu.microsoft", + "name": "HuMicrosoft", + "description": "Microsoft analyzer for Hungarian." + }, + { + "value": "hu.lucene", + "name": "HuLucene", + "description": "Lucene analyzer for Hungarian." + }, + { + "value": "is.microsoft", + "name": "IsMicrosoft", + "description": "Microsoft analyzer for Icelandic." + }, + { + "value": "id.microsoft", + "name": "IdMicrosoft", + "description": "Microsoft analyzer for Indonesian (Bahasa)." + }, + { + "value": "id.lucene", + "name": "IdLucene", + "description": "Lucene analyzer for Indonesian." + }, + { + "value": "ga.lucene", + "name": "GaLucene", + "description": "Lucene analyzer for Irish." + }, + { + "value": "it.microsoft", + "name": "ItMicrosoft", + "description": "Microsoft analyzer for Italian." + }, + { + "value": "it.lucene", + "name": "ItLucene", + "description": "Lucene analyzer for Italian." + }, + { + "value": "ja.microsoft", + "name": "JaMicrosoft", + "description": "Microsoft analyzer for Japanese." + }, + { + "value": "ja.lucene", + "name": "JaLucene", + "description": "Lucene analyzer for Japanese." + }, + { + "value": "kn.microsoft", + "name": "KnMicrosoft", + "description": "Microsoft analyzer for Kannada." + }, + { + "value": "ko.microsoft", + "name": "KoMicrosoft", + "description": "Microsoft analyzer for Korean." + }, + { + "value": "ko.lucene", + "name": "KoLucene", + "description": "Lucene analyzer for Korean." + }, + { + "value": "lv.microsoft", + "name": "LvMicrosoft", + "description": "Microsoft analyzer for Latvian." + }, + { + "value": "lv.lucene", + "name": "LvLucene", + "description": "Lucene analyzer for Latvian." + }, + { + "value": "lt.microsoft", + "name": "LtMicrosoft", + "description": "Microsoft analyzer for Lithuanian." + }, + { + "value": "ml.microsoft", + "name": "MlMicrosoft", + "description": "Microsoft analyzer for Malayalam." + }, + { + "value": "ms.microsoft", + "name": "MsMicrosoft", + "description": "Microsoft analyzer for Malay (Latin)." + }, + { + "value": "mr.microsoft", + "name": "MrMicrosoft", + "description": "Microsoft analyzer for Marathi." + }, + { + "value": "nb.microsoft", + "name": "NbMicrosoft", + "description": "Microsoft analyzer for Norwegian (Bokmål)." + }, + { + "value": "no.lucene", + "name": "NoLucene", + "description": "Lucene analyzer for Norwegian." + }, + { + "value": "fa.lucene", + "name": "FaLucene", + "description": "Lucene analyzer for Persian." + }, + { + "value": "pl.microsoft", + "name": "PlMicrosoft", + "description": "Microsoft analyzer for Polish." + }, + { + "value": "pl.lucene", + "name": "PlLucene", + "description": "Lucene analyzer for Polish." + }, + { + "value": "pt-BR.microsoft", + "name": "PtBrMicrosoft", + "description": "Microsoft analyzer for Portuguese (Brazil)." + }, + { + "value": "pt-BR.lucene", + "name": "PtBrLucene", + "description": "Lucene analyzer for Portuguese (Brazil)." + }, + { + "value": "pt-PT.microsoft", + "name": "PtPtMicrosoft", + "description": "Microsoft analyzer for Portuguese (Portugal)." + }, + { + "value": "pt-PT.lucene", + "name": "PtPtLucene", + "description": "Lucene analyzer for Portuguese (Portugal)." + }, + { + "value": "pa.microsoft", + "name": "PaMicrosoft", + "description": "Microsoft analyzer for Punjabi." + }, + { + "value": "ro.microsoft", + "name": "RoMicrosoft", + "description": "Microsoft analyzer for Romanian." + }, + { + "value": "ro.lucene", + "name": "RoLucene", + "description": "Lucene analyzer for Romanian." + }, + { + "value": "ru.microsoft", + "name": "RuMicrosoft", + "description": "Microsoft analyzer for Russian." + }, + { + "value": "ru.lucene", + "name": "RuLucene", + "description": "Lucene analyzer for Russian." + }, + { + "value": "sr-cyrillic.microsoft", + "name": "SrCyrillicMicrosoft", + "description": "Microsoft analyzer for Serbian (Cyrillic)." + }, + { + "value": "sr-latin.microsoft", + "name": "SrLatinMicrosoft", + "description": "Microsoft analyzer for Serbian (Latin)." + }, + { + "value": "sk.microsoft", + "name": "SkMicrosoft", + "description": "Microsoft analyzer for Slovak." + }, + { + "value": "sl.microsoft", + "name": "SlMicrosoft", + "description": "Microsoft analyzer for Slovenian." + }, + { + "value": "es.microsoft", + "name": "EsMicrosoft", + "description": "Microsoft analyzer for Spanish." + }, + { + "value": "es.lucene", + "name": "EsLucene", + "description": "Lucene analyzer for Spanish." + }, + { + "value": "sv.microsoft", + "name": "SvMicrosoft", + "description": "Microsoft analyzer for Swedish." + }, + { + "value": "sv.lucene", + "name": "SvLucene", + "description": "Lucene analyzer for Swedish." + }, + { + "value": "ta.microsoft", + "name": "TaMicrosoft", + "description": "Microsoft analyzer for Tamil." + }, + { + "value": "te.microsoft", + "name": "TeMicrosoft", + "description": "Microsoft analyzer for Telugu." + }, + { + "value": "th.microsoft", + "name": "ThMicrosoft", + "description": "Microsoft analyzer for Thai." + }, + { + "value": "th.lucene", + "name": "ThLucene", + "description": "Lucene analyzer for Thai." + }, + { + "value": "tr.microsoft", + "name": "TrMicrosoft", + "description": "Microsoft analyzer for Turkish." + }, + { + "value": "tr.lucene", + "name": "TrLucene", + "description": "Lucene analyzer for Turkish." + }, + { + "value": "uk.microsoft", + "name": "UkMicrosoft", + "description": "Microsoft analyzer for Ukrainian." + }, + { + "value": "ur.microsoft", + "name": "UrMicrosoft", + "description": "Microsoft analyzer for Urdu." + }, + { + "value": "vi.microsoft", + "name": "ViMicrosoft", + "description": "Microsoft analyzer for Vietnamese." + }, + { + "value": "standard.lucene", + "name": "StandardLucene", + "description": "Standard Lucene analyzer." + }, + { + "value": "standardasciifolding.lucene", + "name": "StandardAsciiFoldingLucene", + "description": "Standard ASCII Folding Lucene analyzer. See https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search#Analyzers" + }, + { + "value": "keyword", + "name": "Keyword", + "description": "Treats the entire content of a field as a single token. This is useful for data like zip codes, ids, and some product names. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordAnalyzer.html" + }, + { + "value": "pattern", + "name": "Pattern", + "description": "Flexibly separates text into terms via a regular expression pattern. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/PatternAnalyzer.html" + }, + { + "value": "simple", + "name": "Simple", + "description": "Divides text at non-letters and converts them to lower case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/SimpleAnalyzer.html" + }, + { + "value": "stop", + "name": "Stop", + "description": "Divides text at non-letters; Applies the lowercase and stopword token filters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopAnalyzer.html" + }, + { + "value": "whitespace", + "name": "Whitespace", + "description": "An analyzer that uses the whitespace tokenizer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/WhitespaceAnalyzer.html" + } + ] + }, + "description": "Defines the names of all text analyzers supported by the search engine.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Language-support" + } + }, + "LexicalTokenizerName": { + "type": "string", + "enum": [ + "classic", + "edgeNGram", + "keyword_v2", + "letter", + "lowercase", + "microsoft_language_tokenizer", + "microsoft_language_stemming_tokenizer", + "nGram", + "path_hierarchy_v2", + "pattern", + "standard_v2", + "uax_url_email", + "whitespace" + ], + "x-ms-enum": { + "name": "LexicalTokenizerName", + "modelAsString": true, + "values": [ + { + "value": "classic", + "name": "Classic", + "description": "Grammar-based tokenizer that is suitable for processing most European-language documents. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/ClassicTokenizer.html" + }, + { + "value": "edgeNGram", + "name": "EdgeNGram", + "description": "Tokenizes the input from an edge into n-grams of the given size(s). See https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenizer.html" + }, + { + "value": "keyword_v2", + "name": "Keyword", + "description": "Emits the entire input as a single token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordTokenizer.html" + }, + { + "value": "letter", + "name": "Letter", + "description": "Divides text at non-letters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/LetterTokenizer.html" + }, + { + "value": "lowercase", + "name": "Lowercase", + "description": "Divides text at non-letters and converts them to lower case. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/LowerCaseTokenizer.html" + }, + { + "value": "microsoft_language_tokenizer", + "name": "MicrosoftLanguageTokenizer", + "description": "Divides text using language-specific rules." + }, + { + "value": "microsoft_language_stemming_tokenizer", + "name": "MicrosoftLanguageStemmingTokenizer", + "description": "Divides text using language-specific rules and reduces words to their base forms." + }, + { + "value": "nGram", + "name": "NGram", + "description": "Tokenizes the input into n-grams of the given size(s). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenizer.html" + }, + { + "value": "path_hierarchy_v2", + "name": "PathHierarchy", + "description": "Tokenizer for path-like hierarchies. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/path/PathHierarchyTokenizer.html" + }, + { + "value": "pattern", + "name": "Pattern", + "description": "Tokenizer that uses regex pattern matching to construct distinct tokens. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternTokenizer.html" + }, + { + "value": "standard_v2", + "name": "Standard", + "description": "Standard Lucene analyzer; Composed of the standard tokenizer, lowercase filter and stop filter. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/StandardTokenizer.html" + }, + { + "value": "uax_url_email", + "name": "UaxUrlEmail", + "description": "Tokenizes urls and emails as one token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/UAX29URLEmailTokenizer.html" + }, + { + "value": "whitespace", + "name": "Whitespace", + "description": "Divides text at whitespace. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/WhitespaceTokenizer.html" + } + ] + }, + "description": "Defines the names of all tokenizers supported by the search engine.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "TokenFilterName": { + "type": "string", + "enum": [ + "arabic_normalization", + "apostrophe", + "asciifolding", + "cjk_bigram", + "cjk_width", + "classic", + "common_grams", + "edgeNGram_v2", + "elision", + "german_normalization", + "hindi_normalization", + "indic_normalization", + "keyword_repeat", + "kstem", + "length", + "limit", + "lowercase", + "nGram_v2", + "persian_normalization", + "phonetic", + "porter_stem", + "reverse", + "scandinavian_normalization", + "scandinavian_folding", + "shingle", + "snowball", + "sorani_normalization", + "stemmer", + "stopwords", + "trim", + "truncate", + "unique", + "uppercase", + "word_delimiter" + ], + "x-ms-enum": { + "name": "TokenFilterName", + "modelAsString": true, + "values": [ + { + "value": "arabic_normalization", + "name": "ArabicNormalization", + "description": "A token filter that applies the Arabic normalizer to normalize the orthography. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ar/ArabicNormalizationFilter.html" + }, + { + "value": "apostrophe", + "name": "Apostrophe", + "description": "Strips all characters after an apostrophe (including the apostrophe itself). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/tr/ApostropheFilter.html" + }, + { + "value": "asciifolding", + "name": "AsciiFolding", + "description": "Converts alphabetic, numeric, and symbolic Unicode characters which are not in the first 127 ASCII characters (the \"Basic Latin\" Unicode block) into their ASCII equivalents, if such equivalents exist. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ASCIIFoldingFilter.html" + }, + { + "value": "cjk_bigram", + "name": "CjkBigram", + "description": "Forms bigrams of CJK terms that are generated from the standard tokenizer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/cjk/CJKBigramFilter.html" + }, + { + "value": "cjk_width", + "name": "CjkWidth", + "description": "Normalizes CJK width differences. Folds fullwidth ASCII variants into the equivalent basic Latin, and half-width Katakana variants into the equivalent Kana. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/cjk/CJKWidthFilter.html" + }, + { + "value": "classic", + "name": "Classic", + "description": "Removes English possessives, and dots from acronyms. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/ClassicFilter.html" + }, + { + "value": "common_grams", + "name": "CommonGram", + "description": "Construct bigrams for frequently occurring terms while indexing. Single terms are still indexed too, with bigrams overlaid. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/commongrams/CommonGramsFilter.html" + }, + { + "value": "edgeNGram_v2", + "name": "EdgeNGram", + "description": "Generates n-grams of the given size(s) starting from the front or the back of an input token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenFilter.html" + }, + { + "value": "elision", + "name": "Elision", + "description": "Removes elisions. For example, \"l'avion\" (the plane) will be converted to \"avion\" (plane). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/util/ElisionFilter.html" + }, + { + "value": "german_normalization", + "name": "GermanNormalization", + "description": "Normalizes German characters according to the heuristics of the German2 snowball algorithm. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/de/GermanNormalizationFilter.html" + }, + { + "value": "hindi_normalization", + "name": "HindiNormalization", + "description": "Normalizes text in Hindi to remove some differences in spelling variations. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/hi/HindiNormalizationFilter.html" + }, + { + "value": "indic_normalization", + "name": "IndicNormalization", + "description": "Normalizes the Unicode representation of text in Indian languages. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/in/IndicNormalizationFilter.html" + }, + { + "value": "keyword_repeat", + "name": "KeywordRepeat", + "description": "Emits each incoming token twice, once as keyword and once as non-keyword. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/KeywordRepeatFilter.html" + }, + { + "value": "kstem", + "name": "KStem", + "description": "A high-performance kstem filter for English. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/en/KStemFilter.html" + }, + { + "value": "length", + "name": "Length", + "description": "Removes words that are too long or too short. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LengthFilter.html" + }, + { + "value": "limit", + "name": "Limit", + "description": "Limits the number of tokens while indexing. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LimitTokenCountFilter.html" + }, + { + "value": "lowercase", + "name": "Lowercase", + "description": "Normalizes token text to lower case. See https://lucene.apache.org/core/6_6_1/analyzers-common/org/apache/lucene/analysis/core/LowerCaseFilter.html" + }, + { + "value": "nGram_v2", + "name": "NGram", + "description": "Generates n-grams of the given size(s). See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenFilter.html" + }, + { + "value": "persian_normalization", + "name": "PersianNormalization", + "description": "Applies normalization for Persian. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/fa/PersianNormalizationFilter.html" + }, + { + "value": "phonetic", + "name": "Phonetic", + "description": "Create tokens for phonetic matches. See https://lucene.apache.org/core/4_10_3/analyzers-phonetic/org/apache/lucene/analysis/phonetic/package-tree.html" + }, + { + "value": "porter_stem", + "name": "PorterStem", + "description": "Uses the Porter stemming algorithm to transform the token stream. See http://tartarus.org/~martin/PorterStemmer" + }, + { + "value": "reverse", + "name": "Reverse", + "description": "Reverses the token string. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/reverse/ReverseStringFilter.html" + }, + { + "value": "scandinavian_normalization", + "name": "ScandinavianNormalization", + "description": "Normalizes use of the interchangeable Scandinavian characters. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ScandinavianNormalizationFilter.html" + }, + { + "value": "scandinavian_folding", + "name": "ScandinavianFoldingNormalization", + "description": "Folds Scandinavian characters åÅäæÄÆ->a and öÖøØ->o. It also discriminates against use of double vowels aa, ae, ao, oe and oo, leaving just the first one. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ScandinavianFoldingFilter.html" + }, + { + "value": "shingle", + "name": "Shingle", + "description": "Creates combinations of tokens as a single token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/shingle/ShingleFilter.html" + }, + { + "value": "snowball", + "name": "Snowball", + "description": "A filter that stems words using a Snowball-generated stemmer. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/snowball/SnowballFilter.html" + }, + { + "value": "sorani_normalization", + "name": "SoraniNormalization", + "description": "Normalizes the Unicode representation of Sorani text. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ckb/SoraniNormalizationFilter.html" + }, + { + "value": "stemmer", + "name": "Stemmer", + "description": "Language specific stemming filter. See https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search#TokenFilters" + }, + { + "value": "stopwords", + "name": "Stopwords", + "description": "Removes stop words from a token stream. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopFilter.html" + }, + { + "value": "trim", + "name": "Trim", + "description": "Trims leading and trailing whitespace from tokens. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/TrimFilter.html" + }, + { + "value": "truncate", + "name": "Truncate", + "description": "Truncates the terms to a specific length. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/TruncateTokenFilter.html" + }, + { + "value": "unique", + "name": "Unique", + "description": "Filters out tokens with same text as the previous token. See http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/RemoveDuplicatesTokenFilter.html" + }, + { + "value": "uppercase", + "name": "Uppercase", + "description": "Normalizes token text to upper case. See https://lucene.apache.org/core/6_6_1/analyzers-common/org/apache/lucene/analysis/core/UpperCaseFilter.html" + }, + { + "value": "word_delimiter", + "name": "WordDelimiter", + "description": "Splits words into subwords and performs optional transformations on subword groups." + } + ] + }, + "description": "Defines the names of all token filters supported by the search engine.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "CharFilterName": { + "type": "string", + "enum": [ + "html_strip" + ], + "x-ms-enum": { + "name": "CharFilterName", + "modelAsString": true, + "values": [ + { + "value": "html_strip", + "name": "HtmlStrip", + "description": "A character filter that attempts to strip out HTML constructs. See https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/charfilter/HTMLStripCharFilter.html" + } + ] + }, + "description": "Defines the names of all character filters supported by the search engine.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "RegexFlags": { + "type": "string", + "enum": [ + "CANON_EQ", + "CASE_INSENSITIVE", + "COMMENTS", + "DOTALL", + "LITERAL", + "MULTILINE", + "UNICODE_CASE", + "UNIX_LINES" + ], + "x-ms-enum": { + "name": "RegexFlags", + "modelAsString": true, + "values": [ + { + "value": "CANON_EQ", + "name": "CanonEq", + "description": "Enables canonical equivalence." + }, + { + "value": "CASE_INSENSITIVE", + "name": "CaseInsensitive", + "description": "Enables case-insensitive matching." + }, + { + "value": "COMMENTS", + "name": "Comments", + "description": "Permits whitespace and comments in the pattern." + }, + { + "value": "DOTALL", + "name": "DotAll", + "description": "Enables dotall mode." + }, + { + "value": "LITERAL", + "name": "Literal", + "description": "Enables literal parsing of the pattern." + }, + { + "value": "MULTILINE", + "name": "Multiline", + "description": "Enables multiline mode." + }, + { + "value": "UNICODE_CASE", + "name": "UnicodeCase", + "description": "Enables Unicode-aware case folding." + }, + { + "value": "UNIX_LINES", + "name": "UnixLines", + "description": "Enables Unix lines mode." + } + ] + }, + "description": "Defines flags that can be combined to control how regular expressions are used in the pattern analyzer and pattern tokenizer.", + "externalDocs": { + "url": "http://docs.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html#field_summary" + } + }, + "VectorEncodingFormat": { + "type": "string", + "enum": [ + "packedBit" + ], + "x-ms-enum": { + "name": "VectorEncodingFormat", + "modelAsString": true, + "values": [ + { + "value": "packedBit", + "name": "PackedBit", + "description": "Encoding format representing bits packed into a wider data type." + } + ] + }, + "description": "The encoding format for interpreting vector field contents." + }, + "SearchFieldDataType": { + "type": "string", + "enum": [ + "Edm.String", + "Edm.Int32", + "Edm.Int64", + "Edm.Double", + "Edm.Boolean", + "Edm.DateTimeOffset", + "Edm.GeographyPoint", + "Edm.ComplexType", + "Edm.Single", + "Edm.Half", + "Edm.Int16", + "Edm.SByte", + "Edm.Byte" + ], + "x-ms-enum": { + "name": "SearchFieldDataType", + "modelAsString": true, + "values": [ + { + "value": "Edm.String", + "name": "String", + "description": "Indicates that a field contains a string." + }, + { + "value": "Edm.Int32", + "name": "Int32", + "description": "Indicates that a field contains a 32-bit signed integer." + }, + { + "value": "Edm.Int64", + "name": "Int64", + "description": "Indicates that a field contains a 64-bit signed integer." + }, + { + "value": "Edm.Double", + "name": "Double", + "description": "Indicates that a field contains an IEEE double-precision floating point number." + }, + { + "value": "Edm.Boolean", + "name": "Boolean", + "description": "Indicates that a field contains a Boolean value (true or false)." + }, + { + "value": "Edm.DateTimeOffset", + "name": "DateTimeOffset", + "description": "Indicates that a field contains a date/time value, including timezone information." + }, + { + "value": "Edm.GeographyPoint", + "name": "GeographyPoint", + "description": "Indicates that a field contains a geo-location in terms of longitude and latitude." + }, + { + "value": "Edm.ComplexType", + "name": "Complex", + "description": "Indicates that a field contains one or more complex objects that in turn have sub-fields of other types." + }, + { + "value": "Edm.Single", + "name": "Single", + "description": "Indicates that a field contains a single-precision floating point number. This is only valid when used with Collection(Edm.Single)." + }, + { + "value": "Edm.Half", + "name": "Half", + "description": "Indicates that a field contains a half-precision floating point number. This is only valid when used with Collection(Edm.Half)." + }, + { + "value": "Edm.Int16", + "name": "Int16", + "description": "Indicates that a field contains a 16-bit signed integer. This is only valid when used with Collection(Edm.Int16)." + }, + { + "value": "Edm.SByte", + "name": "SByte", + "description": "Indicates that a field contains a 8-bit signed integer. This is only valid when used with Collection(Edm.SByte)." + }, + { + "value": "Edm.Byte", + "name": "Byte", + "description": "Indicates that a field contains a 8-bit unsigned integer. This is only valid when used with Collection(Edm.Byte)." + } + ] + }, + "description": "Defines the data type of a field in a search index." + }, + "LexicalAnalyzer": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of analyzer." + }, + "name": { + "type": "string", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/custom-analyzers-in-azure-search#index-attribute-reference" + }, + "description": "The name of the analyzer. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters." + } + }, + "required": [ + "@odata.type", + "name" + ], + "description": "Base type for analyzers." + }, + "CustomAnalyzer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.CustomAnalyzer", + "allOf": [ + { + "$ref": "#/definitions/LexicalAnalyzer" + } + ], + "properties": { + "tokenizer": { + "$ref": "#/definitions/LexicalTokenizerName", + "description": "The name of the tokenizer to use to divide continuous text into a sequence of tokens, such as breaking a sentence into words." + }, + "tokenFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/TokenFilterName", + "x-nullable": false + }, + "description": "A list of token filters used to filter out or modify the tokens generated by a tokenizer. For example, you can specify a lowercase filter that converts all characters to lowercase. The filters are run in the order in which they are listed." + }, + "charFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/CharFilterName", + "x-nullable": false + }, + "description": "A list of character filters used to prepare input text before it is processed by the tokenizer. For instance, they can replace certain characters or symbols. The filters are run in the order in which they are listed." + } + }, + "required": [ + "tokenizer" + ], + "description": "Allows you to take control over the process of converting text into indexable/searchable tokens. It's a user-defined configuration consisting of a single predefined tokenizer and one or more filters. The tokenizer is responsible for breaking text into tokens, and the filters for modifying tokens emitted by the tokenizer." + }, + "PatternAnalyzer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PatternAnalyzer", + "allOf": [ + { + "$ref": "#/definitions/LexicalAnalyzer" + } + ], + "properties": { + "lowercase": { + "x-ms-client-name": "LowerCaseTerms", + "type": "boolean", + "default": true, + "description": "A value indicating whether terms should be lower-cased. Default is true." + }, + "pattern": { + "type": "string", + "default": "\\W+", + "description": "A regular expression pattern to match token separators. Default is an expression that matches one or more non-word characters." + }, + "flags": { + "$ref": "#/definitions/RegexFlags", + "description": "Regular expression flags." + }, + "stopwords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of stopwords." + } + }, + "description": "Flexibly separates text into terms via a regular expression pattern. This analyzer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/PatternAnalyzer.html" + } + }, + "LuceneStandardAnalyzer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StandardAnalyzer", + "allOf": [ + { + "$ref": "#/definitions/LexicalAnalyzer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Default is 255. Tokens longer than the maximum length are split. The maximum token length that can be used is 300 characters." + }, + "stopwords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of stopwords." + } + }, + "description": "Standard Apache Lucene analyzer; Composed of the standard tokenizer, lowercase filter and stop filter.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/StandardAnalyzer.html" + } + }, + "StopAnalyzer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StopAnalyzer", + "allOf": [ + { + "$ref": "#/definitions/LexicalAnalyzer" + } + ], + "properties": { + "stopwords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of stopwords." + } + }, + "description": "Divides text at non-letters; Applies the lowercase and stopword token filters. This analyzer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopAnalyzer.html" + } + }, + "LexicalTokenizer": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of tokenizer." + }, + "name": { + "type": "string", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/custom-analyzers-in-azure-search#index-attribute-reference" + }, + "description": "The name of the tokenizer. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters." + } + }, + "required": [ + "@odata.type", + "name" + ], + "description": "Base type for tokenizers.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "ClassicTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.ClassicTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Default is 255. Tokens longer than the maximum length are split. The maximum token length that can be used is 300 characters." + } + }, + "description": "Grammar-based tokenizer that is suitable for processing most European-language documents. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/ClassicTokenizer.html" + } + }, + "TokenCharacterKind": { + "type": "string", + "enum": [ + "letter", + "digit", + "whitespace", + "punctuation", + "symbol" + ], + "x-ms-enum": { + "name": "TokenCharacterKind", + "modelAsString": false, + "values": [ + { + "value": "letter", + "name": "Letter", + "description": "Keeps letters in tokens." + }, + { + "value": "digit", + "name": "Digit", + "description": "Keeps digits in tokens." + }, + { + "value": "whitespace", + "name": "Whitespace", + "description": "Keeps whitespace in tokens." + }, + { + "value": "punctuation", + "name": "Punctuation", + "description": "Keeps punctuation in tokens." + }, + { + "value": "symbol", + "name": "Symbol", + "description": "Keeps symbols in tokens." + } + ] + }, + "description": "Represents classes of characters on which a token filter can operate." + }, + "EdgeNGramTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.EdgeNGramTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "maximum": 300, + "description": "The minimum n-gram length. Default is 1. Maximum is 300. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "maximum": 300, + "description": "The maximum n-gram length. Default is 2. Maximum is 300." + }, + "tokenChars": { + "type": "array", + "items": { + "$ref": "#/definitions/TokenCharacterKind", + "x-nullable": false + }, + "description": "Character classes to keep in the tokens." + } + }, + "description": "Tokenizes the input from an edge into n-grams of the given size(s). This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenizer.html" + } + }, + "KeywordTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.KeywordTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "bufferSize": { + "type": "integer", + "format": "int32", + "default": 256, + "description": "The read buffer size in bytes. Default is 256." + } + }, + "description": "Emits the entire input as a single token. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordTokenizer.html" + }, + "x-az-search-deprecated": true + }, + "KeywordTokenizerV2": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.KeywordTokenizerV2", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 256, + "maximum": 300, + "description": "The maximum token length. Default is 256. Tokens longer than the maximum length are split. The maximum token length that can be used is 300 characters." + } + }, + "description": "Emits the entire input as a single token. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/KeywordTokenizer.html" + } + }, + "MicrosoftTokenizerLanguage": { + "type": "string", + "enum": [ + "bangla", + "bulgarian", + "catalan", + "chineseSimplified", + "chineseTraditional", + "croatian", + "czech", + "danish", + "dutch", + "english", + "french", + "german", + "greek", + "gujarati", + "hindi", + "icelandic", + "indonesian", + "italian", + "japanese", + "kannada", + "korean", + "malay", + "malayalam", + "marathi", + "norwegianBokmaal", + "polish", + "portuguese", + "portugueseBrazilian", + "punjabi", + "romanian", + "russian", + "serbianCyrillic", + "serbianLatin", + "slovenian", + "spanish", + "swedish", + "tamil", + "telugu", + "thai", + "ukrainian", + "urdu", + "vietnamese" + ], + "x-ms-enum": { + "name": "MicrosoftTokenizerLanguage", + "modelAsString": false, + "values": [ + { + "value": "bangla", + "name": "Bangla", + "description": "Selects the Microsoft tokenizer for Bangla." + }, + { + "value": "bulgarian", + "name": "Bulgarian", + "description": "Selects the Microsoft tokenizer for Bulgarian." + }, + { + "value": "catalan", + "name": "Catalan", + "description": "Selects the Microsoft tokenizer for Catalan." + }, + { + "value": "chineseSimplified", + "name": "ChineseSimplified", + "description": "Selects the Microsoft tokenizer for Chinese (Simplified)." + }, + { + "value": "chineseTraditional", + "name": "ChineseTraditional", + "description": "Selects the Microsoft tokenizer for Chinese (Traditional)." + }, + { + "value": "croatian", + "name": "Croatian", + "description": "Selects the Microsoft tokenizer for Croatian." + }, + { + "value": "czech", + "name": "Czech", + "description": "Selects the Microsoft tokenizer for Czech." + }, + { + "value": "danish", + "name": "Danish", + "description": "Selects the Microsoft tokenizer for Danish." + }, + { + "value": "dutch", + "name": "Dutch", + "description": "Selects the Microsoft tokenizer for Dutch." + }, + { + "value": "english", + "name": "English", + "description": "Selects the Microsoft tokenizer for English." + }, + { + "value": "french", + "name": "French", + "description": "Selects the Microsoft tokenizer for French." + }, + { + "value": "german", + "name": "German", + "description": "Selects the Microsoft tokenizer for German." + }, + { + "value": "greek", + "name": "Greek", + "description": "Selects the Microsoft tokenizer for Greek." + }, + { + "value": "gujarati", + "name": "Gujarati", + "description": "Selects the Microsoft tokenizer for Gujarati." + }, + { + "value": "hindi", + "name": "Hindi", + "description": "Selects the Microsoft tokenizer for Hindi." + }, + { + "value": "icelandic", + "name": "Icelandic", + "description": "Selects the Microsoft tokenizer for Icelandic." + }, + { + "value": "indonesian", + "name": "Indonesian", + "description": "Selects the Microsoft tokenizer for Indonesian." + }, + { + "value": "italian", + "name": "Italian", + "description": "Selects the Microsoft tokenizer for Italian." + }, + { + "value": "japanese", + "name": "Japanese", + "description": "Selects the Microsoft tokenizer for Japanese." + }, + { + "value": "kannada", + "name": "Kannada", + "description": "Selects the Microsoft tokenizer for Kannada." + }, + { + "value": "korean", + "name": "Korean", + "description": "Selects the Microsoft tokenizer for Korean." + }, + { + "value": "malay", + "name": "Malay", + "description": "Selects the Microsoft tokenizer for Malay." + }, + { + "value": "malayalam", + "name": "Malayalam", + "description": "Selects the Microsoft tokenizer for Malayalam." + }, + { + "value": "marathi", + "name": "Marathi", + "description": "Selects the Microsoft tokenizer for Marathi." + }, + { + "value": "norwegianBokmaal", + "name": "NorwegianBokmaal", + "description": "Selects the Microsoft tokenizer for Norwegian (Bokmål)." + }, + { + "value": "polish", + "name": "Polish", + "description": "Selects the Microsoft tokenizer for Polish." + }, + { + "value": "portuguese", + "name": "Portuguese", + "description": "Selects the Microsoft tokenizer for Portuguese." + }, + { + "value": "portugueseBrazilian", + "name": "PortugueseBrazilian", + "description": "Selects the Microsoft tokenizer for Portuguese (Brazil)." + }, + { + "value": "punjabi", + "name": "Punjabi", + "description": "Selects the Microsoft tokenizer for Punjabi." + }, + { + "value": "romanian", + "name": "Romanian", + "description": "Selects the Microsoft tokenizer for Romanian." + }, + { + "value": "russian", + "name": "Russian", + "description": "Selects the Microsoft tokenizer for Russian." + }, + { + "value": "serbianCyrillic", + "name": "SerbianCyrillic", + "description": "Selects the Microsoft tokenizer for Serbian (Cyrillic)." + }, + { + "value": "serbianLatin", + "name": "SerbianLatin", + "description": "Selects the Microsoft tokenizer for Serbian (Latin)." + }, + { + "value": "slovenian", + "name": "Slovenian", + "description": "Selects the Microsoft tokenizer for Slovenian." + }, + { + "value": "spanish", + "name": "Spanish", + "description": "Selects the Microsoft tokenizer for Spanish." + }, + { + "value": "swedish", + "name": "Swedish", + "description": "Selects the Microsoft tokenizer for Swedish." + }, + { + "value": "tamil", + "name": "Tamil", + "description": "Selects the Microsoft tokenizer for Tamil." + }, + { + "value": "telugu", + "name": "Telugu", + "description": "Selects the Microsoft tokenizer for Telugu." + }, + { + "value": "thai", + "name": "Thai", + "description": "Selects the Microsoft tokenizer for Thai." + }, + { + "value": "ukrainian", + "name": "Ukrainian", + "description": "Selects the Microsoft tokenizer for Ukrainian." + }, + { + "value": "urdu", + "name": "Urdu", + "description": "Selects the Microsoft tokenizer for Urdu." + }, + { + "value": "vietnamese", + "name": "Vietnamese", + "description": "Selects the Microsoft tokenizer for Vietnamese." + } + ] + }, + "description": "Lists the languages supported by the Microsoft language tokenizer." + }, + "MicrosoftLanguageTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.MicrosoftLanguageTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Tokens longer than the maximum length are split. Maximum token length that can be used is 300 characters. Tokens longer than 300 characters are first split into tokens of length 300 and then each of those tokens is split based on the max token length set. Default is 255." + }, + "isSearchTokenizer": { + "type": "boolean", + "default": false, + "description": "A value indicating how the tokenizer is used. Set to true if used as the search tokenizer, set to false if used as the indexing tokenizer. Default is false." + }, + "language": { + "$ref": "#/definitions/MicrosoftTokenizerLanguage", + "description": "The language to use. The default is English." + } + }, + "description": "Divides text using language-specific rules." + }, + "MicrosoftStemmingTokenizerLanguage": { + "type": "string", + "enum": [ + "arabic", + "bangla", + "bulgarian", + "catalan", + "croatian", + "czech", + "danish", + "dutch", + "english", + "estonian", + "finnish", + "french", + "german", + "greek", + "gujarati", + "hebrew", + "hindi", + "hungarian", + "icelandic", + "indonesian", + "italian", + "kannada", + "latvian", + "lithuanian", + "malay", + "malayalam", + "marathi", + "norwegianBokmaal", + "polish", + "portuguese", + "portugueseBrazilian", + "punjabi", + "romanian", + "russian", + "serbianCyrillic", + "serbianLatin", + "slovak", + "slovenian", + "spanish", + "swedish", + "tamil", + "telugu", + "turkish", + "ukrainian", + "urdu" + ], + "x-ms-enum": { + "name": "MicrosoftStemmingTokenizerLanguage", + "modelAsString": false, + "values": [ + { + "value": "arabic", + "name": "Arabic", + "description": "Selects the Microsoft stemming tokenizer for Arabic." + }, + { + "value": "bangla", + "name": "Bangla", + "description": "Selects the Microsoft stemming tokenizer for Bangla." + }, + { + "value": "bulgarian", + "name": "Bulgarian", + "description": "Selects the Microsoft stemming tokenizer for Bulgarian." + }, + { + "value": "catalan", + "name": "Catalan", + "description": "Selects the Microsoft stemming tokenizer for Catalan." + }, + { + "value": "croatian", + "name": "Croatian", + "description": "Selects the Microsoft stemming tokenizer for Croatian." + }, + { + "value": "czech", + "name": "Czech", + "description": "Selects the Microsoft stemming tokenizer for Czech." + }, + { + "value": "danish", + "name": "Danish", + "description": "Selects the Microsoft stemming tokenizer for Danish." + }, + { + "value": "dutch", + "name": "Dutch", + "description": "Selects the Microsoft stemming tokenizer for Dutch." + }, + { + "value": "english", + "name": "English", + "description": "Selects the Microsoft stemming tokenizer for English." + }, + { + "value": "estonian", + "name": "Estonian", + "description": "Selects the Microsoft stemming tokenizer for Estonian." + }, + { + "value": "finnish", + "name": "Finnish", + "description": "Selects the Microsoft stemming tokenizer for Finnish." + }, + { + "value": "french", + "name": "French", + "description": "Selects the Microsoft stemming tokenizer for French." + }, + { + "value": "german", + "name": "German", + "description": "Selects the Microsoft stemming tokenizer for German." + }, + { + "value": "greek", + "name": "Greek", + "description": "Selects the Microsoft stemming tokenizer for Greek." + }, + { + "value": "gujarati", + "name": "Gujarati", + "description": "Selects the Microsoft stemming tokenizer for Gujarati." + }, + { + "value": "hebrew", + "name": "Hebrew", + "description": "Selects the Microsoft stemming tokenizer for Hebrew." + }, + { + "value": "hindi", + "name": "Hindi", + "description": "Selects the Microsoft stemming tokenizer for Hindi." + }, + { + "value": "hungarian", + "name": "Hungarian", + "description": "Selects the Microsoft stemming tokenizer for Hungarian." + }, + { + "value": "icelandic", + "name": "Icelandic", + "description": "Selects the Microsoft stemming tokenizer for Icelandic." + }, + { + "value": "indonesian", + "name": "Indonesian", + "description": "Selects the Microsoft stemming tokenizer for Indonesian." + }, + { + "value": "italian", + "name": "Italian", + "description": "Selects the Microsoft stemming tokenizer for Italian." + }, + { + "value": "kannada", + "name": "Kannada", + "description": "Selects the Microsoft stemming tokenizer for Kannada." + }, + { + "value": "latvian", + "name": "Latvian", + "description": "Selects the Microsoft stemming tokenizer for Latvian." + }, + { + "value": "lithuanian", + "name": "Lithuanian", + "description": "Selects the Microsoft stemming tokenizer for Lithuanian." + }, + { + "value": "malay", + "name": "Malay", + "description": "Selects the Microsoft stemming tokenizer for Malay." + }, + { + "value": "malayalam", + "name": "Malayalam", + "description": "Selects the Microsoft stemming tokenizer for Malayalam." + }, + { + "value": "marathi", + "name": "Marathi", + "description": "Selects the Microsoft stemming tokenizer for Marathi." + }, + { + "value": "norwegianBokmaal", + "name": "NorwegianBokmaal", + "description": "Selects the Microsoft stemming tokenizer for Norwegian (Bokmål)." + }, + { + "value": "polish", + "name": "Polish", + "description": "Selects the Microsoft stemming tokenizer for Polish." + }, + { + "value": "portuguese", + "name": "Portuguese", + "description": "Selects the Microsoft stemming tokenizer for Portuguese." + }, + { + "value": "portugueseBrazilian", + "name": "PortugueseBrazilian", + "description": "Selects the Microsoft stemming tokenizer for Portuguese (Brazil)." + }, + { + "value": "punjabi", + "name": "Punjabi", + "description": "Selects the Microsoft stemming tokenizer for Punjabi." + }, + { + "value": "romanian", + "name": "Romanian", + "description": "Selects the Microsoft stemming tokenizer for Romanian." + }, + { + "value": "russian", + "name": "Russian", + "description": "Selects the Microsoft stemming tokenizer for Russian." + }, + { + "value": "serbianCyrillic", + "name": "SerbianCyrillic", + "description": "Selects the Microsoft stemming tokenizer for Serbian (Cyrillic)." + }, + { + "value": "serbianLatin", + "name": "SerbianLatin", + "description": "Selects the Microsoft stemming tokenizer for Serbian (Latin)." + }, + { + "value": "slovak", + "name": "Slovak", + "description": "Selects the Microsoft stemming tokenizer for Slovak." + }, + { + "value": "slovenian", + "name": "Slovenian", + "description": "Selects the Microsoft stemming tokenizer for Slovenian." + }, + { + "value": "spanish", + "name": "Spanish", + "description": "Selects the Microsoft stemming tokenizer for Spanish." + }, + { + "value": "swedish", + "name": "Swedish", + "description": "Selects the Microsoft stemming tokenizer for Swedish." + }, + { + "value": "tamil", + "name": "Tamil", + "description": "Selects the Microsoft stemming tokenizer for Tamil." + }, + { + "value": "telugu", + "name": "Telugu", + "description": "Selects the Microsoft stemming tokenizer for Telugu." + }, + { + "value": "turkish", + "name": "Turkish", + "description": "Selects the Microsoft stemming tokenizer for Turkish." + }, + { + "value": "ukrainian", + "name": "Ukrainian", + "description": "Selects the Microsoft stemming tokenizer for Ukrainian." + }, + { + "value": "urdu", + "name": "Urdu", + "description": "Selects the Microsoft stemming tokenizer for Urdu." + } + ] + }, + "description": "Lists the languages supported by the Microsoft language stemming tokenizer." + }, + "MicrosoftLanguageStemmingTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.MicrosoftLanguageStemmingTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Tokens longer than the maximum length are split. Maximum token length that can be used is 300 characters. Tokens longer than 300 characters are first split into tokens of length 300 and then each of those tokens is split based on the max token length set. Default is 255." + }, + "isSearchTokenizer": { + "type": "boolean", + "default": false, + "description": "A value indicating how the tokenizer is used. Set to true if used as the search tokenizer, set to false if used as the indexing tokenizer. Default is false." + }, + "language": { + "$ref": "#/definitions/MicrosoftStemmingTokenizerLanguage", + "description": "The language to use. The default is English." + } + }, + "description": "Divides text using language-specific rules and reduces words to their base forms." + }, + "NGramTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.NGramTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "maximum": 300, + "description": "The minimum n-gram length. Default is 1. Maximum is 300. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "maximum": 300, + "description": "The maximum n-gram length. Default is 2. Maximum is 300." + }, + "tokenChars": { + "type": "array", + "items": { + "$ref": "#/definitions/TokenCharacterKind", + "x-nullable": false + }, + "description": "Character classes to keep in the tokens." + } + }, + "description": "Tokenizes the input into n-grams of the given size(s). This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenizer.html" + } + }, + "PathHierarchyTokenizerV2": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PathHierarchyTokenizerV2", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "delimiter": { + "type": "string", + "format": "char", + "default": "/", + "description": "The delimiter character to use. Default is \"/\"." + }, + "replacement": { + "type": "string", + "format": "char", + "default": "/", + "description": "A value that, if set, replaces the delimiter character. Default is \"/\"." + }, + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 300, + "maximum": 300, + "description": "The maximum token length. Default and maximum is 300." + }, + "reverse": { + "x-ms-client-name": "ReverseTokenOrder", + "type": "boolean", + "default": false, + "description": "A value indicating whether to generate tokens in reverse order. Default is false." + }, + "skip": { + "x-ms-client-name": "NumberOfTokensToSkip", + "type": "integer", + "format": "int32", + "default": 0, + "description": "The number of initial tokens to skip. Default is 0." + } + }, + "description": "Tokenizer for path-like hierarchies. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/path/PathHierarchyTokenizer.html" + } + }, + "PatternTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PatternTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "pattern": { + "type": "string", + "default": "\\W+", + "description": "A regular expression pattern to match token separators. Default is an expression that matches one or more non-word characters." + }, + "flags": { + "$ref": "#/definitions/RegexFlags", + "description": "Regular expression flags." + }, + "group": { + "type": "integer", + "format": "int32", + "default": -1, + "description": "The zero-based ordinal of the matching group in the regular expression pattern to extract into tokens. Use -1 if you want to use the entire pattern to split the input into tokens, irrespective of matching groups. Default is -1." + } + }, + "description": "Tokenizer that uses regex pattern matching to construct distinct tokens. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternTokenizer.html" + } + }, + "LuceneStandardTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StandardTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "description": "The maximum token length. Default is 255. Tokens longer than the maximum length are split." + } + }, + "description": "Breaks text following the Unicode Text Segmentation rules. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/StandardTokenizer.html" + }, + "x-az-search-deprecated": true + }, + "LuceneStandardTokenizerV2": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StandardTokenizerV2", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Default is 255. Tokens longer than the maximum length are split. The maximum token length that can be used is 300 characters." + } + }, + "description": "Breaks text following the Unicode Text Segmentation rules. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/StandardTokenizer.html" + } + }, + "UaxUrlEmailTokenizer": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.UaxUrlEmailTokenizer", + "allOf": [ + { + "$ref": "#/definitions/LexicalTokenizer" + } + ], + "properties": { + "maxTokenLength": { + "type": "integer", + "format": "int32", + "default": 255, + "maximum": 300, + "description": "The maximum token length. Default is 255. Tokens longer than the maximum length are split. The maximum token length that can be used is 300 characters." + } + }, + "description": "Tokenizes urls and emails as one token. This tokenizer is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/standard/UAX29URLEmailTokenizer.html" + } + }, + "TokenFilter": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of token filter." + }, + "name": { + "type": "string", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/custom-analyzers-in-azure-search#index-attribute-reference" + }, + "description": "The name of the token filter. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters." + } + }, + "required": [ + "@odata.type", + "name" + ], + "description": "Base type for token filters.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "AsciiFoldingTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.AsciiFoldingTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "preserveOriginal": { + "type": "boolean", + "default": false, + "description": "A value indicating whether the original token will be kept. Default is false." + } + }, + "description": "Converts alphabetic, numeric, and symbolic Unicode characters which are not in the first 127 ASCII characters (the \"Basic Latin\" Unicode block) into their ASCII equivalents, if such equivalents exist. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/ASCIIFoldingFilter.html" + } + }, + "CjkBigramTokenFilterScripts": { + "type": "string", + "enum": [ + "han", + "hiragana", + "katakana", + "hangul" + ], + "x-ms-enum": { + "name": "CjkBigramTokenFilterScripts", + "modelAsString": false, + "values": [ + { + "value": "han", + "name": "Han", + "description": "Ignore Han script when forming bigrams of CJK terms." + }, + { + "value": "hiragana", + "name": "Hiragana", + "description": "Ignore Hiragana script when forming bigrams of CJK terms." + }, + { + "value": "katakana", + "name": "Katakana", + "description": "Ignore Katakana script when forming bigrams of CJK terms." + }, + { + "value": "hangul", + "name": "Hangul", + "description": "Ignore Hangul script when forming bigrams of CJK terms." + } + ] + }, + "description": "Scripts that can be ignored by CjkBigramTokenFilter." + }, + "CjkBigramTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.CjkBigramTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "ignoreScripts": { + "type": "array", + "items": { + "$ref": "#/definitions/CjkBigramTokenFilterScripts", + "x-nullable": false + }, + "description": "The scripts to ignore." + }, + "outputUnigrams": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to output both unigrams and bigrams (if true), or just bigrams (if false). Default is false." + } + }, + "description": "Forms bigrams of CJK terms that are generated from the standard tokenizer. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/cjk/CJKBigramFilter.html" + } + }, + "CommonGramTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.CommonGramTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "commonWords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The set of common words." + }, + "ignoreCase": { + "type": "boolean", + "default": false, + "description": "A value indicating whether common words matching will be case insensitive. Default is false." + }, + "queryMode": { + "x-ms-client-name": "UseQueryMode", + "type": "boolean", + "default": false, + "description": "A value that indicates whether the token filter is in query mode. When in query mode, the token filter generates bigrams and then removes common words and single terms followed by a common word. Default is false." + } + }, + "required": [ + "commonWords" + ], + "description": "Construct bigrams for frequently occurring terms while indexing. Single terms are still indexed too, with bigrams overlaid. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/commongrams/CommonGramsFilter.html" + } + }, + "DictionaryDecompounderTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.DictionaryDecompounderTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "wordList": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of words to match against." + }, + "minWordSize": { + "type": "integer", + "format": "int32", + "default": 5, + "maximum": 300, + "description": "The minimum word size. Only words longer than this get processed. Default is 5. Maximum is 300." + }, + "minSubwordSize": { + "type": "integer", + "format": "int32", + "default": 2, + "maximum": 300, + "description": "The minimum subword size. Only subwords longer than this are outputted. Default is 2. Maximum is 300." + }, + "maxSubwordSize": { + "type": "integer", + "format": "int32", + "default": 15, + "maximum": 300, + "description": "The maximum subword size. Only subwords shorter than this are outputted. Default is 15. Maximum is 300." + }, + "onlyLongestMatch": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to add only the longest matching subword to the output. Default is false." + } + }, + "required": [ + "wordList" + ], + "description": "Decomposes compound words found in many Germanic languages. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/compound/DictionaryCompoundWordTokenFilter.html" + } + }, + "EdgeNGramTokenFilterSide": { + "type": "string", + "enum": [ + "front", + "back" + ], + "x-ms-enum": { + "name": "EdgeNGramTokenFilterSide", + "modelAsString": false, + "values": [ + { + "value": "front", + "name": "Front", + "description": "Specifies that the n-gram should be generated from the front of the input." + }, + { + "value": "back", + "name": "Back", + "description": "Specifies that the n-gram should be generated from the back of the input." + } + ] + }, + "description": "Specifies which side of the input an n-gram should be generated from." + }, + "EdgeNGramTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.EdgeNGramTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "description": "The minimum n-gram length. Default is 1. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "description": "The maximum n-gram length. Default is 2." + }, + "side": { + "$ref": "#/definitions/EdgeNGramTokenFilterSide", + "default": "front", + "description": "Specifies which side of the input the n-gram should be generated from. Default is \"front\"." + } + }, + "description": "Generates n-grams of the given size(s) starting from the front or the back of an input token. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenFilter.html" + }, + "x-az-search-deprecated": true + }, + "EdgeNGramTokenFilterV2": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.EdgeNGramTokenFilterV2", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "maximum": 300, + "description": "The minimum n-gram length. Default is 1. Maximum is 300. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "maximum": 300, + "description": "The maximum n-gram length. Default is 2. Maximum is 300." + }, + "side": { + "$ref": "#/definitions/EdgeNGramTokenFilterSide", + "default": "front", + "description": "Specifies which side of the input the n-gram should be generated from. Default is \"front\"." + } + }, + "description": "Generates n-grams of the given size(s) starting from the front or the back of an input token. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/EdgeNGramTokenFilter.html" + } + }, + "ElisionTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.ElisionTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "articles": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The set of articles to remove." + } + }, + "description": "Removes elisions. For example, \"l'avion\" (the plane) will be converted to \"avion\" (plane). This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/util/ElisionFilter.html" + } + }, + "KeepTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.KeepTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "keepWords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of words to keep." + }, + "keepWordsCase": { + "x-ms-client-name": "LowerCaseKeepWords", + "type": "boolean", + "default": false, + "description": "A value indicating whether to lower case all words first. Default is false." + } + }, + "required": [ + "keepWords" + ], + "description": "A token filter that only keeps tokens with text contained in a specified list of words. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/KeepWordFilter.html" + } + }, + "KeywordMarkerTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.KeywordMarkerTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "keywords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of words to mark as keywords." + }, + "ignoreCase": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to ignore case. If true, all words are converted to lower case first. Default is false." + } + }, + "required": [ + "keywords" + ], + "description": "Marks terms as keywords. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/KeywordMarkerFilter.html" + } + }, + "LengthTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.LengthTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "min": { + "x-ms-client-name": "minLength", + "type": "integer", + "format": "int32", + "default": 0, + "maximum": 300, + "description": "The minimum length in characters. Default is 0. Maximum is 300. Must be less than the value of max." + }, + "max": { + "x-ms-client-name": "maxLength", + "type": "integer", + "format": "int32", + "default": 300, + "maximum": 300, + "description": "The maximum length in characters. Default and maximum is 300." + } + }, + "description": "Removes words that are too long or too short. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LengthFilter.html" + } + }, + "LimitTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.LimitTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "maxTokenCount": { + "type": "integer", + "format": "int32", + "default": 1, + "description": "The maximum number of tokens to produce. Default is 1." + }, + "consumeAllTokens": { + "type": "boolean", + "default": false, + "description": "A value indicating whether all tokens from the input must be consumed even if maxTokenCount is reached. Default is false." + } + }, + "description": "Limits the number of tokens while indexing. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/LimitTokenCountFilter.html" + } + }, + "NGramTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.NGramTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "description": "The minimum n-gram length. Default is 1. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "description": "The maximum n-gram length. Default is 2." + } + }, + "description": "Generates n-grams of the given size(s). This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenFilter.html" + }, + "x-az-search-deprecated": true + }, + "NGramTokenFilterV2": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.NGramTokenFilterV2", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "minGram": { + "type": "integer", + "format": "int32", + "default": 1, + "maximum": 300, + "description": "The minimum n-gram length. Default is 1. Maximum is 300. Must be less than the value of maxGram." + }, + "maxGram": { + "type": "integer", + "format": "int32", + "default": 2, + "maximum": 300, + "description": "The maximum n-gram length. Default is 2. Maximum is 300." + } + }, + "description": "Generates n-grams of the given size(s). This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/ngram/NGramTokenFilter.html" + } + }, + "PatternCaptureTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PatternCaptureTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "patterns": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of patterns to match against each token." + }, + "preserveOriginal": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to return the original token even if one of the patterns matches. Default is true." + } + }, + "required": [ + "patterns" + ], + "description": "Uses Java regexes to emit multiple tokens - one for each capture group in one or more patterns. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternCaptureGroupTokenFilter.html" + } + }, + "PatternReplaceTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PatternReplaceTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "pattern": { + "type": "string", + "description": "A regular expression pattern." + }, + "replacement": { + "type": "string", + "description": "The replacement text." + } + }, + "required": [ + "pattern", + "replacement" + ], + "description": "A character filter that replaces characters in the input string. It uses a regular expression to identify character sequences to preserve and a replacement pattern to identify characters to replace. For example, given the input text \"aa bb aa bb\", pattern \"(aa)\\s+(bb)\", and replacement \"$1#$2\", the result would be \"aa#bb aa#bb\". This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternReplaceFilter.html" + } + }, + "PhoneticEncoder": { + "type": "string", + "enum": [ + "metaphone", + "doubleMetaphone", + "soundex", + "refinedSoundex", + "caverphone1", + "caverphone2", + "cologne", + "nysiis", + "koelnerPhonetik", + "haasePhonetik", + "beiderMorse" + ], + "x-ms-enum": { + "name": "PhoneticEncoder", + "modelAsString": false, + "values": [ + { + "value": "metaphone", + "name": "Metaphone", + "description": "Encodes a token into a Metaphone value." + }, + { + "value": "doubleMetaphone", + "name": "DoubleMetaphone", + "description": "Encodes a token into a double metaphone value." + }, + { + "value": "soundex", + "name": "Soundex", + "description": "Encodes a token into a Soundex value." + }, + { + "value": "refinedSoundex", + "name": "RefinedSoundex", + "description": "Encodes a token into a Refined Soundex value." + }, + { + "value": "caverphone1", + "name": "Caverphone1", + "description": "Encodes a token into a Caverphone 1.0 value." + }, + { + "value": "caverphone2", + "name": "Caverphone2", + "description": "Encodes a token into a Caverphone 2.0 value." + }, + { + "value": "cologne", + "name": "Cologne", + "description": "Encodes a token into a Cologne Phonetic value." + }, + { + "value": "nysiis", + "name": "Nysiis", + "description": "Encodes a token into a NYSIIS value." + }, + { + "value": "koelnerPhonetik", + "name": "KoelnerPhonetik", + "description": "Encodes a token using the Kölner Phonetik algorithm." + }, + { + "value": "haasePhonetik", + "name": "HaasePhonetik", + "description": "Encodes a token using the Haase refinement of the Kölner Phonetik algorithm." + }, + { + "value": "beiderMorse", + "name": "BeiderMorse", + "description": "Encodes a token into a Beider-Morse value." + } + ] + }, + "description": "Identifies the type of phonetic encoder to use with a PhoneticTokenFilter." + }, + "PhoneticTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PhoneticTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "encoder": { + "$ref": "#/definitions/PhoneticEncoder", + "default": "metaphone", + "description": "The phonetic encoder to use. Default is \"metaphone\"." + }, + "replace": { + "x-ms-client-name": "ReplaceOriginalTokens", + "type": "boolean", + "default": true, + "description": "A value indicating whether encoded tokens should replace original tokens. If false, encoded tokens are added as synonyms. Default is true." + } + }, + "description": "Create tokens for phonetic matches. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "https://lucene.apache.org/core/4_10_3/analyzers-phonetic/org/apache/lucene/analysis/phonetic/package-tree.html" + } + }, + "ShingleTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.ShingleTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "maxShingleSize": { + "type": "integer", + "format": "int32", + "default": 2, + "minimum": 2, + "description": "The maximum shingle size. Default and minimum value is 2." + }, + "minShingleSize": { + "type": "integer", + "format": "int32", + "default": 2, + "minimum": 2, + "description": "The minimum shingle size. Default and minimum value is 2. Must be less than the value of maxShingleSize." + }, + "outputUnigrams": { + "type": "boolean", + "default": true, + "description": "A value indicating whether the output stream will contain the input tokens (unigrams) as well as shingles. Default is true." + }, + "outputUnigramsIfNoShingles": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to output unigrams for those times when no shingles are available. This property takes precedence when outputUnigrams is set to false. Default is false." + }, + "tokenSeparator": { + "type": "string", + "default": " ", + "description": "The string to use when joining adjacent tokens to form a shingle. Default is a single space (\" \")." + }, + "filterToken": { + "type": "string", + "default": "_", + "description": "The string to insert for each position at which there is no token. Default is an underscore (\"_\")." + } + }, + "description": "Creates combinations of tokens as a single token. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/shingle/ShingleFilter.html" + } + }, + "SnowballTokenFilterLanguage": { + "type": "string", + "enum": [ + "armenian", + "basque", + "catalan", + "danish", + "dutch", + "english", + "finnish", + "french", + "german", + "german2", + "hungarian", + "italian", + "kp", + "lovins", + "norwegian", + "porter", + "portuguese", + "romanian", + "russian", + "spanish", + "swedish", + "turkish" + ], + "x-ms-enum": { + "name": "SnowballTokenFilterLanguage", + "modelAsString": false, + "values": [ + { + "value": "armenian", + "name": "Armenian", + "description": "Selects the Lucene Snowball stemming tokenizer for Armenian." + }, + { + "value": "basque", + "name": "Basque", + "description": "Selects the Lucene Snowball stemming tokenizer for Basque." + }, + { + "value": "catalan", + "name": "Catalan", + "description": "Selects the Lucene Snowball stemming tokenizer for Catalan." + }, + { + "value": "danish", + "name": "Danish", + "description": "Selects the Lucene Snowball stemming tokenizer for Danish." + }, + { + "value": "dutch", + "name": "Dutch", + "description": "Selects the Lucene Snowball stemming tokenizer for Dutch." + }, + { + "value": "english", + "name": "English", + "description": "Selects the Lucene Snowball stemming tokenizer for English." + }, + { + "value": "finnish", + "name": "Finnish", + "description": "Selects the Lucene Snowball stemming tokenizer for Finnish." + }, + { + "value": "french", + "name": "French", + "description": "Selects the Lucene Snowball stemming tokenizer for French." + }, + { + "value": "german", + "name": "German", + "description": "Selects the Lucene Snowball stemming tokenizer for German." + }, + { + "value": "german2", + "name": "German2", + "description": "Selects the Lucene Snowball stemming tokenizer that uses the German variant algorithm." + }, + { + "value": "hungarian", + "name": "Hungarian", + "description": "Selects the Lucene Snowball stemming tokenizer for Hungarian." + }, + { + "value": "italian", + "name": "Italian", + "description": "Selects the Lucene Snowball stemming tokenizer for Italian." + }, + { + "value": "kp", + "name": "Kp", + "description": "Selects the Lucene Snowball stemming tokenizer for Dutch that uses the Kraaij-Pohlmann stemming algorithm." + }, + { + "value": "lovins", + "name": "Lovins", + "description": "Selects the Lucene Snowball stemming tokenizer for English that uses the Lovins stemming algorithm." + }, + { + "value": "norwegian", + "name": "Norwegian", + "description": "Selects the Lucene Snowball stemming tokenizer for Norwegian." + }, + { + "value": "porter", + "name": "Porter", + "description": "Selects the Lucene Snowball stemming tokenizer for English that uses the Porter stemming algorithm." + }, + { + "value": "portuguese", + "name": "Portuguese", + "description": "Selects the Lucene Snowball stemming tokenizer for Portuguese." + }, + { + "value": "romanian", + "name": "Romanian", + "description": "Selects the Lucene Snowball stemming tokenizer for Romanian." + }, + { + "value": "russian", + "name": "Russian", + "description": "Selects the Lucene Snowball stemming tokenizer for Russian." + }, + { + "value": "spanish", + "name": "Spanish", + "description": "Selects the Lucene Snowball stemming tokenizer for Spanish." + }, + { + "value": "swedish", + "name": "Swedish", + "description": "Selects the Lucene Snowball stemming tokenizer for Swedish." + }, + { + "value": "turkish", + "name": "Turkish", + "description": "Selects the Lucene Snowball stemming tokenizer for Turkish." + } + ] + }, + "description": "The language to use for a Snowball token filter." + }, + "SnowballTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.SnowballTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "language": { + "$ref": "#/definitions/SnowballTokenFilterLanguage", + "description": "The language to use." + } + }, + "required": [ + "language" + ], + "description": "A filter that stems words using a Snowball-generated stemmer. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/snowball/SnowballFilter.html" + } + }, + "StemmerTokenFilterLanguage": { + "type": "string", + "enum": [ + "arabic", + "armenian", + "basque", + "brazilian", + "bulgarian", + "catalan", + "czech", + "danish", + "dutch", + "dutchKp", + "english", + "lightEnglish", + "minimalEnglish", + "possessiveEnglish", + "porter2", + "lovins", + "finnish", + "lightFinnish", + "french", + "lightFrench", + "minimalFrench", + "galician", + "minimalGalician", + "german", + "german2", + "lightGerman", + "minimalGerman", + "greek", + "hindi", + "hungarian", + "lightHungarian", + "indonesian", + "irish", + "italian", + "lightItalian", + "sorani", + "latvian", + "norwegian", + "lightNorwegian", + "minimalNorwegian", + "lightNynorsk", + "minimalNynorsk", + "portuguese", + "lightPortuguese", + "minimalPortuguese", + "portugueseRslp", + "romanian", + "russian", + "lightRussian", + "spanish", + "lightSpanish", + "swedish", + "lightSwedish", + "turkish" + ], + "x-ms-enum": { + "name": "StemmerTokenFilterLanguage", + "modelAsString": false, + "values": [ + { + "value": "arabic", + "name": "Arabic", + "description": "Selects the Lucene stemming tokenizer for Arabic." + }, + { + "value": "armenian", + "name": "Armenian", + "description": "Selects the Lucene stemming tokenizer for Armenian." + }, + { + "value": "basque", + "name": "Basque", + "description": "Selects the Lucene stemming tokenizer for Basque." + }, + { + "value": "brazilian", + "name": "Brazilian", + "description": "Selects the Lucene stemming tokenizer for Portuguese (Brazil)." + }, + { + "value": "bulgarian", + "name": "Bulgarian", + "description": "Selects the Lucene stemming tokenizer for Bulgarian." + }, + { + "value": "catalan", + "name": "Catalan", + "description": "Selects the Lucene stemming tokenizer for Catalan." + }, + { + "value": "czech", + "name": "Czech", + "description": "Selects the Lucene stemming tokenizer for Czech." + }, + { + "value": "danish", + "name": "Danish", + "description": "Selects the Lucene stemming tokenizer for Danish." + }, + { + "value": "dutch", + "name": "Dutch", + "description": "Selects the Lucene stemming tokenizer for Dutch." + }, + { + "value": "dutchKp", + "name": "DutchKp", + "description": "Selects the Lucene stemming tokenizer for Dutch that uses the Kraaij-Pohlmann stemming algorithm." + }, + { + "value": "english", + "name": "English", + "description": "Selects the Lucene stemming tokenizer for English." + }, + { + "value": "lightEnglish", + "name": "LightEnglish", + "description": "Selects the Lucene stemming tokenizer for English that does light stemming." + }, + { + "value": "minimalEnglish", + "name": "MinimalEnglish", + "description": "Selects the Lucene stemming tokenizer for English that does minimal stemming." + }, + { + "value": "possessiveEnglish", + "name": "PossessiveEnglish", + "description": "Selects the Lucene stemming tokenizer for English that removes trailing possessives from words." + }, + { + "value": "porter2", + "name": "Porter2", + "description": "Selects the Lucene stemming tokenizer for English that uses the Porter2 stemming algorithm." + }, + { + "value": "lovins", + "name": "Lovins", + "description": "Selects the Lucene stemming tokenizer for English that uses the Lovins stemming algorithm." + }, + { + "value": "finnish", + "name": "Finnish", + "description": "Selects the Lucene stemming tokenizer for Finnish." + }, + { + "value": "lightFinnish", + "name": "LightFinnish", + "description": "Selects the Lucene stemming tokenizer for Finnish that does light stemming." + }, + { + "value": "french", + "name": "French", + "description": "Selects the Lucene stemming tokenizer for French." + }, + { + "value": "lightFrench", + "name": "LightFrench", + "description": "Selects the Lucene stemming tokenizer for French that does light stemming." + }, + { + "value": "minimalFrench", + "name": "MinimalFrench", + "description": "Selects the Lucene stemming tokenizer for French that does minimal stemming." + }, + { + "value": "galician", + "name": "Galician", + "description": "Selects the Lucene stemming tokenizer for Galician." + }, + { + "value": "minimalGalician", + "name": "MinimalGalician", + "description": "Selects the Lucene stemming tokenizer for Galician that does minimal stemming." + }, + { + "value": "german", + "name": "German", + "description": "Selects the Lucene stemming tokenizer for German." + }, + { + "value": "german2", + "name": "German2", + "description": "Selects the Lucene stemming tokenizer that uses the German variant algorithm." + }, + { + "value": "lightGerman", + "name": "LightGerman", + "description": "Selects the Lucene stemming tokenizer for German that does light stemming." + }, + { + "value": "minimalGerman", + "name": "MinimalGerman", + "description": "Selects the Lucene stemming tokenizer for German that does minimal stemming." + }, + { + "value": "greek", + "name": "Greek", + "description": "Selects the Lucene stemming tokenizer for Greek." + }, + { + "value": "hindi", + "name": "Hindi", + "description": "Selects the Lucene stemming tokenizer for Hindi." + }, + { + "value": "hungarian", + "name": "Hungarian", + "description": "Selects the Lucene stemming tokenizer for Hungarian." + }, + { + "value": "lightHungarian", + "name": "LightHungarian", + "description": "Selects the Lucene stemming tokenizer for Hungarian that does light stemming." + }, + { + "value": "indonesian", + "name": "Indonesian", + "description": "Selects the Lucene stemming tokenizer for Indonesian." + }, + { + "value": "irish", + "name": "Irish", + "description": "Selects the Lucene stemming tokenizer for Irish." + }, + { + "value": "italian", + "name": "Italian", + "description": "Selects the Lucene stemming tokenizer for Italian." + }, + { + "value": "lightItalian", + "name": "LightItalian", + "description": "Selects the Lucene stemming tokenizer for Italian that does light stemming." + }, + { + "value": "sorani", + "name": "Sorani", + "description": "Selects the Lucene stemming tokenizer for Sorani." + }, + { + "value": "latvian", + "name": "Latvian", + "description": "Selects the Lucene stemming tokenizer for Latvian." + }, + { + "value": "norwegian", + "name": "Norwegian", + "description": "Selects the Lucene stemming tokenizer for Norwegian (Bokmål)." + }, + { + "value": "lightNorwegian", + "name": "LightNorwegian", + "description": "Selects the Lucene stemming tokenizer for Norwegian (Bokmål) that does light stemming." + }, + { + "value": "minimalNorwegian", + "name": "MinimalNorwegian", + "description": "Selects the Lucene stemming tokenizer for Norwegian (Bokmål) that does minimal stemming." + }, + { + "value": "lightNynorsk", + "name": "LightNynorsk", + "description": "Selects the Lucene stemming tokenizer for Norwegian (Nynorsk) that does light stemming." + }, + { + "value": "minimalNynorsk", + "name": "MinimalNynorsk", + "description": "Selects the Lucene stemming tokenizer for Norwegian (Nynorsk) that does minimal stemming." + }, + { + "value": "portuguese", + "name": "Portuguese", + "description": "Selects the Lucene stemming tokenizer for Portuguese." + }, + { + "value": "lightPortuguese", + "name": "LightPortuguese", + "description": "Selects the Lucene stemming tokenizer for Portuguese that does light stemming." + }, + { + "value": "minimalPortuguese", + "name": "MinimalPortuguese", + "description": "Selects the Lucene stemming tokenizer for Portuguese that does minimal stemming." + }, + { + "value": "portugueseRslp", + "name": "PortugueseRslp", + "description": "Selects the Lucene stemming tokenizer for Portuguese that uses the RSLP stemming algorithm." + }, + { + "value": "romanian", + "name": "Romanian", + "description": "Selects the Lucene stemming tokenizer for Romanian." + }, + { + "value": "russian", + "name": "Russian", + "description": "Selects the Lucene stemming tokenizer for Russian." + }, + { + "value": "lightRussian", + "name": "LightRussian", + "description": "Selects the Lucene stemming tokenizer for Russian that does light stemming." + }, + { + "value": "spanish", + "name": "Spanish", + "description": "Selects the Lucene stemming tokenizer for Spanish." + }, + { + "value": "lightSpanish", + "name": "LightSpanish", + "description": "Selects the Lucene stemming tokenizer for Spanish that does light stemming." + }, + { + "value": "swedish", + "name": "Swedish", + "description": "Selects the Lucene stemming tokenizer for Swedish." + }, + { + "value": "lightSwedish", + "name": "LightSwedish", + "description": "Selects the Lucene stemming tokenizer for Swedish that does light stemming." + }, + { + "value": "turkish", + "name": "Turkish", + "description": "Selects the Lucene stemming tokenizer for Turkish." + } + ] + }, + "description": "The language to use for a stemmer token filter." + }, + "StemmerTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StemmerTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "language": { + "$ref": "#/definitions/StemmerTokenFilterLanguage", + "description": "The language to use." + } + }, + "required": [ + "language" + ], + "description": "Language specific stemming filter. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search#TokenFilters" + } + }, + "StemmerOverrideTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StemmerOverrideTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "rules": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of stemming rules in the following format: \"word => stem\", for example: \"ran => run\"." + } + }, + "required": [ + "rules" + ], + "description": "Provides the ability to override other stemming filters with custom dictionary-based stemming. Any dictionary-stemmed terms will be marked as keywords so that they will not be stemmed with stemmers down the chain. Must be placed before any stemming filters. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/StemmerOverrideFilter.html" + } + }, + "StopwordsList": { + "type": "string", + "enum": [ + "arabic", + "armenian", + "basque", + "brazilian", + "bulgarian", + "catalan", + "czech", + "danish", + "dutch", + "english", + "finnish", + "french", + "galician", + "german", + "greek", + "hindi", + "hungarian", + "indonesian", + "irish", + "italian", + "latvian", + "norwegian", + "persian", + "portuguese", + "romanian", + "russian", + "sorani", + "spanish", + "swedish", + "thai", + "turkish" + ], + "x-ms-enum": { + "name": "StopwordsList", + "modelAsString": false, + "values": [ + { + "value": "arabic", + "name": "Arabic", + "description": "Selects the stopword list for Arabic." + }, + { + "value": "armenian", + "name": "Armenian", + "description": "Selects the stopword list for Armenian." + }, + { + "value": "basque", + "name": "Basque", + "description": "Selects the stopword list for Basque." + }, + { + "value": "brazilian", + "name": "Brazilian", + "description": "Selects the stopword list for Portuguese (Brazil)." + }, + { + "value": "bulgarian", + "name": "Bulgarian", + "description": "Selects the stopword list for Bulgarian." + }, + { + "value": "catalan", + "name": "Catalan", + "description": "Selects the stopword list for Catalan." + }, + { + "value": "czech", + "name": "Czech", + "description": "Selects the stopword list for Czech." + }, + { + "value": "danish", + "name": "Danish", + "description": "Selects the stopword list for Danish." + }, + { + "value": "dutch", + "name": "Dutch", + "description": "Selects the stopword list for Dutch." + }, + { + "value": "english", + "name": "English", + "description": "Selects the stopword list for English." + }, + { + "value": "finnish", + "name": "Finnish", + "description": "Selects the stopword list for Finnish." + }, + { + "value": "french", + "name": "French", + "description": "Selects the stopword list for French." + }, + { + "value": "galician", + "name": "Galician", + "description": "Selects the stopword list for Galician." + }, + { + "value": "german", + "name": "German", + "description": "Selects the stopword list for German." + }, + { + "value": "greek", + "name": "Greek", + "description": "Selects the stopword list for Greek." + }, + { + "value": "hindi", + "name": "Hindi", + "description": "Selects the stopword list for Hindi." + }, + { + "value": "hungarian", + "name": "Hungarian", + "description": "Selects the stopword list for Hungarian." + }, + { + "value": "indonesian", + "name": "Indonesian", + "description": "Selects the stopword list for Indonesian." + }, + { + "value": "irish", + "name": "Irish", + "description": "Selects the stopword list for Irish." + }, + { + "value": "italian", + "name": "Italian", + "description": "Selects the stopword list for Italian." + }, + { + "value": "latvian", + "name": "Latvian", + "description": "Selects the stopword list for Latvian." + }, + { + "value": "norwegian", + "name": "Norwegian", + "description": "Selects the stopword list for Norwegian." + }, + { + "value": "persian", + "name": "Persian", + "description": "Selects the stopword list for Persian." + }, + { + "value": "portuguese", + "name": "Portuguese", + "description": "Selects the stopword list for Portuguese." + }, + { + "value": "romanian", + "name": "Romanian", + "description": "Selects the stopword list for Romanian." + }, + { + "value": "russian", + "name": "Russian", + "description": "Selects the stopword list for Russian." + }, + { + "value": "sorani", + "name": "Sorani", + "description": "Selects the stopword list for Sorani." + }, + { + "value": "spanish", + "name": "Spanish", + "description": "Selects the stopword list for Spanish." + }, + { + "value": "swedish", + "name": "Swedish", + "description": "Selects the stopword list for Swedish." + }, + { + "value": "thai", + "name": "Thai", + "description": "Selects the stopword list for Thai." + }, + { + "value": "turkish", + "name": "Turkish", + "description": "Selects the stopword list for Turkish." + } + ] + }, + "description": "Identifies a predefined list of language-specific stopwords." + }, + "StopwordsTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.StopwordsTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "stopwords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of stopwords. This property and the stopwords list property cannot both be set." + }, + "stopwordsList": { + "$ref": "#/definitions/StopwordsList", + "default": "english", + "description": "A predefined list of stopwords to use. This property and the stopwords property cannot both be set. Default is English." + }, + "ignoreCase": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to ignore case. If true, all words are converted to lower case first. Default is false." + }, + "removeTrailing": { + "x-ms-client-name": "RemoveTrailingStopWords", + "type": "boolean", + "default": true, + "description": "A value indicating whether to ignore the last search term if it's a stop word. Default is true." + } + }, + "description": "Removes stop words from a token stream. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/core/StopFilter.html" + } + }, + "SynonymTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.SynonymTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "synonyms": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of synonyms in following one of two formats: 1. incredible, unbelievable, fabulous => amazing - all terms on the left side of => symbol will be replaced with all terms on its right side; 2. incredible, unbelievable, fabulous, amazing - comma separated list of equivalent words. Set the expand option to change how this list is interpreted." + }, + "ignoreCase": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to case-fold input for matching. Default is false." + }, + "expand": { + "type": "boolean", + "default": true, + "description": "A value indicating whether all words in the list of synonyms (if => notation is not used) will map to one another. If true, all words in the list of synonyms (if => notation is not used) will map to one another. The following list: incredible, unbelievable, fabulous, amazing is equivalent to: incredible, unbelievable, fabulous, amazing => incredible, unbelievable, fabulous, amazing. If false, the following list: incredible, unbelievable, fabulous, amazing will be equivalent to: incredible, unbelievable, fabulous, amazing => incredible. Default is true." + } + }, + "required": [ + "synonyms" + ], + "description": "Matches single or multi-word synonyms in a token stream. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/synonym/SynonymFilter.html" + } + }, + "TruncateTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.TruncateTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "length": { + "type": "integer", + "format": "int32", + "default": 300, + "maximum": 300, + "description": "The length at which terms will be truncated. Default and maximum is 300." + } + }, + "description": "Truncates the terms to a specific length. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/TruncateTokenFilter.html" + } + }, + "UniqueTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.UniqueTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "onlyOnSamePosition": { + "type": "boolean", + "default": false, + "description": "A value indicating whether to remove duplicates only at the same position. Default is false." + } + }, + "description": "Filters out tokens with same text as the previous token. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/RemoveDuplicatesTokenFilter.html" + } + }, + "WordDelimiterTokenFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.WordDelimiterTokenFilter", + "allOf": [ + { + "$ref": "#/definitions/TokenFilter" + } + ], + "properties": { + "generateWordParts": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to generate part words. If set, causes parts of words to be generated; for example \"AzureSearch\" becomes \"Azure\" \"Search\". Default is true." + }, + "generateNumberParts": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to generate number subwords. Default is true." + }, + "catenateWords": { + "type": "boolean", + "default": false, + "description": "A value indicating whether maximum runs of word parts will be catenated. For example, if this is set to true, \"Azure-Search\" becomes \"AzureSearch\". Default is false." + }, + "catenateNumbers": { + "type": "boolean", + "default": false, + "description": "A value indicating whether maximum runs of number parts will be catenated. For example, if this is set to true, \"1-2\" becomes \"12\". Default is false." + }, + "catenateAll": { + "type": "boolean", + "default": false, + "description": "A value indicating whether all subword parts will be catenated. For example, if this is set to true, \"Azure-Search-1\" becomes \"AzureSearch1\". Default is false." + }, + "splitOnCaseChange": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to split words on caseChange. For example, if this is set to true, \"AzureSearch\" becomes \"Azure\" \"Search\". Default is true." + }, + "preserveOriginal": { + "type": "boolean", + "default": false, + "description": "A value indicating whether original words will be preserved and added to the subword list. Default is false." + }, + "splitOnNumerics": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to split on numbers. For example, if this is set to true, \"Azure1Search\" becomes \"Azure\" \"1\" \"Search\". Default is true." + }, + "stemEnglishPossessive": { + "type": "boolean", + "default": true, + "description": "A value indicating whether to remove trailing \"'s\" for each subword. Default is true." + }, + "protectedWords": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of tokens to protect from being delimited." + } + }, + "description": "Splits words into subwords and performs optional transformations on subword groups. This token filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "http://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/miscellaneous/WordDelimiterFilter.html" + } + }, + "CharFilter": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of char filter." + }, + "name": { + "type": "string", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/custom-analyzers-in-azure-search#index-attribute-reference" + }, + "description": "The name of the char filter. It must only contain letters, digits, spaces, dashes or underscores, can only start and end with alphanumeric characters, and is limited to 128 characters." + } + }, + "required": [ + "@odata.type", + "name" + ], + "description": "Base type for character filters.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "MappingCharFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.MappingCharFilter", + "allOf": [ + { + "$ref": "#/definitions/CharFilter" + } + ], + "properties": { + "mappings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of mappings of the following format: \"a=>b\" (all occurrences of the character \"a\" will be replaced with character \"b\")." + } + }, + "required": [ + "mappings" + ], + "description": "A character filter that applies mappings defined with the mappings option. Matching is greedy (longest pattern matching at a given point wins). Replacement is allowed to be the empty string. This character filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/charfilter/MappingCharFilter.html" + } + }, + "PatternReplaceCharFilter": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.PatternReplaceCharFilter", + "allOf": [ + { + "$ref": "#/definitions/CharFilter" + } + ], + "properties": { + "pattern": { + "type": "string", + "description": "A regular expression pattern." + }, + "replacement": { + "type": "string", + "description": "The replacement text." + } + }, + "required": [ + "pattern", + "replacement" + ], + "description": "A character filter that replaces characters in the input string. It uses a regular expression to identify character sequences to preserve and a replacement pattern to identify characters to replace. For example, given the input text \"aa bb aa bb\", pattern \"(aa)\\s+(bb)\", and replacement \"$1#$2\", the result would be \"aa#bb aa#bb\". This character filter is implemented using Apache Lucene.", + "externalDocs": { + "url": "https://lucene.apache.org/core/4_10_3/analyzers-common/org/apache/lucene/analysis/pattern/PatternReplaceCharFilter.html" + } + }, + "Similarity": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string" + } + }, + "required": [ + "@odata.type" + ], + "description": "Base type for similarity algorithms. Similarity algorithms are used to calculate scores that tie queries to documents. The higher the score, the more relevant the document is to that specific query. Those scores are used to rank the search results.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/index-ranking-similarity" + } + }, + "ClassicSimilarity": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.ClassicSimilarity", + "allOf": [ + { + "$ref": "#/definitions/Similarity" + } + ], + "description": "Legacy similarity algorithm which uses the Lucene TFIDFSimilarity implementation of TF-IDF. This variation of TF-IDF introduces static document length normalization as well as coordinating factors that penalize documents that only partially match the searched queries." + }, + "BM25Similarity": { + "x-ms-discriminator-value": "#Microsoft.Azure.Search.BM25Similarity", + "allOf": [ + { + "$ref": "#/definitions/Similarity" + } + ], + "properties": { + "k1": { + "type": "number", + "format": "double", + "description": "This property controls the scaling function between the term frequency of each matching terms and the final relevance score of a document-query pair. By default, a value of 1.2 is used. A value of 0.0 means the score does not scale with an increase in term frequency.", + "x-nullable": true + }, + "b": { + "type": "number", + "format": "double", + "description": "This property controls how the length of a document affects the relevance score. By default, a value of 0.75 is used. A value of 0.0 means no length normalization is applied, while a value of 1.0 means the score is fully normalized by the length of the document.", + "x-nullable": true + } + }, + "description": "Ranking function based on the Okapi BM25 similarity algorithm. BM25 is a TF-IDF-like algorithm that includes length normalization (controlled by the 'b' parameter) as well as term frequency saturation (controlled by the 'k1' parameter)." + }, + "VectorSearch": { + "type": "object", + "properties": { + "profiles": { + "type": "array", + "items": { + "$ref": "#/definitions/VectorSearchProfile" + }, + "description": "Defines combinations of configurations to use with vector search." + }, + "algorithms": { + "type": "array", + "items": { + "$ref": "#/definitions/VectorSearchAlgorithmConfiguration" + }, + "description": "Contains configuration options specific to the algorithm used during indexing or querying." + }, + "vectorizers": { + "type": "array", + "items": { + "$ref": "#/definitions/VectorSearchVectorizer" + }, + "description": "Contains configuration options on how to vectorize text vector queries." + }, + "compressions": { + "type": "array", + "items": { + "$ref": "#/definitions/VectorSearchCompressionConfiguration" + }, + "description": "Contains configuration options specific to the compression method used during indexing or querying." + } + }, + "description": "Contains configuration options related to vector search." + }, + "VectorSearchProfile": { + "type": "object", + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name to associate with this particular vector search profile.", + "x-nullable": false + }, + "algorithm": { + "x-ms-client-name": "algorithmConfigurationName", + "type": "string", + "description": "The name of the vector search algorithm configuration that specifies the algorithm and optional parameters.", + "x-nullable": false + }, + "vectorizer": { + "x-ms-client-name": "vectorizerName", + "type": "string", + "description": "The name of the vectorization being configured for use with vector search.", + "x-nullable": false + }, + "compression": { + "x-ms-client-name": "compressionConfigurationName", + "type": "string", + "description": "The name of the compression method configuration that specifies the compression method and optional parameters.", + "x-nullable": false + } + }, + "required": [ + "name", + "algorithm" + ], + "description": "Defines a combination of configurations to use with vector search." + }, + "VectorSearchAlgorithmConfiguration": { + "type": "object", + "discriminator": "kind", + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name to associate with this particular configuration.", + "x-nullable": false + }, + "kind": { + "$ref": "#/definitions/VectorSearchAlgorithmKind", + "description": "The name of the kind of algorithm being configured for use with vector search.", + "x-nullable": false + } + }, + "required": [ + "name", + "kind" + ], + "description": "Contains configuration options specific to the algorithm used during indexing or querying." + }, + "HnswVectorSearchAlgorithmConfiguration": { + "x-ms-client-name": "HnswAlgorithmConfiguration", + "type": "object", + "x-ms-discriminator-value": "hnsw", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchAlgorithmConfiguration" + } + ], + "properties": { + "hnswParameters": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/HnswParameters", + "description": "Contains the parameters specific to HNSW algorithm." + } + }, + "description": "Contains configuration options specific to the HNSW approximate nearest neighbors algorithm used during indexing and querying. The HNSW algorithm offers a tunable trade-off between search speed and accuracy." + }, + "HnswParameters": { + "type": "object", + "properties": { + "m": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "minimum": 4, + "maximum": 10, + "default": 4, + "description": "The number of bi-directional links created for every new element during construction. Increasing this parameter value may improve recall and reduce retrieval times for datasets with high intrinsic dimensionality at the expense of increased memory consumption and longer indexing time." + }, + "efConstruction": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "minimum": 100, + "maximum": 1000, + "default": 400, + "description": "The size of the dynamic list containing the nearest neighbors, which is used during index time. Increasing this parameter may improve index quality, at the expense of increased indexing time. At a certain point, increasing this parameter leads to diminishing returns." + }, + "efSearch": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "minimum": 100, + "maximum": 1000, + "default": 500, + "description": "The size of the dynamic list containing the nearest neighbors, which is used during search time. Increasing this parameter may improve search results, at the expense of slower search. At a certain point, increasing this parameter leads to diminishing returns." + }, + "metric": { + "$ref": "#/definitions/VectorSearchAlgorithmMetric", + "x-nullable": true, + "description": "The similarity metric to use for vector comparisons." + } + }, + "description": "Contains the parameters specific to the HNSW algorithm." + }, + "ExhaustiveKnnVectorSearchAlgorithmConfiguration": { + "x-ms-client-name": "ExhaustiveKnnAlgorithmConfiguration", + "type": "object", + "x-ms-discriminator-value": "exhaustiveKnn", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchAlgorithmConfiguration" + } + ], + "properties": { + "exhaustiveKnnParameters": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/ExhaustiveKnnParameters", + "description": "Contains the parameters specific to exhaustive KNN algorithm." + } + }, + "description": "Contains configuration options specific to the exhaustive KNN algorithm used during querying, which will perform brute-force search across the entire vector index." + }, + "ExhaustiveKnnParameters": { + "type": "object", + "properties": { + "metric": { + "$ref": "#/definitions/VectorSearchAlgorithmMetric", + "x-nullable": true, + "description": "The similarity metric to use for vector comparisons." + } + }, + "description": "Contains the parameters specific to exhaustive KNN algorithm." + }, + "VectorSearchAlgorithmMetric": { + "type": "string", + "enum": [ + "cosine", + "euclidean", + "dotProduct", + "hamming" + ], + "x-ms-enum": { + "name": "VectorSearchAlgorithmMetric", + "modelAsString": true, + "values": [ + { + "value": "cosine", + "name": "Cosine", + "description": "Measures the angle between vectors to quantify their similarity, disregarding magnitude. The smaller the angle, the closer the similarity." + }, + { + "value": "euclidean", + "name": "Euclidean", + "description": "Computes the straight-line distance between vectors in a multi-dimensional space. The smaller the distance, the closer the similarity." + }, + { + "value": "dotProduct", + "name": "DotProduct", + "description": "Calculates the sum of element-wise products to gauge alignment and magnitude similarity. The larger and more positive, the closer the similarity." + }, + { + "value": "hamming", + "name": "Hamming", + "description": "Only applicable to bit-packed binary data types. Determines dissimilarity by counting differing positions in binary vectors. The fewer differences, the closer the similarity." + } + ] + }, + "description": "The similarity metric to use for vector comparisons. It is recommended to choose the same similarity metric as the embedding model was trained on." + }, + "VectorSearchAlgorithmKind": { + "type": "string", + "enum": [ + "hnsw", + "exhaustiveKnn" + ], + "x-ms-enum": { + "name": "VectorSearchAlgorithmKind", + "modelAsString": true, + "values": [ + { + "value": "hnsw", + "name": "Hnsw", + "description": "HNSW (Hierarchical Navigable Small World), a type of approximate nearest neighbors algorithm." + }, + { + "value": "exhaustiveKnn", + "name": "ExhaustiveKnn", + "description": "Exhaustive KNN algorithm which will perform brute-force search." + } + ] + }, + "description": "The algorithm used for indexing and querying." + }, + "VectorSearchCompressionConfiguration": { + "type": "object", + "discriminator": "kind", + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name to associate with this particular configuration.", + "x-nullable": false + }, + "kind": { + "$ref": "#/definitions/VectorSearchCompressionKind", + "description": "The name of the kind of compression method being configured for use with vector search.", + "x-nullable": false + }, + "rerankWithOriginalVectors": { + "type": "boolean", + "default": true, + "description": "If set to true, once the ordered set of results calculated using compressed vectors are obtained, they will be reranked again by recalculating the full-precision similarity scores. This will improve recall at the expense of latency." + }, + "defaultOversampling": { + "type": "number", + "format": "double", + "description": "Default oversampling factor. Oversampling will internally request more documents (specified by this multiplier) in the initial search. This increases the set of results that will be reranked using recomputed similarity scores from full-precision vectors. Minimum value is 1, meaning no oversampling (1x). This parameter can only be set when rerankWithOriginalVectors is true. Higher values improve recall at the expense of latency.", + "x-nullable": true + } + }, + "required": [ + "name", + "kind" + ], + "description": "Contains configuration options specific to the compression method used during indexing or querying." + }, + "ScalarQuantizationVectorSearchCompressionConfiguration": { + "x-ms-client-name": "ScalarQuantizationCompressionConfiguration", + "type": "object", + "x-ms-discriminator-value": "scalarQuantization", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchCompressionConfiguration" + } + ], + "properties": { + "scalarQuantizationParameters": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/ScalarQuantizationParameters", + "description": "Contains the parameters specific to Scalar Quantization." + } + }, + "description": "Contains configuration options specific to the scalar quantization compression method used during indexing and querying." + }, + "ScalarQuantizationParameters": { + "type": "object", + "properties": { + "quantizedDataType": { + "$ref": "#/definitions/VectorSearchCompressionTargetDataType", + "x-nullable": true, + "description": "The quantized data type of compressed vector values." + } + }, + "description": "Contains the parameters specific to Scalar Quantization." + }, + "BinaryQuantizationVectorSearchCompressionConfiguration": { + "x-ms-client-name": "BinaryQuantizationCompressionConfiguration", + "type": "object", + "x-ms-discriminator-value": "binaryQuantization", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchCompressionConfiguration" + } + ], + "description": "Contains configuration options specific to the binary quantization compression method used during indexing and querying." + }, + "VectorSearchCompressionTargetDataType": { + "type": "string", + "enum": [ + "int8" + ], + "x-ms-enum": { + "name": "VectorSearchCompressionTargetDataType", + "modelAsString": true, + "values": [ + { + "value": "int8", + "name": "Int8" + } + ] + }, + "description": "The quantized data type of compressed vector values." + }, + "VectorSearchCompressionKind": { + "type": "string", + "enum": [ + "scalarQuantization", + "binaryQuantization" + ], + "x-ms-enum": { + "name": "VectorSearchCompressionKind", + "modelAsString": true, + "values": [ + { + "value": "scalarQuantization", + "name": "ScalarQuantization", + "description": "Scalar Quantization, a type of compression method. In scalar quantization, the original vectors values are compressed to a narrower type by discretizing and representing each component of a vector using a reduced set of quantized values, thereby reducing the overall data size." + }, + { + "value": "binaryQuantization", + "name": "BinaryQuantization", + "description": "Binary Quantization, a type of compression method. In binary quantization, the original vectors values are compressed to the narrower binary type by discretizing and representing each component of a vector using binary values, thereby reducing the overall data size." + } + ] + }, + "description": "The compression method used for indexing and querying." + }, + "VectorSearchVectorizer": { + "type": "object", + "discriminator": "kind", + "properties": { + "name": { + "externalDocs": { + "url": "https://docs.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name to associate with this particular vectorization method.", + "x-nullable": false + }, + "kind": { + "$ref": "#/definitions/VectorSearchVectorizerKind", + "description": "The name of the kind of vectorization method being configured for use with vector search.", + "x-nullable": false + } + }, + "required": [ + "name", + "kind" + ], + "description": "Specifies the vectorization method to be used during query time." + }, + "AzureOpenAIVectorizer": { + "type": "object", + "x-ms-discriminator-value": "azureOpenAI", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchVectorizer" + } + ], + "properties": { + "azureOpenAIParameters": { + "x-ms-client-name": "AzureOpenAIParameters", + "$ref": "#/definitions/AzureOpenAIParameters", + "description": "Contains the parameters specific to Azure OpenAI embedding vectorization." + } + }, + "description": "Specifies the Azure OpenAI resource used to vectorize a query string." + }, + "AzureOpenAIParameters": { + "type": "object", + "properties": { + "resourceUri": { + "type": "string", + "format": "uri", + "description": "The resource URI of the Azure OpenAI resource." + }, + "deploymentId": { + "x-ms-client-name": "deploymentName", + "type": "string", + "description": "ID of the Azure OpenAI model deployment on the designated resource." + }, + "apiKey": { + "type": "string", + "description": "API key of the designated Azure OpenAI resource." + }, + "authIdentity": { + "$ref": "#/definitions/SearchIndexerDataIdentity", + "x-nullable": true, + "description": "The user-assigned managed identity used for outbound connections." + }, + "modelName": { + "$ref": "#/definitions/AzureOpenAIModelName", + "description": "The name of the embedding model that is deployed at the provided deploymentId path." + } + }, + "description": "Specifies the parameters for connecting to the Azure OpenAI resource." + }, + "AzureOpenAIModelName": { + "type": "string", + "enum": [ + "text-embedding-ada-002", + "text-embedding-3-large", + "text-embedding-3-small" + ], + "x-ms-enum": { + "name": "AzureOpenAIModelName", + "modelAsString": true, + "values": [ + { + "value": "text-embedding-ada-002", + "name": "TextEmbeddingAda002" + }, + { + "value": "text-embedding-3-large", + "name": "TextEmbedding3Large" + }, + { + "value": "text-embedding-3-small", + "name": "TextEmbedding3Small" + } + ] + }, + "description": "The Azure Open AI model name that will be called." + }, + "WebApiVectorizer": { + "type": "object", + "x-ms-discriminator-value": "customWebApi", + "allOf": [ + { + "$ref": "#/definitions/VectorSearchVectorizer" + } + ], + "properties": { + "customWebApiParameters": { + "x-ms-client-name": "WebApiParameters", + "$ref": "#/definitions/WebApiParameters", + "description": "Specifies the properties of the user-defined vectorizer." + } + }, + "description": "Specifies a user-defined vectorizer for generating the vector embedding of a query string. Integration of an external vectorizer is achieved using the custom Web API interface of a skillset." + }, + "WebApiParameters": { + "type": "object", + "properties": { + "uri": { + "type": "string", + "format": "uri", + "description": "The URI of the Web API providing the vectorizer." + }, + "httpHeaders": { + "$ref": "#/definitions/WebApiHttpHeaders", + "description": "The headers required to make the HTTP request." + }, + "httpMethod": { + "type": "string", + "description": "The method for the HTTP request." + }, + "timeout": { + "type": "string", + "format": "duration", + "description": "The desired timeout for the request. Default is 30 seconds." + }, + "authResourceId": { + "type": "string", + "x-nullable": true, + "x-ms-format": "arm-id", + "description": "Applies to custom endpoints that connect to external code in an Azure function or some other application that provides the transformations. This value should be the application ID created for the function or app when it was registered with Azure Active Directory. When specified, the vectorization connects to the function or app using a managed ID (either system or user-assigned) of the search service and the access token of the function or app, using this value as the resource id for creating the scope of the access token." + }, + "authIdentity": { + "$ref": "#/definitions/SearchIndexerDataIdentity", + "x-nullable": true, + "description": "The user-assigned managed identity used for outbound connections. If an authResourceId is provided and it's not specified, the system-assigned managed identity is used. On updates to the indexer, if the identity is unspecified, the value remains unchanged. If set to \"none\", the value of this property is cleared." + } + }, + "description": "Specifies the properties for connecting to a user-defined vectorizer." + }, + "VectorSearchVectorizerKind": { + "type": "string", + "enum": [ + "azureOpenAI", + "customWebApi" + ], + "x-ms-enum": { + "name": "VectorSearchVectorizerKind", + "modelAsString": true, + "values": [ + { + "value": "azureOpenAI", + "name": "AzureOpenAI", + "description": "Generate embeddings using an Azure OpenAI resource at query time." + }, + { + "value": "customWebApi", + "name": "CustomWebApi", + "description": "Generate embeddings using a custom web endpoint at query time." + } + ] + }, + "description": "The vectorization method to be used during query time." + }, + "DataSourceCredentials": { + "properties": { + "connectionString": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Data-Source" + }, + "type": "string", + "description": "The connection string for the datasource. Set to `` (with brackets) if you don't want the connection string updated. Set to `` if you want to remove the connection string value from the datasource." + } + }, + "description": "Represents credentials that can be used to connect to a datasource." + }, + "SearchIndexerDataContainer": { + "properties": { + "name": { + "type": "string", + "description": "The name of the table or view (for Azure SQL data source) or collection (for CosmosDB data source) that will be indexed." + }, + "query": { + "type": "string", + "description": "A query that is applied to this data container. The syntax and meaning of this parameter is datasource-specific. Not supported by Azure SQL datasources." + } + }, + "required": [ + "name" + ], + "description": "Represents information about the entity (such as Azure SQL table or CosmosDB collection) that will be indexed." + }, + "SearchIndexerDataIdentity": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of identity." + } + }, + "required": [ + "@odata.type" + ], + "description": "Abstract base type for data identities." + }, + "SearchIndexerDataNoneIdentity": { + "description": "Clears the identity property of a datasource.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.DataNoneIdentity", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerDataIdentity" + } + ] + }, + "SearchIndexerDataUserAssignedIdentity": { + "description": "Specifies the identity for a datasource to use.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.DataUserAssignedIdentity", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerDataIdentity" + } + ], + "properties": { + "userAssignedIdentity": { + "type": "string", + "description": "The fully qualified Azure resource Id of a user assigned managed identity typically in the form \"/subscriptions/12345678-1234-1234-1234-1234567890ab/resourceGroups/rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myId\" that should have been assigned to the search service." + } + }, + "required": [ + "userAssignedIdentity" + ] + }, + "DataChangeDetectionPolicy": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of data change detection policy." + } + }, + "required": [ + "@odata.type" + ], + "description": "Base type for data change detection policies." + }, + "HighWaterMarkChangeDetectionPolicy": { + "description": "Defines a data change detection policy that captures changes based on the value of a high water mark column.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.HighWaterMarkChangeDetectionPolicy", + "allOf": [ + { + "$ref": "#/definitions/DataChangeDetectionPolicy" + } + ], + "properties": { + "highWaterMarkColumnName": { + "type": "string", + "description": "The name of the high water mark column." + } + }, + "required": [ + "highWaterMarkColumnName" + ] + }, + "SqlIntegratedChangeTrackingPolicy": { + "description": "Defines a data change detection policy that captures changes using the Integrated Change Tracking feature of Azure SQL Database.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.SqlIntegratedChangeTrackingPolicy", + "allOf": [ + { + "$ref": "#/definitions/DataChangeDetectionPolicy" + } + ] + }, + "DataDeletionDetectionPolicy": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of data deletion detection policy." + } + }, + "required": [ + "@odata.type" + ], + "description": "Base type for data deletion detection policies." + }, + "SoftDeleteColumnDeletionDetectionPolicy": { + "description": "Defines a data deletion detection policy that implements a soft-deletion strategy. It determines whether an item should be deleted based on the value of a designated 'soft delete' column.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.SoftDeleteColumnDeletionDetectionPolicy", + "allOf": [ + { + "$ref": "#/definitions/DataDeletionDetectionPolicy" + } + ], + "properties": { + "softDeleteColumnName": { + "type": "string", + "description": "The name of the column to use for soft-deletion detection." + }, + "softDeleteMarkerValue": { + "type": "string", + "description": "The marker value that identifies an item as deleted." + } + } + }, + "SearchIndexerDataSourceType": { + "type": "string", + "enum": [ + "azuresql", + "cosmosdb", + "azureblob", + "azuretable", + "mysql", + "adlsgen2" + ], + "x-ms-enum": { + "name": "SearchIndexerDataSourceType", + "modelAsString": true, + "values": [ + { + "value": "azuresql", + "name": "AzureSql", + "description": "Indicates an Azure SQL datasource." + }, + { + "value": "cosmosdb", + "name": "CosmosDb", + "description": "Indicates a CosmosDB datasource." + }, + { + "value": "azureblob", + "name": "AzureBlob", + "description": "Indicates an Azure Blob datasource." + }, + { + "value": "azuretable", + "name": "AzureTable", + "description": "Indicates an Azure Table datasource." + }, + { + "value": "mysql", + "name": "MySql", + "description": "Indicates a MySql datasource." + }, + { + "value": "adlsgen2", + "name": "AdlsGen2", + "description": "Indicates an ADLS Gen2 datasource." + } + ] + }, + "description": "Defines the type of a datasource." + }, + "SearchIndexerDataSource": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the datasource." + }, + "description": { + "type": "string", + "description": "The description of the datasource." + }, + "type": { + "$ref": "#/definitions/SearchIndexerDataSourceType", + "description": "The type of the datasource." + }, + "credentials": { + "$ref": "#/definitions/DataSourceCredentials", + "description": "Credentials for the datasource." + }, + "container": { + "$ref": "#/definitions/SearchIndexerDataContainer", + "description": "The data container for the datasource." + }, + "dataChangeDetectionPolicy": { + "$ref": "#/definitions/DataChangeDetectionPolicy", + "x-nullable": true, + "description": "The data change detection policy for the datasource." + }, + "dataDeletionDetectionPolicy": { + "$ref": "#/definitions/DataDeletionDetectionPolicy", + "x-nullable": true, + "description": "The data deletion detection policy for the datasource." + }, + "@odata.etag": { + "x-ms-client-name": "ETag", + "type": "string", + "description": "The ETag of the data source." + }, + "encryptionKey": { + "$ref": "#/definitions/SearchResourceEncryptionKey", + "description": "A description of an encryption key that you create in Azure Key Vault. This key is used to provide an additional level of encryption-at-rest for your datasource definition when you want full assurance that no one, not even Microsoft, can decrypt your data source definition. Once you have encrypted your data source definition, it will always remain encrypted. The search service will ignore attempts to set this property to null. You can change this property as needed if you want to rotate your encryption key; Your datasource definition will be unaffected. Encryption with customer-managed keys is not available for free search services, and is only available for paid services created on or after January 1, 2019.", + "externalDocs": { + "url": "https://aka.ms/azure-search-encryption-with-cmk" + }, + "x-nullable": true + } + }, + "required": [ + "name", + "type", + "credentials", + "container" + ], + "description": "Represents a datasource definition, which can be used to configure an indexer." + }, + "ListDataSourcesResult": { + "properties": { + "value": { + "x-ms-client-name": "DataSources", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndexerDataSource" + }, + "description": "The datasources in the Search service." + } + }, + "required": [ + "value" + ], + "description": "Response from a List Datasources request. If successful, it includes the full definitions of all datasources." + }, + "IndexingSchedule": { + "properties": { + "interval": { + "type": "string", + "format": "duration", + "description": "The interval of time between indexer executions." + }, + "startTime": { + "type": "string", + "format": "date-time", + "description": "The time when an indexer should start running." + } + }, + "required": [ + "interval" + ], + "description": "Represents a schedule for indexer execution." + }, + "IndexingParameters": { + "properties": { + "batchSize": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The number of items that are read from the data source and indexed as a single batch in order to improve performance. The default depends on the data source type." + }, + "maxFailedItems": { + "type": "integer", + "format": "int32", + "default": 0, + "x-nullable": true, + "description": "The maximum number of items that can fail indexing for indexer execution to still be considered successful. -1 means no limit. Default is 0." + }, + "maxFailedItemsPerBatch": { + "type": "integer", + "format": "int32", + "default": 0, + "x-nullable": true, + "description": "The maximum number of items in a single batch that can fail indexing for the batch to still be considered successful. -1 means no limit. Default is 0." + }, + "configuration": { + "$ref": "#/definitions/IndexingParametersConfiguration" + } + }, + "description": "Represents parameters for indexer execution.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/create-indexer#parameters" + } + }, + "IndexingParametersConfiguration": { + "type": "object", + "properties": { + "parsingMode": { + "$ref": "#/definitions/ParsingMode" + }, + "excludedFileNameExtensions": { + "type": "string", + "default": "", + "description": "Comma-delimited list of filename extensions to ignore when processing from Azure blob storage. For example, you could exclude \".png, .mp4\" to skip over those files during indexing." + }, + "indexedFileNameExtensions": { + "type": "string", + "default": "", + "description": "Comma-delimited list of filename extensions to select when processing from Azure blob storage. For example, you could focus indexing on specific application files \".docx, .pptx, .msg\" to specifically include those file types." + }, + "failOnUnsupportedContentType": { + "type": "boolean", + "default": false, + "description": "For Azure blobs, set to false if you want to continue indexing when an unsupported content type is encountered, and you don't know all the content types (file extensions) in advance." + }, + "failOnUnprocessableDocument": { + "type": "boolean", + "default": false, + "description": "For Azure blobs, set to false if you want to continue indexing if a document fails indexing." + }, + "indexStorageMetadataOnlyForOversizedDocuments": { + "type": "boolean", + "default": false, + "description": "For Azure blobs, set this property to true to still index storage metadata for blob content that is too large to process. Oversized blobs are treated as errors by default. For limits on blob size, see https://learn.microsoft.com/azure/search/search-limits-quotas-capacity." + }, + "delimitedTextHeaders": { + "type": "string", + "description": "For CSV blobs, specifies a comma-delimited list of column headers, useful for mapping source fields to destination fields in an index." + }, + "delimitedTextDelimiter": { + "type": "string", + "description": "For CSV blobs, specifies the end-of-line single-character delimiter for CSV files where each line starts a new document (for example, \"|\")." + }, + "firstLineContainsHeaders": { + "type": "boolean", + "default": true, + "description": "For CSV blobs, indicates that the first (non-blank) line of each blob contains headers." + }, + "documentRoot": { + "type": "string", + "description": "For JSON arrays, given a structured or semi-structured document, you can specify a path to the array using this property." + }, + "dataToExtract": { + "$ref": "#/definitions/DataToExtract" + }, + "imageAction": { + "$ref": "#/definitions/ImageAction" + }, + "allowSkillsetToReadFileData": { + "type": "boolean", + "default": false, + "description": "If true, will create a path //document//file_data that is an object representing the original file data downloaded from your blob data source. This allows you to pass the original file data to a custom skill for processing within the enrichment pipeline, or to the Document Extraction skill." + }, + "pdfTextRotationAlgorithm": { + "$ref": "#/definitions/PdfTextRotationAlgorithm" + }, + "executionEnvironment": { + "$ref": "#/definitions/ExecutionEnvironment" + }, + "queryTimeout": { + "type": "string", + "default": "00:05:00", + "description": "Increases the timeout beyond the 5-minute default for Azure SQL database data sources, specified in the format \"hh:mm:ss\"." + } + }, + "additionalProperties": true, + "description": "A dictionary of indexer-specific configuration properties. Each name is the name of a specific property. Each value must be of a primitive type.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/create-indexer#parameters" + } + }, + "ParsingMode": { + "type": "string", + "enum": [ + "default", + "text", + "delimitedText", + "json", + "jsonArray", + "jsonLines" + ], + "x-ms-enum": { + "name": "BlobIndexerParsingMode", + "modelAsString": true, + "values": [ + { + "value": "default", + "name": "Default", + "description": "Set to default for normal file processing." + }, + { + "value": "text", + "name": "Text", + "description": "Set to text to improve indexing performance on plain text files in blob storage." + }, + { + "value": "delimitedText", + "name": "DelimitedText", + "description": "Set to delimitedText when blobs are plain CSV files." + }, + { + "value": "json", + "name": "Json", + "description": "Set to json to extract structured content from JSON files." + }, + { + "value": "jsonArray", + "name": "JsonArray", + "description": "Set to jsonArray to extract individual elements of a JSON array as separate documents." + }, + { + "value": "jsonLines", + "name": "JsonLines", + "description": "Set to jsonLines to extract individual JSON entities, separated by a new line, as separate documents." + } + ] + }, + "default": "default", + "description": "Represents the parsing mode for indexing from an Azure blob data source." + }, + "DataToExtract": { + "type": "string", + "enum": [ + "storageMetadata", + "allMetadata", + "contentAndMetadata" + ], + "x-ms-enum": { + "name": "BlobIndexerDataToExtract", + "modelAsString": true, + "values": [ + { + "value": "storageMetadata", + "name": "StorageMetadata", + "description": "Indexes just the standard blob properties and user-specified metadata." + }, + { + "value": "allMetadata", + "name": "AllMetadata", + "description": "Extracts metadata provided by the Azure blob storage subsystem and the content-type specific metadata (for example, metadata unique to just .png files are indexed)." + }, + { + "value": "contentAndMetadata", + "name": "ContentAndMetadata", + "description": "Extracts all metadata and textual content from each blob." + } + ] + }, + "default": "contentAndMetadata", + "description": "Specifies the data to extract from Azure blob storage and tells the indexer which data to extract from image content when \"imageAction\" is set to a value other than \"none\". This applies to embedded image content in a .PDF or other application, or image files such as .jpg and .png, in Azure blobs." + }, + "ImageAction": { + "type": "string", + "enum": [ + "none", + "generateNormalizedImages", + "generateNormalizedImagePerPage" + ], + "x-ms-enum": { + "name": "BlobIndexerImageAction", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Ignores embedded images or image files in the data set. This is the default." + }, + { + "value": "generateNormalizedImages", + "name": "GenerateNormalizedImages", + "description": "Extracts text from images (for example, the word \"STOP\" from a traffic stop sign), and embeds it into the content field. This action requires that \"dataToExtract\" is set to \"contentAndMetadata\". A normalized image refers to additional processing resulting in uniform image output, sized and rotated to promote consistent rendering when you include images in visual search results. This information is generated for each image when you use this option." + }, + { + "value": "generateNormalizedImagePerPage", + "name": "GenerateNormalizedImagePerPage", + "description": "Extracts text from images (for example, the word \"STOP\" from a traffic stop sign), and embeds it into the content field, but treats PDF files differently in that each page will be rendered as an image and normalized accordingly, instead of extracting embedded images. Non-PDF file types will be treated the same as if \"generateNormalizedImages\" was set." + } + ] + }, + "default": "none", + "description": "Determines how to process embedded images and image files in Azure blob storage. Setting the \"imageAction\" configuration to any value other than \"none\" requires that a skillset also be attached to that indexer." + }, + "PdfTextRotationAlgorithm": { + "type": "string", + "enum": [ + "none", + "detectAngles" + ], + "x-ms-enum": { + "name": "BlobIndexerPDFTextRotationAlgorithm", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "Leverages normal text extraction. This is the default." + }, + { + "value": "detectAngles", + "name": "DetectAngles", + "description": "May produce better and more readable text extraction from PDF files that have rotated text within them. Note that there may be a small performance speed impact when this parameter is used. This parameter only applies to PDF files, and only to PDFs with embedded text. If the rotated text appears within an embedded image in the PDF, this parameter does not apply." + } + ] + }, + "default": "none", + "description": "Determines algorithm for text extraction from PDF files in Azure blob storage." + }, + "ExecutionEnvironment": { + "type": "string", + "enum": [ + "standard", + "private" + ], + "x-ms-enum": { + "name": "IndexerExecutionEnvironment", + "modelAsString": true, + "values": [ + { + "value": "standard", + "name": "standard", + "description": "Indicates that the search service can determine where the indexer should execute. This is the default environment when nothing is specified and is the recommended value." + }, + { + "value": "private", + "name": "private", + "description": "Indicates that the indexer should run with the environment provisioned specifically for the search service. This should only be specified as the execution environment if the indexer needs to access resources securely over shared private link resources." + } + ] + }, + "default": "standard", + "description": "Specifies the environment in which the indexer should execute." + }, + "FieldMappingFunction": { + "properties": { + "name": { + "type": "string", + "description": "The name of the field mapping function." + }, + "parameters": { + "type": "object", + "x-nullable": true, + "additionalProperties": true, + "description": "A dictionary of parameter name/value pairs to pass to the function. Each value must be of a primitive type." + } + }, + "required": [ + "name" + ], + "description": "Represents a function that transforms a value from a data source before indexing.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/search-indexer-field-mappings" + } + }, + "FieldMapping": { + "properties": { + "sourceFieldName": { + "type": "string", + "description": "The name of the field in the data source." + }, + "targetFieldName": { + "type": "string", + "description": "The name of the target field in the index. Same as the source field name by default." + }, + "mappingFunction": { + "$ref": "#/definitions/FieldMappingFunction", + "x-nullable": true, + "description": "A function to apply to each source field value before indexing." + } + }, + "required": [ + "sourceFieldName" + ], + "description": "Defines a mapping between a field in a data source and a target field in an index.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/search-indexer-field-mappings" + } + }, + "SearchIndexer": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the indexer." + }, + "description": { + "type": "string", + "description": "The description of the indexer." + }, + "dataSourceName": { + "type": "string", + "description": "The name of the datasource from which this indexer reads data." + }, + "skillsetName": { + "type": "string", + "description": "The name of the skillset executing with this indexer." + }, + "targetIndexName": { + "type": "string", + "description": "The name of the index to which this indexer writes data." + }, + "schedule": { + "$ref": "#/definitions/IndexingSchedule", + "x-nullable": true, + "description": "The schedule for this indexer." + }, + "parameters": { + "$ref": "#/definitions/IndexingParameters", + "x-nullable": true, + "description": "Parameters for indexer execution." + }, + "fieldMappings": { + "type": "array", + "items": { + "$ref": "#/definitions/FieldMapping" + }, + "description": "Defines mappings between fields in the data source and corresponding target fields in the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/search-indexer-field-mappings" + } + }, + "outputFieldMappings": { + "type": "array", + "items": { + "$ref": "#/definitions/FieldMapping" + }, + "description": "Output field mappings are applied after enrichment and immediately before indexing.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/search-indexer-field-mappings" + } + }, + "disabled": { + "x-ms-client-name": "IsDisabled", + "type": "boolean", + "default": false, + "x-nullable": true, + "description": "A value indicating whether the indexer is disabled. Default is false." + }, + "@odata.etag": { + "x-ms-client-name": "ETag", + "type": "string", + "description": "The ETag of the indexer." + }, + "encryptionKey": { + "$ref": "#/definitions/SearchResourceEncryptionKey", + "description": "A description of an encryption key that you create in Azure Key Vault. This key is used to provide an additional level of encryption-at-rest for your indexer definition (as well as indexer execution status) when you want full assurance that no one, not even Microsoft, can decrypt them. Once you have encrypted your indexer definition, it will always remain encrypted. The search service will ignore attempts to set this property to null. You can change this property as needed if you want to rotate your encryption key; Your indexer definition (and indexer execution status) will be unaffected. Encryption with customer-managed keys is not available for free search services, and is only available for paid services created on or after January 1, 2019.", + "externalDocs": { + "url": "https://aka.ms/azure-search-encryption-with-cmk" + }, + "x-nullable": true + } + }, + "required": [ + "name", + "dataSourceName", + "targetIndexName" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Indexer-operations" + }, + "description": "Represents an indexer." + }, + "ListIndexersResult": { + "properties": { + "value": { + "x-ms-client-name": "Indexers", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndexer" + }, + "description": "The indexers in the Search service." + } + }, + "required": [ + "value" + ], + "description": "Response from a List Indexers request. If successful, it includes the full definitions of all indexers." + }, + "SearchIndexerError": { + "properties": { + "key": { + "type": "string", + "readOnly": true, + "description": "The key of the item for which indexing failed." + }, + "errorMessage": { + "type": "string", + "readOnly": true, + "description": "The message describing the error that occurred while processing the item." + }, + "statusCode": { + "type": "integer", + "format": "int32", + "x-nullable": false, + "readOnly": true, + "description": "The status code indicating why the indexing operation failed. Possible values include: 400 for a malformed input document, 404 for document not found, 409 for a version conflict, 422 when the index is temporarily unavailable, or 503 for when the service is too busy." + }, + "name": { + "type": "string", + "readOnly": true, + "description": "The name of the source at which the error originated. For example, this could refer to a particular skill in the attached skillset. This may not be always available." + }, + "details": { + "type": "string", + "readOnly": true, + "description": "Additional, verbose details about the error to assist in debugging the indexer. This may not be always available." + }, + "documentationLink": { + "type": "string", + "readOnly": true, + "description": "A link to a troubleshooting guide for these classes of errors. This may not be always available." + } + }, + "required": [ + "errorMessage", + "statusCode" + ], + "description": "Represents an item- or document-level indexing error." + }, + "SearchIndexerWarning": { + "properties": { + "key": { + "type": "string", + "readOnly": true, + "description": "The key of the item which generated a warning." + }, + "message": { + "type": "string", + "readOnly": true, + "description": "The message describing the warning that occurred while processing the item." + }, + "name": { + "type": "string", + "readOnly": true, + "description": "The name of the source at which the warning originated. For example, this could refer to a particular skill in the attached skillset. This may not be always available." + }, + "details": { + "type": "string", + "readOnly": true, + "description": "Additional, verbose details about the warning to assist in debugging the indexer. This may not be always available." + }, + "documentationLink": { + "type": "string", + "readOnly": true, + "description": "A link to a troubleshooting guide for these classes of warnings. This may not be always available." + } + }, + "required": [ + "message" + ], + "description": "Represents an item-level warning." + }, + "IndexerExecutionResult": { + "properties": { + "status": { + "$ref": "#/definitions/IndexerExecutionStatus", + "readOnly": true, + "description": "The outcome of this indexer execution." + }, + "errorMessage": { + "type": "string", + "readOnly": true, + "description": "The error message indicating the top-level error, if any." + }, + "startTime": { + "type": "string", + "format": "date-time", + "readOnly": true, + "description": "The start time of this indexer execution." + }, + "endTime": { + "type": "string", + "format": "date-time", + "readOnly": true, + "x-nullable": true, + "description": "The end time of this indexer execution, if the execution has already completed." + }, + "errors": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndexerError" + }, + "description": "The item-level indexing errors." + }, + "warnings": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndexerWarning" + }, + "description": "The item-level indexing warnings." + }, + "itemsProcessed": { + "x-ms-client-name": "ItemCount", + "type": "integer", + "format": "int32", + "x-nullable": false, + "readOnly": true, + "description": "The number of items that were processed during this indexer execution. This includes both successfully processed items and items where indexing was attempted but failed." + }, + "itemsFailed": { + "x-ms-client-name": "FailedItemCount", + "type": "integer", + "format": "int32", + "x-nullable": false, + "readOnly": true, + "description": "The number of items that failed to be indexed during this indexer execution." + }, + "initialTrackingState": { + "type": "string", + "readOnly": true, + "description": "Change tracking state with which an indexer execution started." + }, + "finalTrackingState": { + "type": "string", + "readOnly": true, + "description": "Change tracking state with which an indexer execution finished." + } + }, + "required": [ + "status", + "errors", + "warnings", + "itemsProcessed", + "itemsFailed" + ], + "description": "Represents the result of an individual indexer execution." + }, + "IndexerExecutionStatus": { + "type": "string", + "enum": [ + "transientFailure", + "success", + "inProgress", + "reset" + ], + "x-ms-enum": { + "name": "IndexerExecutionStatus", + "modelAsString": false, + "values": [ + { + "value": "transientFailure", + "name": "TransientFailure", + "description": "An indexer invocation has failed, but the failure may be transient. Indexer invocations will continue per schedule." + }, + { + "value": "success", + "name": "Success", + "description": "Indexer execution completed successfully." + }, + { + "value": "inProgress", + "name": "InProgress", + "description": "Indexer execution is in progress." + }, + { + "value": "reset", + "name": "Reset", + "description": "Indexer has been reset." + } + ] + }, + "x-nullable": false, + "description": "Represents the status of an individual indexer execution." + }, + "SearchIndexerStatus": { + "properties": { + "status": { + "$ref": "#/definitions/IndexerStatus", + "readOnly": true, + "description": "Overall indexer status." + }, + "lastResult": { + "$ref": "#/definitions/IndexerExecutionResult", + "readOnly": true, + "description": "The result of the most recent or an in-progress indexer execution." + }, + "executionHistory": { + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/IndexerExecutionResult" + }, + "description": "History of the recent indexer executions, sorted in reverse chronological order." + }, + "limits": { + "$ref": "#/definitions/SearchIndexerLimits", + "readOnly": true, + "description": "The execution limits for the indexer." + } + }, + "required": [ + "status", + "executionHistory", + "limits" + ], + "description": "Represents the current status and execution history of an indexer." + }, + "IndexerStatus": { + "type": "string", + "enum": [ + "unknown", + "error", + "running" + ], + "x-ms-enum": { + "name": "IndexerStatus", + "modelAsString": false, + "values": [ + { + "value": "unknown", + "name": "Unknown", + "description": "Indicates that the indexer is in an unknown state." + }, + { + "value": "error", + "name": "Error", + "description": "Indicates that the indexer experienced an error that cannot be corrected without human intervention." + }, + { + "value": "running", + "name": "Running", + "description": "Indicates that the indexer is running normally." + } + ] + }, + "x-nullable": false, + "description": "Represents the overall indexer status." + }, + "SearchIndexerLimits": { + "properties": { + "maxRunTime": { + "type": "string", + "format": "duration", + "readOnly": true, + "description": "The maximum duration that the indexer is permitted to run for one execution." + }, + "maxDocumentExtractionSize": { + "type": "number", + "format": "int64", + "readOnly": true, + "description": "The maximum size of a document, in bytes, which will be considered valid for indexing." + }, + "maxDocumentContentCharactersToExtract": { + "type": "number", + "format": "int64", + "readOnly": true, + "description": "The maximum number of characters that will be extracted from a document picked up for indexing." + } + } + }, + "SearchField": { + "properties": { + "name": { + "type": "string", + "description": "The name of the field, which must be unique within the fields collection of the index or parent field.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + } + }, + "type": { + "$ref": "#/definitions/SearchFieldDataType", + "description": "The data type of the field.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/supported-data-types" + } + }, + "key": { + "type": "boolean", + "description": "A value indicating whether the field uniquely identifies documents in the index. Exactly one top-level field in each index must be chosen as the key field and it must be of type Edm.String. Key fields can be used to look up documents directly and update or delete specific documents. Default is false for simple fields and null for complex fields." + }, + "retrievable": { + "type": "boolean", + "description": "A value indicating whether the field can be returned in a search result. You can disable this option if you want to use a field (for example, margin) as a filter, sorting, or scoring mechanism but do not want the field to be visible to the end user. This property must be true for key fields, and it must be null for complex fields. This property can be changed on existing fields. Enabling this property does not cause any increase in index storage requirements. Default is true for simple fields, false for vector fields, and null for complex fields." + }, + "stored": { + "type": "boolean", + "description": "An immutable value indicating whether the field will be persisted separately on disk to be returned in a search result. You can disable this option if you don't plan to return the field contents in a search response to save on storage overhead. This can only be set during index creation and only for vector fields. This property cannot be changed for existing fields or set as false for new fields. If this property is set as false, the property 'retrievable' must also be set to false. This property must be true or unset for key fields, for new fields, and for non-vector fields, and it must be null for complex fields. Disabling this property will reduce index storage requirements. The default is true for vector fields." + }, + "searchable": { + "type": "boolean", + "description": "A value indicating whether the field is full-text searchable. This means it will undergo analysis such as word-breaking during indexing. If you set a searchable field to a value like \"sunny day\", internally it will be split into the individual tokens \"sunny\" and \"day\". This enables full-text searches for these terms. Fields of type Edm.String or Collection(Edm.String) are searchable by default. This property must be false for simple fields of other non-string data types, and it must be null for complex fields. Note: searchable fields consume extra space in your index to accommodate additional tokenized versions of the field value for full-text searches. If you want to save space in your index and you don't need a field to be included in searches, set searchable to false." + }, + "filterable": { + "type": "boolean", + "description": "A value indicating whether to enable the field to be referenced in $filter queries. filterable differs from searchable in how strings are handled. Fields of type Edm.String or Collection(Edm.String) that are filterable do not undergo word-breaking, so comparisons are for exact matches only. For example, if you set such a field f to \"sunny day\", $filter=f eq 'sunny' will find no matches, but $filter=f eq 'sunny day' will. This property must be null for complex fields. Default is true for simple fields and null for complex fields." + }, + "sortable": { + "type": "boolean", + "description": "A value indicating whether to enable the field to be referenced in $orderby expressions. By default, the search engine sorts results by score, but in many experiences users will want to sort by fields in the documents. A simple field can be sortable only if it is single-valued (it has a single value in the scope of the parent document). Simple collection fields cannot be sortable, since they are multi-valued. Simple sub-fields of complex collections are also multi-valued, and therefore cannot be sortable. This is true whether it's an immediate parent field, or an ancestor field, that's the complex collection. Complex fields cannot be sortable and the sortable property must be null for such fields. The default for sortable is true for single-valued simple fields, false for multi-valued simple fields, and null for complex fields." + }, + "facetable": { + "type": "boolean", + "description": "A value indicating whether to enable the field to be referenced in facet queries. Typically used in a presentation of search results that includes hit count by category (for example, search for digital cameras and see hits by brand, by megapixels, by price, and so on). This property must be null for complex fields. Fields of type Edm.GeographyPoint or Collection(Edm.GeographyPoint) cannot be facetable. Default is true for all other simple fields." + }, + "analyzer": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Language-support" + }, + "$ref": "#/definitions/LexicalAnalyzerName", + "description": "The name of the analyzer to use for the field. This option can be used only with searchable fields and it can't be set together with either searchAnalyzer or indexAnalyzer. Once the analyzer is chosen, it cannot be changed for the field. Must be null for complex fields.", + "x-nullable": true + }, + "searchAnalyzer": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Language-support" + }, + "$ref": "#/definitions/LexicalAnalyzerName", + "description": "The name of the analyzer used at search time for the field. This option can be used only with searchable fields. It must be set together with indexAnalyzer and it cannot be set together with the analyzer option. This property cannot be set to the name of a language analyzer; use the analyzer property instead if you need a language analyzer. This analyzer can be updated on an existing field. Must be null for complex fields.", + "x-nullable": true + }, + "indexAnalyzer": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Language-support" + }, + "$ref": "#/definitions/LexicalAnalyzerName", + "description": "The name of the analyzer used at indexing time for the field. This option can be used only with searchable fields. It must be set together with searchAnalyzer and it cannot be set together with the analyzer option. This property cannot be set to the name of a language analyzer; use the analyzer property instead if you need a language analyzer. Once the analyzer is chosen, it cannot be changed for the field. Must be null for complex fields.", + "x-nullable": true + }, + "dimensions": { + "x-ms-client-name": "vectorSearchDimensions", + "type": "integer", + "format": "int32", + "x-nullable": true, + "minimum": 2, + "maximum": 2048, + "description": "The dimensionality of the vector field." + }, + "vectorSearchProfile": { + "x-ms-client-name": "vectorSearchProfileName", + "type": "string", + "x-nullable": true, + "description": "The name of the vector search profile that specifies the algorithm and vectorizer to use when searching the vector field." + }, + "vectorEncoding": { + "x-ms-client-name": "VectorEncodingFormat", + "$ref": "#/definitions/VectorEncodingFormat", + "description": "The encoding format to interpret the field contents.", + "x-nullable": true + }, + "synonymMaps": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Synonym-Map-operations" + }, + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of the names of synonym maps to associate with this field. This option can be used only with searchable fields. Currently only one synonym map per field is supported. Assigning a synonym map to a field ensures that query terms targeting that field are expanded at query-time using the rules in the synonym map. This attribute can be changed on existing fields. Must be null or an empty collection for complex fields." + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchField" + }, + "description": "A list of sub-fields if this is a field of type Edm.ComplexType or Collection(Edm.ComplexType). Must be null or empty for simple fields." + } + }, + "required": [ + "name", + "type" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Index" + }, + "description": "Represents a field in an index definition, which describes the name, data type, and search behavior of a field." + }, + "TextWeights": { + "properties": { + "weights": { + "type": "object", + "additionalProperties": { + "type": "number", + "format": "double", + "x-nullable": false + }, + "description": "The dictionary of per-field weights to boost document scoring. The keys are field names and the values are the weights for each field." + } + }, + "required": [ + "weights" + ], + "description": "Defines weights on index fields for which matches should boost scoring in search queries." + }, + "ScoringFunction": { + "discriminator": "type", + "properties": { + "type": { + "type": "string", + "description": "Indicates the type of function to use. Valid values include magnitude, freshness, distance, and tag. The function type must be lower case." + }, + "fieldName": { + "type": "string", + "description": "The name of the field used as input to the scoring function." + }, + "boost": { + "type": "number", + "format": "double", + "description": "A multiplier for the raw score. Must be a positive number not equal to 1.0." + }, + "interpolation": { + "$ref": "#/definitions/ScoringFunctionInterpolation", + "description": "A value indicating how boosting will be interpolated across document scores; defaults to \"Linear\"." + } + }, + "required": [ + "type", + "fieldName", + "boost" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Base type for functions that can modify document scores during ranking." + }, + "DistanceScoringFunction": { + "x-ms-discriminator-value": "distance", + "allOf": [ + { + "$ref": "#/definitions/ScoringFunction" + } + ], + "properties": { + "distance": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/DistanceScoringParameters", + "description": "Parameter values for the distance scoring function." + } + }, + "required": [ + "distance" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Defines a function that boosts scores based on distance from a geographic location." + }, + "DistanceScoringParameters": { + "properties": { + "referencePointParameter": { + "type": "string", + "description": "The name of the parameter passed in search queries to specify the reference location." + }, + "boostingDistance": { + "type": "number", + "format": "double", + "description": "The distance in kilometers from the reference location where the boosting range ends." + } + }, + "required": [ + "referencePointParameter", + "boostingDistance" + ], + "description": "Provides parameter values to a distance scoring function." + }, + "FreshnessScoringFunction": { + "x-ms-discriminator-value": "freshness", + "allOf": [ + { + "$ref": "#/definitions/ScoringFunction" + } + ], + "properties": { + "freshness": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/FreshnessScoringParameters", + "description": "Parameter values for the freshness scoring function." + } + }, + "required": [ + "freshness" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Defines a function that boosts scores based on the value of a date-time field." + }, + "FreshnessScoringParameters": { + "properties": { + "boostingDuration": { + "type": "string", + "format": "duration", + "description": "The expiration period after which boosting will stop for a particular document." + } + }, + "required": [ + "boostingDuration" + ], + "description": "Provides parameter values to a freshness scoring function." + }, + "MagnitudeScoringFunction": { + "x-ms-discriminator-value": "magnitude", + "allOf": [ + { + "$ref": "#/definitions/ScoringFunction" + } + ], + "properties": { + "magnitude": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/MagnitudeScoringParameters", + "description": "Parameter values for the magnitude scoring function." + } + }, + "required": [ + "magnitude" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Defines a function that boosts scores based on the magnitude of a numeric field." + }, + "MagnitudeScoringParameters": { + "properties": { + "boostingRangeStart": { + "type": "number", + "format": "double", + "description": "The field value at which boosting starts." + }, + "boostingRangeEnd": { + "type": "number", + "format": "double", + "description": "The field value at which boosting ends." + }, + "constantBoostBeyondRange": { + "x-ms-client-name": "ShouldBoostBeyondRangeByConstant", + "type": "boolean", + "description": "A value indicating whether to apply a constant boost for field values beyond the range end value; default is false." + } + }, + "required": [ + "boostingRangeStart", + "boostingRangeEnd" + ], + "description": "Provides parameter values to a magnitude scoring function." + }, + "TagScoringFunction": { + "x-ms-discriminator-value": "tag", + "allOf": [ + { + "$ref": "#/definitions/ScoringFunction" + } + ], + "properties": { + "tag": { + "x-ms-client-name": "Parameters", + "$ref": "#/definitions/TagScoringParameters", + "description": "Parameter values for the tag scoring function." + } + }, + "required": [ + "tag" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Defines a function that boosts scores of documents with string values matching a given list of tags." + }, + "TagScoringParameters": { + "properties": { + "tagsParameter": { + "type": "string", + "description": "The name of the parameter passed in search queries to specify the list of tags to compare against the target field." + } + }, + "required": [ + "tagsParameter" + ], + "description": "Provides parameter values to a tag scoring function." + }, + "ScoringFunctionInterpolation": { + "type": "string", + "enum": [ + "linear", + "constant", + "quadratic", + "logarithmic" + ], + "x-ms-enum": { + "name": "ScoringFunctionInterpolation", + "modelAsString": false, + "values": [ + { + "value": "linear", + "name": "Linear", + "description": "Boosts scores by a linearly decreasing amount. This is the default interpolation for scoring functions." + }, + { + "value": "constant", + "name": "Constant", + "description": "Boosts scores by a constant factor." + }, + { + "value": "quadratic", + "name": "Quadratic", + "description": "Boosts scores by an amount that decreases quadratically. Boosts decrease slowly for higher scores, and more quickly as the scores decrease. This interpolation option is not allowed in tag scoring functions." + }, + { + "value": "logarithmic", + "name": "Logarithmic", + "description": "Boosts scores by an amount that decreases logarithmically. Boosts decrease quickly for higher scores, and more slowly as the scores decrease. This interpolation option is not allowed in tag scoring functions." + } + ] + }, + "description": "Defines the function used to interpolate score boosting across a range of documents." + }, + "ScoringProfile": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the scoring profile." + }, + "text": { + "x-ms-client-name": "TextWeights", + "$ref": "#/definitions/TextWeights", + "description": "Parameters that boost scoring based on text matches in certain index fields.", + "x-nullable": true + }, + "functions": { + "type": "array", + "items": { + "$ref": "#/definitions/ScoringFunction" + }, + "description": "The collection of functions that influence the scoring of documents." + }, + "functionAggregation": { + "$ref": "#/definitions/ScoringFunctionAggregation", + "description": "A value indicating how the results of individual scoring functions should be combined. Defaults to \"Sum\". Ignored if there are no scoring functions." + } + }, + "required": [ + "name" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Add-scoring-profiles-to-a-search-index" + }, + "description": "Defines parameters for a search index that influence scoring in search queries." + }, + "ScoringFunctionAggregation": { + "type": "string", + "enum": [ + "sum", + "average", + "minimum", + "maximum", + "firstMatching" + ], + "x-ms-enum": { + "name": "ScoringFunctionAggregation", + "modelAsString": false, + "values": [ + { + "value": "sum", + "name": "Sum", + "description": "Boost scores by the sum of all scoring function results." + }, + { + "value": "average", + "name": "Average", + "description": "Boost scores by the average of all scoring function results." + }, + { + "value": "minimum", + "name": "Minimum", + "description": "Boost scores by the minimum of all scoring function results." + }, + { + "value": "maximum", + "name": "Maximum", + "description": "Boost scores by the maximum of all scoring function results." + }, + { + "value": "firstMatching", + "name": "FirstMatching", + "description": "Boost scores using the first applicable scoring function in the scoring profile." + } + ] + }, + "description": "Defines the aggregation function used to combine the results of all the scoring functions in a scoring profile." + }, + "CorsOptions": { + "properties": { + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of origins from which JavaScript code will be granted access to your index. Can contain a list of hosts of the form {protocol}://{fully-qualified-domain-name}[:{port#}], or a single '*' to allow all origins (not recommended)." + }, + "maxAgeInSeconds": { + "type": "integer", + "format": "int64", + "description": "The duration for which browsers should cache CORS preflight responses. Defaults to 5 minutes.", + "x-nullable": true + } + }, + "required": [ + "allowedOrigins" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Index" + }, + "description": "Defines options to control Cross-Origin Resource Sharing (CORS) for an index." + }, + "Suggester": { + "properties": { + "name": { + "type": "string", + "description": "The name of the suggester." + }, + "searchMode": { + "type": "string", + "enum": [ + "analyzingInfixMatching" + ], + "x-ms-enum": { + "name": "SuggesterSearchMode", + "modelAsString": false, + "values": [ + { + "value": "analyzingInfixMatching", + "name": "AnalyzingInfixMatching", + "description": "Matches consecutive whole terms and prefixes in a field. For example, for the field 'The fastest brown fox', the queries 'fast' and 'fastest brow' would both match." + } + ] + }, + "description": "A value indicating the capabilities of the suggester." + }, + "sourceFields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The list of field names to which the suggester applies. Each field must be searchable." + } + }, + "required": [ + "name", + "searchMode", + "sourceFields" + ], + "description": "Defines how the Suggest API should apply to a group of fields in the index." + }, + "SearchIndex": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the index." + }, + "fields": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchField" + }, + "description": "The fields of the index." + }, + "scoringProfiles": { + "type": "array", + "items": { + "$ref": "#/definitions/ScoringProfile" + }, + "description": "The scoring profiles for the index." + }, + "defaultScoringProfile": { + "type": "string", + "description": "The name of the scoring profile to use if none is specified in the query. If this property is not set and no scoring profile is specified in the query, then default scoring (tf-idf) will be used." + }, + "corsOptions": { + "$ref": "#/definitions/CorsOptions", + "description": "Options to control Cross-Origin Resource Sharing (CORS) for the index.", + "x-nullable": true + }, + "suggesters": { + "type": "array", + "items": { + "$ref": "#/definitions/Suggester" + }, + "description": "The suggesters for the index." + }, + "analyzers": { + "type": "array", + "items": { + "$ref": "#/definitions/LexicalAnalyzer" + }, + "description": "The analyzers for the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "tokenizers": { + "type": "array", + "items": { + "$ref": "#/definitions/LexicalTokenizer" + }, + "description": "The tokenizers for the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "tokenFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/TokenFilter" + }, + "description": "The token filters for the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "charFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/CharFilter" + }, + "description": "The character filters for the index.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Custom-analyzers-in-Azure-Search" + } + }, + "encryptionKey": { + "$ref": "#/definitions/SearchResourceEncryptionKey", + "description": "A description of an encryption key that you create in Azure Key Vault. This key is used to provide an additional level of encryption-at-rest for your data when you want full assurance that no one, not even Microsoft, can decrypt your data. Once you have encrypted your data, it will always remain encrypted. The search service will ignore attempts to set this property to null. You can change this property as needed if you want to rotate your encryption key; Your data will be unaffected. Encryption with customer-managed keys is not available for free search services, and is only available for paid services created on or after January 1, 2019.", + "externalDocs": { + "url": "https://aka.ms/azure-search-encryption-with-cmk" + }, + "x-nullable": true + }, + "similarity": { + "$ref": "#/definitions/Similarity", + "description": "The type of similarity algorithm to be used when scoring and ranking the documents matching a search query. The similarity algorithm can only be defined at index creation time and cannot be modified on existing indexes. If null, the ClassicSimilarity algorithm is used.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/index-ranking-similarity" + } + }, + "semantic": { + "$ref": "#/definitions/SemanticSettings", + "description": "Defines parameters for a search index that influence semantic capabilities.", + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/semantic-search-overview" + }, + "x-ms-client-name": "SemanticSearch", + "x-nullable": true + }, + "vectorSearch": { + "$ref": "#/definitions/VectorSearch", + "description": "Contains configuration options related to vector search.", + "x-ms-client-name": "VectorSearch", + "x-nullable": true + }, + "@odata.etag": { + "x-ms-client-name": "ETag", + "type": "string", + "description": "The ETag of the index." + } + }, + "required": [ + "name", + "fields" + ], + "description": "Represents a search index definition, which describes the fields and search behavior of an index." + }, + "GetIndexStatisticsResult": { + "properties": { + "documentCount": { + "type": "integer", + "format": "int64", + "x-nullable": false, + "readOnly": true, + "description": "The number of documents in the index." + }, + "storageSize": { + "type": "integer", + "format": "int64", + "x-nullable": false, + "readOnly": true, + "description": "The amount of storage in bytes consumed by the index." + }, + "vectorIndexSize": { + "type": "integer", + "format": "int64", + "readOnly": true, + "description": "The amount of memory in bytes consumed by vectors in the index." + } + }, + "required": [ + "documentCount", + "storageSize", + "vectorIndexSize" + ], + "description": "Statistics for a given index. Statistics are collected periodically and are not guaranteed to always be up-to-date." + }, + "ListIndexesResult": { + "properties": { + "value": { + "x-ms-client-name": "Indexes", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndex" + }, + "description": "The indexes in the Search service." + } + }, + "required": [ + "value" + ], + "description": "Response from a List Indexes request. If successful, it includes the full definitions of all indexes." + }, + "SearchIndexerSkillset": { + "properties": { + "name": { + "type": "string", + "description": "The name of the skillset." + }, + "description": { + "type": "string", + "description": "The description of the skillset." + }, + "skills": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerSkill" + }, + "description": "A list of skills in the skillset." + }, + "cognitiveServices": { + "x-ms-client-name": "CognitiveServicesAccount", + "$ref": "#/definitions/CognitiveServicesAccount", + "description": "Details about the Azure AI service to be used when running skills." + }, + "knowledgeStore": { + "$ref": "#/definitions/SearchIndexerKnowledgeStore", + "description": "Definition of additional projections to Azure blob, table, or files, of enriched data." + }, + "indexProjections": { + "$ref": "#/definitions/SearchIndexerIndexProjections", + "description": "Definition of additional projections to secondary search index(es)." + }, + "@odata.etag": { + "x-ms-client-name": "ETag", + "type": "string", + "description": "The ETag of the skillset." + }, + "encryptionKey": { + "$ref": "#/definitions/SearchResourceEncryptionKey", + "description": "A description of an encryption key that you create in Azure Key Vault. This key is used to provide an additional level of encryption-at-rest for your skillset definition when you want full assurance that no one, not even Microsoft, can decrypt your skillset definition. Once you have encrypted your skillset definition, it will always remain encrypted. The search service will ignore attempts to set this property to null. You can change this property as needed if you want to rotate your encryption key; Your skillset definition will be unaffected. Encryption with customer-managed keys is not available for free search services, and is only available for paid services created on or after January 1, 2019.", + "externalDocs": { + "url": "https://aka.ms/azure-search-encryption-with-cmk" + }, + "x-nullable": true + } + }, + "required": [ + "name", + "skills" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-tutorial-blob" + }, + "description": "A list of skills." + }, + "CognitiveServicesAccount": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of Azure AI service resource attached to a skillset." + }, + "description": { + "type": "string", + "description": "Description of the Azure AI service resource attached to a skillset." + } + }, + "required": [ + "@odata.type" + ], + "description": "Base type for describing any Azure AI service resource attached to a skillset." + }, + "SearchIndexerKnowledgeStore": { + "properties": { + "storageConnectionString": { + "type": "string", + "description": "The connection string to the storage account projections will be stored in." + }, + "projections": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreProjection", + "x-nullable": false + }, + "description": "A list of additional projections to perform during indexing." + } + }, + "required": [ + "storageConnectionString", + "projections" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/knowledge-store-projection-overview" + }, + "description": "Definition of additional projections to azure blob, table, or files, of enriched data." + }, + "SearchIndexerKnowledgeStoreProjection": { + "properties": { + "tables": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreTableProjectionSelector", + "x-nullable": false + }, + "description": "Projections to Azure Table storage." + }, + "objects": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreObjectProjectionSelector", + "x-nullable": false + }, + "description": "Projections to Azure Blob storage." + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreFileProjectionSelector", + "x-nullable": false + }, + "description": "Projections to Azure File storage." + } + }, + "description": "Container object for various projection selectors." + }, + "SearchIndexerKnowledgeStoreProjectionSelector": { + "properties": { + "referenceKeyName": { + "type": "string", + "description": "Name of reference key to different projection." + }, + "generatedKeyName": { + "type": "string", + "description": "Name of generated key to store projection under." + }, + "source": { + "type": "string", + "description": "Source data to project." + }, + "sourceContext": { + "type": "string", + "description": "Source context for complex projections." + }, + "inputs": { + "type": "array", + "items": { + "$ref": "#/definitions/InputFieldMappingEntry" + }, + "description": "Nested inputs for complex projections." + } + }, + "description": "Abstract class to share properties between concrete selectors." + }, + "SearchIndexerKnowledgeStoreBlobProjectionSelector": { + "properties": { + "storageContainer": { + "type": "string", + "description": "Blob container to store projections in." + } + }, + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreProjectionSelector" + } + ], + "required": [ + "storageContainer" + ], + "description": "Abstract class to share properties between concrete selectors." + }, + "SearchIndexerKnowledgeStoreTableProjectionSelector": { + "properties": { + "tableName": { + "type": "string", + "description": "Name of the Azure table to store projected data in." + } + }, + "required": [ + "generatedKeyName", + "tableName" + ], + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreProjectionSelector" + } + ], + "description": "Description for what data to store in Azure Tables." + }, + "SearchIndexerKnowledgeStoreObjectProjectionSelector": { + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreBlobProjectionSelector" + } + ], + "description": "Projection definition for what data to store in Azure Blob." + }, + "SearchIndexerKnowledgeStoreFileProjectionSelector": { + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerKnowledgeStoreBlobProjectionSelector" + } + ], + "description": "Projection definition for what data to store in Azure Files." + }, + "SearchIndexerKnowledgeStoreParameters": { + "type": "object", + "properties": { + "synthesizeGeneratedKeyName": { + "type": "boolean", + "default": false, + "description": "Whether or not projections should synthesize a generated key name if one isn't already present." + } + }, + "additionalProperties": true, + "description": "A dictionary of knowledge store-specific configuration properties. Each name is the name of a specific property. Each value must be of a primitive type." + }, + "SearchIndexerIndexProjections": { + "type": "object", + "properties": { + "selectors": { + "type": "array", + "items": { + "$ref": "#/definitions/SearchIndexerIndexProjectionSelector" + }, + "description": "A list of projections to be performed to secondary search indexes." + }, + "parameters": { + "$ref": "#/definitions/SearchIndexerIndexProjectionsParameters" + } + }, + "required": [ + "selectors" + ], + "description": "Definition of additional projections to secondary search indexes." + }, + "SearchIndexerIndexProjectionSelector": { + "type": "object", + "properties": { + "targetIndexName": { + "type": "string", + "description": "Name of the search index to project to. Must have a key field with the 'keyword' analyzer set." + }, + "parentKeyFieldName": { + "type": "string", + "description": "Name of the field in the search index to map the parent document's key value to. Must be a string field that is filterable and not the key field." + }, + "sourceContext": { + "type": "string", + "description": "Source context for the projections. Represents the cardinality at which the document will be split into multiple sub documents." + }, + "mappings": { + "type": "array", + "items": { + "$ref": "#/definitions/InputFieldMappingEntry" + }, + "description": "Mappings for the projection, or which source should be mapped to which field in the target index." + } + }, + "required": [ + "targetIndexName", + "parentKeyFieldName", + "sourceContext", + "mappings" + ], + "description": "Description for what data to store in the designated search index." + }, + "SearchIndexerIndexProjectionsParameters": { + "type": "object", + "properties": { + "projectionMode": { + "$ref": "#/definitions/IndexProjectionMode", + "description": "Defines behavior of the index projections in relation to the rest of the indexer." + } + }, + "additionalProperties": true, + "description": "A dictionary of index projection-specific configuration properties. Each name is the name of a specific property. Each value must be of a primitive type." + }, + "IndexProjectionMode": { + "type": "string", + "enum": [ + "skipIndexingParentDocuments", + "includeIndexingParentDocuments" + ], + "x-ms-enum": { + "name": "IndexProjectionMode", + "modelAsString": true, + "values": [ + { + "value": "skipIndexingParentDocuments", + "name": "SkipIndexingParentDocuments", + "description": "The source document will be skipped from writing into the indexer's target index." + }, + { + "value": "includeIndexingParentDocuments", + "name": "IncludeIndexingParentDocuments", + "description": "The source document will be written into the indexer's target index. This is the default pattern." + } + ] + }, + "description": "Defines behavior of the index projections in relation to the rest of the indexer." + }, + "DefaultCognitiveServicesAccount": { + "description": "An empty object that represents the default Azure AI service resource for a skillset.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.DefaultCognitiveServices", + "allOf": [ + { + "$ref": "#/definitions/CognitiveServicesAccount" + } + ] + }, + "CognitiveServicesAccountKey": { + "description": "The multi-region account key of an Azure AI service resource that's attached to a skillset.", + "x-ms-discriminator-value": "#Microsoft.Azure.Search.CognitiveServicesByKey", + "allOf": [ + { + "$ref": "#/definitions/CognitiveServicesAccount" + } + ], + "properties": { + "key": { + "type": "string", + "description": "The key used to provision the Azure AI service resource attached to a skillset." + } + }, + "required": [ + "key" + ] + }, + "SearchIndexerSkill": { + "discriminator": "@odata.type", + "properties": { + "@odata.type": { + "type": "string", + "description": "A URI fragment specifying the type of skill." + }, + "name": { + "type": "string", + "description": "The name of the skill which uniquely identifies it within the skillset. A skill with no name defined will be given a default name of its 1-based index in the skills array, prefixed with the character '#'." + }, + "description": { + "type": "string", + "description": "The description of the skill which describes the inputs, outputs, and usage of the skill." + }, + "context": { + "type": "string", + "description": "Represents the level at which operations take place, such as the document root or document content (for example, /document or /document/content). The default is /document." + }, + "inputs": { + "type": "array", + "items": { + "$ref": "#/definitions/InputFieldMappingEntry" + }, + "description": "Inputs of the skills could be a column in the source data set, or the output of an upstream skill." + }, + "outputs": { + "type": "array", + "items": { + "$ref": "#/definitions/OutputFieldMappingEntry" + }, + "description": "The output of a skill is either a field in a search index, or a value that can be consumed as an input by another skill." + } + }, + "required": [ + "@odata.type", + "inputs", + "outputs" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-predefined-skills" + }, + "description": "Base type for skills." + }, + "CustomEntity": { + "properties": { + "name": { + "type": "string", + "description": "The top-level entity descriptor. Matches in the skill output will be grouped by this name, and it should represent the \"normalized\" form of the text being found." + }, + "description": { + "type": "string", + "x-nullable": true, + "description": "This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output." + }, + "type": { + "type": "string", + "x-nullable": true, + "description": "This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output." + }, + "subtype": { + "type": "string", + "x-nullable": true, + "description": "This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output." + }, + "id": { + "type": "string", + "x-nullable": true, + "description": "This field can be used as a passthrough for custom metadata about the matched text(s). The value of this field will appear with every match of its entity in the skill output." + }, + "caseSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Defaults to false. Boolean value denoting whether comparisons with the entity name should be sensitive to character casing. Sample case insensitive matches of \"Microsoft\" could be: microsoft, microSoft, MICROSOFT." + }, + "accentSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Defaults to false. Boolean value denoting whether comparisons with the entity name should be sensitive to accent." + }, + "fuzzyEditDistance": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "Defaults to 0. Maximum value of 5. Denotes the acceptable number of divergent characters that would still constitute a match with the entity name. The smallest possible fuzziness for any given match is returned. For instance, if the edit distance is set to 3, \"Windows10\" would still match \"Windows\", \"Windows10\" and \"Windows 7\". When case sensitivity is set to false, case differences do NOT count towards fuzziness tolerance, but otherwise do." + }, + "defaultCaseSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Changes the default case sensitivity value for this entity. It be used to change the default value of all aliases caseSensitive values." + }, + "defaultAccentSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Changes the default accent sensitivity value for this entity. It be used to change the default value of all aliases accentSensitive values." + }, + "defaultFuzzyEditDistance": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "Changes the default fuzzy edit distance value for this entity. It can be used to change the default value of all aliases fuzzyEditDistance values." + }, + "aliases": { + "type": "array", + "items": { + "$ref": "#/definitions/CustomEntityAlias" + }, + "x-nullable": true, + "description": "An array of complex objects that can be used to specify alternative spellings or synonyms to the root entity name." + } + }, + "required": [ + "name" + ], + "description": "An object that contains information about the matches that were found, and related metadata." + }, + "CustomEntityAlias": { + "properties": { + "text": { + "type": "string", + "description": "The text of the alias." + }, + "caseSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Determine if the alias is case sensitive." + }, + "accentSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "Determine if the alias is accent sensitive." + }, + "fuzzyEditDistance": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "Determine the fuzzy edit distance of the alias." + } + }, + "required": [ + "text" + ], + "description": "A complex object that can be used to specify alternative spellings or synonyms to the root entity name." + }, + "InputFieldMappingEntry": { + "properties": { + "name": { + "type": "string", + "description": "The name of the input." + }, + "source": { + "type": "string", + "description": "The source of the input." + }, + "sourceContext": { + "type": "string", + "description": "The source context used for selecting recursive inputs." + }, + "inputs": { + "type": "array", + "items": { + "$ref": "#/definitions/InputFieldMappingEntry" + }, + "description": "The recursive inputs used when creating a complex type." + } + }, + "required": [ + "name" + ], + "description": "Input field mapping for a skill." + }, + "OutputFieldMappingEntry": { + "properties": { + "name": { + "type": "string", + "description": "The name of the output defined by the skill." + }, + "targetName": { + "type": "string", + "description": "The target name of the output. It is optional and default to name." + } + }, + "required": [ + "name" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "description": "Output field mapping for a skill." + }, + "ConditionalSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Util.ConditionalSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-conditional" + }, + "description": "A skill that enables scenarios that require a Boolean operation to determine the data to assign to an output." + }, + "KeyPhraseExtractionSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.KeyPhraseExtractionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/KeyPhraseExtractionSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + }, + "maxKeyPhraseCount": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "A number indicating how many key phrases to return. If absent, all identified key phrases will be returned." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics service. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-keyphrases" + }, + "description": "A skill that uses text analytics for key phrase extraction." + }, + "OcrSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Vision.OcrSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/OcrSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + }, + "detectOrientation": { + "x-ms-client-name": "ShouldDetectOrientation", + "type": "boolean", + "default": false, + "description": "A value indicating to turn orientation detection on or not. Default is false." + }, + "lineEnding": { + "$ref": "#/definitions/OcrSkillLineEnding", + "description": "Defines the sequence of characters to use between the lines of text recognized by the OCR skill. The default value is \"space\"." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-ocr" + }, + "description": "A skill that extracts text from image files." + }, + "ImageAnalysisSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Vision.ImageAnalysisSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/ImageAnalysisSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + }, + "visualFeatures": { + "type": "array", + "items": { + "$ref": "#/definitions/VisualFeature", + "x-nullable": false + }, + "description": "A list of visual features." + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/ImageDetail", + "x-nullable": false + }, + "description": "A string indicating which domain-specific details to return." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-image-analysis" + }, + "description": "A skill that analyzes image files. It extracts a rich set of visual features based on the image content." + }, + "LanguageDetectionSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.LanguageDetectionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultCountryHint": { + "type": "string", + "x-nullable": true, + "description": "A country code to use as a hint to the language detection model if it cannot disambiguate the language." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics service. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-language-detection" + }, + "description": "A skill that detects the language of input text and reports a single language code for every document submitted on the request. The language code is paired with a score indicating the confidence of the analysis." + }, + "ShaperSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Util.ShaperSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-shaper" + }, + "description": "A skill for reshaping the outputs. It creates a complex type to support composite fields (also known as multipart fields)." + }, + "MergeSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.MergeSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "insertPreTag": { + "type": "string", + "default": " ", + "description": "The tag indicates the start of the merged text. By default, the tag is an empty space." + }, + "insertPostTag": { + "type": "string", + "default": " ", + "description": "The tag indicates the end of the merged text. By default, the tag is an empty space." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-textmerger" + }, + "description": "A skill for merging two or more strings into a single unified string, with an optional user-defined delimiter separating each component part." + }, + "EntityRecognitionSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.EntityRecognitionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "categories": { + "type": "array", + "items": { + "$ref": "#/definitions/EntityCategory", + "x-nullable": false + }, + "description": "A list of entity categories that should be extracted." + }, + "defaultLanguageCode": { + "$ref": "#/definitions/EntityRecognitionSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + }, + "includeTypelessEntities": { + "type": "boolean", + "x-nullable": true, + "description": "Determines whether or not to include entities which are well known but don't conform to a pre-defined type. If this configuration is not set (default), set to null or set to false, entities which don't conform to one of the pre-defined types will not be surfaced." + }, + "minimumPrecision": { + "type": "number", + "format": "double", + "x-nullable": true, + "description": "A value between 0 and 1 that be used to only include entities whose confidence score is greater than the value specified. If not set (default), or if explicitly set to null, all entities will be included." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-entity-recognition" + }, + "description": "This skill is deprecated. Use the V3.EntityRecognitionSkill instead.", + "x-az-search-deprecated": true + }, + "SentimentSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.SentimentSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/SentimentSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-sentiment" + }, + "description": "This skill is deprecated. Use the V3.SentimentSkill instead.", + "x-az-search-deprecated": true + }, + "SentimentSkillV3": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.V3.SentimentSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "type": "string", + "x-nullable": true, + "description": "A value indicating which language code to use. Default is `en`." + }, + "includeOpinionMining": { + "type": "boolean", + "default": false, + "description": "If set to true, the skill output will include information from Text Analytics for opinion mining, namely targets (nouns or verbs) and their associated assessment (adjective) in the text. Default is false." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics service. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-sentiment-v3" + }, + "description": "Using the Text Analytics API, evaluates unstructured text and for each record, provides sentiment labels (such as \"negative\", \"neutral\" and \"positive\") based on the highest confidence score found by the service at a sentence and document-level." + }, + "EntityLinkingSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.V3.EntityLinkingSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "type": "string", + "x-nullable": true, + "description": "A value indicating which language code to use. Default is `en`." + }, + "minimumPrecision": { + "type": "number", + "format": "double", + "minimum": 0.0, + "maximum": 1.0, + "x-nullable": true, + "description": "A value between 0 and 1 that be used to only include entities whose confidence score is greater than the value specified. If not set (default), or if explicitly set to null, all entities will be included." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics service. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-entity-linking-v3" + }, + "description": "Using the Text Analytics API, extracts linked entities from text." + }, + "EntityRecognitionSkillV3": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.V3.EntityRecognitionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "categories": { + "type": "array", + "items": { + "type": "string", + "x-nullable": false + }, + "description": "A list of entity categories that should be extracted." + }, + "defaultLanguageCode": { + "type": "string", + "x-nullable": true, + "description": "A value indicating which language code to use. Default is `en`." + }, + "minimumPrecision": { + "type": "number", + "format": "double", + "minimum": 0.0, + "maximum": 1.0, + "x-nullable": true, + "description": "A value between 0 and 1 that be used to only include entities whose confidence score is greater than the value specified. If not set (default), or if explicitly set to null, all entities will be included." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics API. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-entity-recognition-v3" + }, + "description": "Using the Text Analytics API, extracts entities of different types from text." + }, + "PIIDetectionSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.PIIDetectionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "type": "string", + "x-nullable": true, + "description": "A value indicating which language code to use. Default is `en`." + }, + "minimumPrecision": { + "type": "number", + "format": "double", + "x-nullable": true, + "minimum": 0.0, + "maximum": 1.0, + "description": "A value between 0 and 1 that be used to only include entities whose confidence score is greater than the value specified. If not set (default), or if explicitly set to null, all entities will be included." + }, + "maskingMode": { + "$ref": "#/definitions/PIIDetectionSkillMaskingMode", + "description": "A parameter that provides various ways to mask the personal information detected in the input text. Default is 'none'." + }, + "maskingCharacter": { + "x-ms-client-name": "mask", + "type": "string", + "x-nullable": true, + "maxLength": 1, + "description": "The character used to mask the text if the maskingMode parameter is set to replace. Default is '*'." + }, + "modelVersion": { + "type": "string", + "x-nullable": true, + "description": "The version of the model to use when calling the Text Analytics service. It will default to the latest available when not specified. We recommend you do not specify this value unless absolutely necessary." + }, + "piiCategories": { + "type": "array", + "items": { + "type": "string", + "x-nullable": false + }, + "description": "A list of PII entity categories that should be extracted and masked." + }, + "domain": { + "type": "string", + "x-nullable": true, + "description": "If specified, will set the PII domain to include only a subset of the entity categories. Possible values include: 'phi', 'none'. Default is 'none'." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-pii-detection" + }, + "description": "Using the Text Analytics API, extracts personal information from an input text and gives you the option of masking it." + }, + "SplitSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.SplitSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/SplitSkillLanguage", + "description": "A value indicating which language code to use. Default is `en`." + }, + "textSplitMode": { + "$ref": "#/definitions/TextSplitMode", + "x-nullable": false, + "description": "A value indicating which split mode to perform." + }, + "maximumPageLength": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The desired maximum page length. Default is 10000." + }, + "pageOverlapLength": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "Only applicable when textSplitMode is set to 'pages'. If specified, n+1th chunk will start with this number of characters/tokens from the end of the nth chunk." + }, + "maximumPagesToTake": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "Only applicable when textSplitMode is set to 'pages'. If specified, the SplitSkill will discontinue splitting after processing the first 'maximumPagesToTake' pages, in order to improve performance when only a few initial pages are needed from each document." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-textsplit" + }, + "description": "A skill to split a string into chunks of text." + }, + "CustomEntityLookupSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.CustomEntityLookupSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultLanguageCode": { + "$ref": "#/definitions/CustomEntityLookupSkillLanguage", + "x-nullable": true, + "description": "A value indicating which language code to use. Default is `en`." + }, + "entitiesDefinitionUri": { + "type": "string", + "x-nullable": true, + "description": "Path to a JSON or CSV file containing all the target text to match against. This entity definition is read at the beginning of an indexer run. Any updates to this file during an indexer run will not take effect until subsequent runs. This config must be accessible over HTTPS." + }, + "inlineEntitiesDefinition": { + "type": "array", + "items": { + "$ref": "#/definitions/CustomEntity" + }, + "x-nullable": true, + "description": "The inline CustomEntity definition." + }, + "globalDefaultCaseSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "A global flag for CaseSensitive. If CaseSensitive is not set in CustomEntity, this value will be the default value." + }, + "globalDefaultAccentSensitive": { + "type": "boolean", + "x-nullable": true, + "description": "A global flag for AccentSensitive. If AccentSensitive is not set in CustomEntity, this value will be the default value." + }, + "globalDefaultFuzzyEditDistance": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "A global flag for FuzzyEditDistance. If FuzzyEditDistance is not set in CustomEntity, this value will be the default value." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/en-us/azure/search/cognitive-search-skill-custom-entity-lookup" + }, + "description": "A skill looks for text from a custom, user-defined list of words and phrases." + }, + "TextTranslationSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Text.TranslationSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "defaultToLanguageCode": { + "$ref": "#/definitions/TextTranslationSkillLanguage", + "description": "The language code to translate documents into for documents that don't specify the to language explicitly. " + }, + "defaultFromLanguageCode": { + "$ref": "#/definitions/TextTranslationSkillLanguage", + "description": "The language code to translate documents from for documents that don't specify the from language explicitly." + }, + "suggestedFrom": { + "$ref": "#/definitions/TextTranslationSkillLanguage", + "x-nullable": true, + "description": "The language code to translate documents from when neither the fromLanguageCode input nor the defaultFromLanguageCode parameter are provided, and the automatic language detection is unsuccessful. Default is `en`." + } + }, + "required": [ + "defaultToLanguageCode" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-text-translation" + }, + "description": "A skill to translate text from one language to another." + }, + "DocumentExtractionSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Util.DocumentExtractionSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "parsingMode": { + "type": "string", + "x-nullable": true, + "description": "The parsingMode for the skill. Will be set to 'default' if not defined." + }, + "dataToExtract": { + "type": "string", + "x-nullable": true, + "description": "The type of data to be extracted for the skill. Will be set to 'contentAndMetadata' if not defined." + }, + "configuration": { + "type": "object", + "additionalProperties": true, + "x-nullable": true, + "description": "A dictionary of configurations for the skill." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-skill-document-extraction" + }, + "description": "A skill that extracts content from a file within the enrichment pipeline." + }, + "WebApiSkill": { + "x-ms-discriminator-value": "#Microsoft.Skills.Custom.WebApiSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + } + ], + "properties": { + "uri": { + "type": "string", + "description": "The url for the Web API." + }, + "httpHeaders": { + "$ref": "#/definitions/WebApiHttpHeaders", + "description": "The headers required to make the http request." + }, + "httpMethod": { + "type": "string", + "description": "The method for the http request." + }, + "timeout": { + "type": "string", + "format": "duration", + "description": "The desired timeout for the request. Default is 30 seconds." + }, + "batchSize": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The desired batch size which indicates number of documents." + }, + "degreeOfParallelism": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "If set, the number of parallel calls that can be made to the Web API." + }, + "authResourceId": { + "type": "string", + "x-nullable": true, + "x-ms-format": "arm-id", + "description": "Applies to custom skills that connect to external code in an Azure function or some other application that provides the transformations. This value should be the application ID created for the function or app when it was registered with Azure Active Directory. When specified, the custom skill connects to the function or app using a managed ID (either system or user-assigned) of the search service and the access token of the function or app, using this value as the resource id for creating the scope of the access token." + }, + "authIdentity": { + "$ref": "#/definitions/SearchIndexerDataIdentity", + "x-nullable": true, + "description": "The user-assigned managed identity used for outbound connections. If an authResourceId is provided and it's not specified, the system-assigned managed identity is used. On updates to the indexer, if the identity is unspecified, the value remains unchanged. If set to \"none\", the value of this property is cleared." + } + }, + "required": [ + "uri" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/cognitive-search-custom-skill-web-api" + }, + "description": "A skill that can call a Web API endpoint, allowing you to extend a skillset by having it call your custom code." + }, + "WebApiHttpHeaders": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "A dictionary of http request headers." + }, + "AzureOpenAIEmbeddingSkill": { + "x-ms-client-name": "AzureOpenAIEmbeddingSkill", + "x-ms-discriminator-value": "#Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill", + "allOf": [ + { + "$ref": "#/definitions/SearchIndexerSkill" + }, + { + "$ref": "#/definitions/AzureOpenAIParameters" + } + ], + "properties": { + "dimensions": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The number of dimensions the resulting output embeddings should have. Only supported in text-embedding-3 and later models." + } + }, + "type": "object", + "description": "Allows you to generate a vector embedding for a given text input using the Azure OpenAI resource." + }, + "ListSkillsetsResult": { + "properties": { + "value": { + "x-ms-client-name": "Skillsets", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SearchIndexerSkillset" + }, + "description": "The skillsets defined in the Search service." + } + }, + "required": [ + "value" + ], + "description": "Response from a list skillset request. If successful, it includes the full definitions of all skillsets." + }, + "TextSplitMode": { + "type": "string", + "enum": [ + "pages", + "sentences" + ], + "x-ms-enum": { + "name": "TextSplitMode", + "modelAsString": true, + "values": [ + { + "value": "pages", + "name": "Pages", + "description": "Split the text into individual pages." + }, + { + "value": "sentences", + "name": "Sentences", + "description": "Split the text into individual sentences." + } + ] + }, + "description": "A value indicating which split mode to perform." + }, + "VisualFeature": { + "type": "string", + "enum": [ + "adult", + "brands", + "categories", + "description", + "faces", + "objects", + "tags" + ], + "x-ms-enum": { + "name": "VisualFeature", + "modelAsString": true, + "values": [ + { + "value": "adult", + "name": "Adult", + "description": "Visual features recognized as adult persons." + }, + { + "value": "brands", + "name": "Brands", + "description": "Visual features recognized as commercial brands." + }, + { + "value": "categories", + "name": "Categories", + "description": "Categories." + }, + { + "value": "description", + "name": "Description", + "description": "Description." + }, + { + "value": "faces", + "name": "Faces", + "description": "Visual features recognized as people faces." + }, + { + "value": "objects", + "name": "Objects", + "description": "Visual features recognized as objects." + }, + { + "value": "tags", + "name": "Tags", + "description": "Tags." + } + ] + }, + "description": "The strings indicating what visual feature types to return." + }, + "ImageDetail": { + "type": "string", + "enum": [ + "celebrities", + "landmarks" + ], + "x-ms-enum": { + "name": "ImageDetail", + "modelAsString": true, + "values": [ + { + "value": "celebrities", + "name": "Celebrities", + "description": "Details recognized as celebrities." + }, + { + "value": "landmarks", + "name": "Landmarks", + "description": "Details recognized as landmarks." + } + ] + }, + "description": "A string indicating which domain-specific details to return." + }, + "EntityCategory": { + "type": "string", + "enum": [ + "location", + "organization", + "person", + "quantity", + "datetime", + "url", + "email" + ], + "x-ms-enum": { + "name": "EntityCategory", + "modelAsString": true, + "values": [ + { + "value": "location", + "name": "Location", + "description": "Entities describing a physical location." + }, + { + "value": "organization", + "name": "Organization", + "description": "Entities describing an organization." + }, + { + "value": "person", + "name": "Person", + "description": "Entities describing a person." + }, + { + "value": "quantity", + "name": "Quantity", + "description": "Entities describing a quantity." + }, + { + "value": "datetime", + "name": "Datetime", + "description": "Entities describing a date and time." + }, + { + "value": "url", + "name": "Url", + "description": "Entities describing a URL." + }, + { + "value": "email", + "name": "Email", + "description": "Entities describing an email address." + } + ] + }, + "description": "A string indicating what entity categories to return." + }, + "PIIDetectionSkillMaskingMode": { + "type": "string", + "enum": [ + "none", + "replace" + ], + "x-ms-enum": { + "name": "PIIDetectionSkillMaskingMode", + "modelAsString": true, + "values": [ + { + "value": "none", + "name": "None", + "description": "No masking occurs and the maskedText output will not be returned." + }, + { + "value": "replace", + "name": "Replace", + "description": "Replaces the detected entities with the character given in the maskingCharacter parameter. The character will be repeated to the length of the detected entity so that the offsets will correctly correspond to both the input text as well as the output maskedText." + } + ] + }, + "description": "A string indicating what maskingMode to use to mask the personal information detected in the input text." + }, + "SentimentSkillLanguage": { + "type": "string", + "enum": [ + "da", + "nl", + "en", + "fi", + "fr", + "de", + "el", + "it", + "no", + "pl", + "pt-PT", + "ru", + "es", + "sv", + "tr" + ], + "x-ms-enum": { + "name": "SentimentSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "da", + "description": "Danish" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "el", + "description": "Greek" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "no", + "description": "Norwegian (Bokmaal)" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt-PT", + "description": "Portuguese (Portugal)" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "tr", + "description": "Turkish" + } + ] + }, + "description": "Deprecated. The language codes supported for input text by SentimentSkill.", + "x-az-search-deprecated": true + }, + "KeyPhraseExtractionSkillLanguage": { + "type": "string", + "enum": [ + "da", + "nl", + "en", + "fi", + "fr", + "de", + "it", + "ja", + "ko", + "no", + "pl", + "pt-PT", + "pt-BR", + "ru", + "es", + "sv" + ], + "x-ms-enum": { + "name": "KeyPhraseExtractionSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "da", + "description": "Danish" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "no", + "description": "Norwegian (Bokmaal)" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt-PT", + "description": "Portuguese (Portugal)" + }, + { + "value": "pt-BR", + "description": "Portuguese (Brazil)" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "sv", + "description": "Swedish" + } + ] + }, + "description": "The language codes supported for input text by KeyPhraseExtractionSkill." + }, + "OcrSkillLanguage": { + "type": "string", + "enum": [ + "af", + "sq", + "anp", + "ar", + "ast", + "awa", + "az", + "bfy", + "eu", + "be", + "be-cyrl", + "be-latn", + "bho", + "bi", + "brx", + "bs", + "bra", + "br", + "bg", + "bns", + "bua", + "ca", + "ceb", + "rab", + "ch", + "hne", + "zh-Hans", + "zh-Hant", + "kw", + "co", + "crh", + "hr", + "cs", + "da", + "prs", + "dhi", + "doi", + "nl", + "en", + "myv", + "et", + "fo", + "fj", + "fil", + "fi", + "fr", + "fur", + "gag", + "gl", + "de", + "gil", + "gon", + "el", + "kl", + "gvr", + "ht", + "hlb", + "hni", + "bgc", + "haw", + "hi", + "mww", + "hoc", + "hu", + "is", + "smn", + "id", + "ia", + "iu", + "ga", + "it", + "ja", + "Jns", + "jv", + "kea", + "kac", + "xnr", + "krc", + "kaa-cyrl", + "kaa", + "csb", + "kk-cyrl", + "kk-latn", + "klr", + "kha", + "quc", + "ko", + "kfq", + "kpy", + "kos", + "kum", + "ku-arab", + "ku-latn", + "kru", + "ky", + "lkt", + "la", + "lt", + "dsb", + "smj", + "lb", + "bfz", + "ms", + "mt", + "kmj", + "gv", + "mi", + "mr", + "mn", + "cnr-cyrl", + "cnr-latn", + "nap", + "ne", + "niu", + "nog", + "sme", + "nb", + "no", + "oc", + "os", + "ps", + "fa", + "pl", + "pt", + "pa", + "ksh", + "ro", + "rm", + "ru", + "sck", + "sm", + "sa", + "sat", + "sco", + "gd", + "sr", + "sr-Cyrl", + "sr-Latn", + "xsr", + "srx", + "sms", + "sk", + "sl", + "so", + "sma", + "es", + "sw", + "sv", + "tg", + "tt", + "tet", + "thf", + "to", + "tr", + "tk", + "tyv", + "hsb", + "ur", + "ug", + "uz-arab", + "uz-cyrl", + "uz", + "vo", + "wae", + "cy", + "fy", + "yua", + "za", + "zu", + "unk" + ], + "x-ms-enum": { + "name": "OcrSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "af", + "description": "Afrikaans" + }, + { + "value": "sq", + "description": "Albanian" + }, + { + "value": "anp", + "description": "Angika (Devanagiri)" + }, + { + "value": "ar", + "description": "Arabic" + }, + { + "value": "ast", + "description": "Asturian" + }, + { + "value": "awa", + "description": "Awadhi-Hindi (Devanagiri)" + }, + { + "value": "az", + "description": "Azerbaijani (Latin)" + }, + { + "value": "bfy", + "description": "Bagheli" + }, + { + "value": "eu", + "description": "Basque" + }, + { + "value": "be", + "description": "Belarusian (Cyrillic and Latin)" + }, + { + "value": "be-cyrl", + "description": "Belarusian (Cyrillic)" + }, + { + "value": "be-latn", + "description": "Belarusian (Latin)" + }, + { + "value": "bho", + "description": "Bhojpuri-Hindi (Devanagiri)" + }, + { + "value": "bi", + "description": "Bislama" + }, + { + "value": "brx", + "description": "Bodo (Devanagiri)" + }, + { + "value": "bs", + "description": "Bosnian Latin" + }, + { + "value": "bra", + "description": "Brajbha" + }, + { + "value": "br", + "description": "Breton" + }, + { + "value": "bg", + "description": "Bulgarian" + }, + { + "value": "bns", + "description": "Bundeli" + }, + { + "value": "bua", + "description": "Buryat (Cyrillic)" + }, + { + "value": "ca", + "description": "Catalan" + }, + { + "value": "ceb", + "description": "Cebuano" + }, + { + "value": "rab", + "description": "Chamling" + }, + { + "value": "ch", + "description": "Chamorro" + }, + { + "value": "hne", + "description": "Chhattisgarhi (Devanagiri)" + }, + { + "value": "zh-Hans", + "description": "Chinese Simplified" + }, + { + "value": "zh-Hant", + "description": "Chinese Traditional" + }, + { + "value": "kw", + "description": "Cornish" + }, + { + "value": "co", + "description": "Corsican" + }, + { + "value": "crh", + "description": "Crimean Tatar (Latin)" + }, + { + "value": "hr", + "description": "Croatian" + }, + { + "value": "cs", + "description": "Czech" + }, + { + "value": "da", + "description": "Danish" + }, + { + "value": "prs", + "description": "Dari" + }, + { + "value": "dhi", + "description": "Dhimal (Devanagiri)" + }, + { + "value": "doi", + "description": "Dogri (Devanagiri)" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "myv", + "description": "Erzya (Cyrillic)" + }, + { + "value": "et", + "description": "Estonian" + }, + { + "value": "fo", + "description": "Faroese" + }, + { + "value": "fj", + "description": "Fijian" + }, + { + "value": "fil", + "description": "Filipino" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "fur", + "description": "Frulian" + }, + { + "value": "gag", + "description": "Gagauz (Latin)" + }, + { + "value": "gl", + "description": "Galician" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "gil", + "description": "Gilbertese" + }, + { + "value": "gon", + "description": "Gondi (Devanagiri)" + }, + { + "value": "el", + "description": "Greek" + }, + { + "value": "kl", + "description": "Greenlandic" + }, + { + "value": "gvr", + "description": "Gurung (Devanagiri)" + }, + { + "value": "ht", + "description": "Haitian Creole" + }, + { + "value": "hlb", + "description": "Halbi (Devanagiri)" + }, + { + "value": "hni", + "description": "Hani" + }, + { + "value": "bgc", + "description": "Haryanvi" + }, + { + "value": "haw", + "description": "Hawaiian" + }, + { + "value": "hi", + "description": "Hindi" + }, + { + "value": "mww", + "description": "Hmong Daw (Latin)" + }, + { + "value": "hoc", + "description": "Ho (Devanagiri)" + }, + { + "value": "hu", + "description": "Hungarian" + }, + { + "value": "is", + "description": "Icelandic" + }, + { + "value": "smn", + "description": "Inari Sami" + }, + { + "value": "id", + "description": "Indonesian" + }, + { + "value": "ia", + "description": "Interlingua" + }, + { + "value": "iu", + "description": "Inuktitut (Latin)" + }, + { + "value": "ga", + "description": "Irish" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "Jns", + "description": "Jaunsari (Devanagiri)" + }, + { + "value": "jv", + "description": "Javanese" + }, + { + "value": "kea", + "description": "Kabuverdianu" + }, + { + "value": "kac", + "description": "Kachin (Latin)" + }, + { + "value": "xnr", + "description": "Kangri (Devanagiri)" + }, + { + "value": "krc", + "description": "Karachay-Balkar" + }, + { + "value": "kaa-cyrl", + "description": "Kara-Kalpak (Cyrillic)" + }, + { + "value": "kaa", + "description": "Kara-Kalpak (Latin)" + }, + { + "value": "csb", + "description": "Kashubian" + }, + { + "value": "kk-cyrl", + "description": "Kazakh (Cyrillic)" + }, + { + "value": "kk-latn", + "description": "Kazakh (Latin)" + }, + { + "value": "klr", + "description": "Khaling" + }, + { + "value": "kha", + "description": "Khasi" + }, + { + "value": "quc", + "description": "K'iche'" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "kfq", + "description": "Korku" + }, + { + "value": "kpy", + "description": "Koryak" + }, + { + "value": "kos", + "description": "Kosraean" + }, + { + "value": "kum", + "description": "Kumyk (Cyrillic)" + }, + { + "value": "ku-arab", + "description": "Kurdish (Arabic)" + }, + { + "value": "ku-latn", + "description": "Kurdish (Latin)" + }, + { + "value": "kru", + "description": "Kurukh (Devanagiri)" + }, + { + "value": "ky", + "description": "Kyrgyz (Cyrillic)" + }, + { + "value": "lkt", + "description": "Lakota" + }, + { + "value": "la", + "description": "Latin" + }, + { + "value": "lt", + "description": "Lithuanian" + }, + { + "value": "dsb", + "description": "Lower Sorbian" + }, + { + "value": "smj", + "description": "Lule Sami" + }, + { + "value": "lb", + "description": "Luxembourgish" + }, + { + "value": "bfz", + "description": "Mahasu Pahari (Devanagiri)" + }, + { + "value": "ms", + "description": "Malay (Latin)" + }, + { + "value": "mt", + "description": "Maltese" + }, + { + "value": "kmj", + "description": "Malto (Devanagiri)" + }, + { + "value": "gv", + "description": "Manx" + }, + { + "value": "mi", + "description": "Maori" + }, + { + "value": "mr", + "description": "Marathi" + }, + { + "value": "mn", + "description": "Mongolian (Cyrillic)" + }, + { + "value": "cnr-cyrl", + "description": "Montenegrin (Cyrillic)" + }, + { + "value": "cnr-latn", + "description": "Montenegrin (Latin)" + }, + { + "value": "nap", + "description": "Neapolitan" + }, + { + "value": "ne", + "description": "Nepali" + }, + { + "value": "niu", + "description": "Niuean" + }, + { + "value": "nog", + "description": "Nogay" + }, + { + "value": "sme", + "description": "Northern Sami (Latin)" + }, + { + "value": "nb", + "description": "Norwegian" + }, + { + "value": "no", + "description": "Norwegian" + }, + { + "value": "oc", + "description": "Occitan" + }, + { + "value": "os", + "description": "Ossetic" + }, + { + "value": "ps", + "description": "Pashto" + }, + { + "value": "fa", + "description": "Persian" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt", + "description": "Portuguese" + }, + { + "value": "pa", + "description": "Punjabi (Arabic)" + }, + { + "value": "ksh", + "description": "Ripuarian" + }, + { + "value": "ro", + "description": "Romanian" + }, + { + "value": "rm", + "description": "Romansh" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "sck", + "description": "Sadri (Devanagiri)" + }, + { + "value": "sm", + "description": "Samoan (Latin)" + }, + { + "value": "sa", + "description": "Sanskrit (Devanagiri)" + }, + { + "value": "sat", + "description": "Santali (Devanagiri)" + }, + { + "value": "sco", + "description": "Scots" + }, + { + "value": "gd", + "description": "Scottish Gaelic" + }, + { + "value": "sr", + "description": "Serbian (Latin)" + }, + { + "value": "sr-Cyrl", + "description": "Serbian (Cyrillic)" + }, + { + "value": "sr-Latn", + "description": "Serbian (Latin)" + }, + { + "value": "xsr", + "description": "Sherpa (Devanagiri)" + }, + { + "value": "srx", + "description": "Sirmauri (Devanagiri)" + }, + { + "value": "sms", + "description": "Skolt Sami" + }, + { + "value": "sk", + "description": "Slovak" + }, + { + "value": "sl", + "description": "Slovenian" + }, + { + "value": "so", + "description": "Somali (Arabic)" + }, + { + "value": "sma", + "description": "Southern Sami" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "sw", + "description": "Swahili (Latin)" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "tg", + "description": "Tajik (Cyrillic)" + }, + { + "value": "tt", + "description": "Tatar (Latin)" + }, + { + "value": "tet", + "description": "Tetum" + }, + { + "value": "thf", + "description": "Thangmi" + }, + { + "value": "to", + "description": "Tongan" + }, + { + "value": "tr", + "description": "Turkish" + }, + { + "value": "tk", + "description": "Turkmen (Latin)" + }, + { + "value": "tyv", + "description": "Tuvan" + }, + { + "value": "hsb", + "description": "Upper Sorbian" + }, + { + "value": "ur", + "description": "Urdu" + }, + { + "value": "ug", + "description": "Uyghur (Arabic)" + }, + { + "value": "uz-arab", + "description": "Uzbek (Arabic)" + }, + { + "value": "uz-cyrl", + "description": "Uzbek (Cyrillic)" + }, + { + "value": "uz", + "description": "Uzbek (Latin)" + }, + { + "value": "vo", + "description": "Volapük" + }, + { + "value": "wae", + "description": "Walser" + }, + { + "value": "cy", + "description": "Welsh" + }, + { + "value": "fy", + "description": "Western Frisian" + }, + { + "value": "yua", + "description": "Yucatec Maya" + }, + { + "value": "za", + "description": "Zhuang" + }, + { + "value": "zu", + "description": "Zulu" + }, + { + "value": "unk", + "description": "Unknown (All)" + } + ] + }, + "description": "The language codes supported for input by OcrSkill." + }, + "OcrSkillLineEnding": { + "type": "string", + "enum": [ + "space", + "carriageReturn", + "lineFeed", + "carriageReturnLineFeed" + ], + "x-ms-enum": { + "name": "LineEnding", + "modelAsString": true, + "values": [ + { + "value": "space", + "name": "Space", + "description": "Lines are separated by a single space character." + }, + { + "value": "carriageReturn", + "name": "CarriageReturn", + "description": "Lines are separated by a carriage return ('\\r') character." + }, + { + "value": "lineFeed", + "name": "LineFeed", + "description": "Lines are separated by a single line feed ('\\n') character." + }, + { + "value": "carriageReturnLineFeed", + "name": "CarriageReturnLineFeed", + "description": "Lines are separated by a carriage return and a line feed ('\\r\\n') character." + } + ] + }, + "description": "Defines the sequence of characters to use between the lines of text recognized by the OCR skill. The default value is \"space\"." + }, + "SplitSkillLanguage": { + "type": "string", + "enum": [ + "am", + "bs", + "cs", + "da", + "de", + "en", + "es", + "et", + "fi", + "fr", + "he", + "hi", + "hr", + "hu", + "id", + "is", + "it", + "ja", + "ko", + "lv", + "nb", + "nl", + "pl", + "pt", + "pt-br", + "ru", + "sk", + "sl", + "sr", + "sv", + "tr", + "ur", + "zh" + ], + "x-ms-enum": { + "name": "SplitSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "am", + "description": "Amharic" + }, + { + "value": "bs", + "description": "Bosnian" + }, + { + "value": "cs", + "description": "Czech" + }, + { + "value": "da", + "description": "Danish" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "et", + "description": "Estonian" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "he", + "description": "Hebrew" + }, + { + "value": "hi", + "description": "Hindi" + }, + { + "value": "hr", + "description": "Croatian" + }, + { + "value": "hu", + "description": "Hungarian" + }, + { + "value": "id", + "description": "Indonesian" + }, + { + "value": "is", + "description": "Icelandic" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "lv", + "description": "Latvian" + }, + { + "value": "nb", + "description": "Norwegian" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt", + "description": "Portuguese (Portugal)" + }, + { + "value": "pt-br", + "description": "Portuguese (Brazil)" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "sk", + "description": "Slovak" + }, + { + "value": "sl", + "description": "Slovenian" + }, + { + "value": "sr", + "description": "Serbian" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "tr", + "description": "Turkish" + }, + { + "value": "ur", + "description": "Urdu" + }, + { + "value": "zh", + "description": "Chinese (Simplified)" + } + ] + }, + "description": "The language codes supported for input text by SplitSkill." + }, + "CustomEntityLookupSkillLanguage": { + "type": "string", + "enum": [ + "da", + "de", + "en", + "es", + "fi", + "fr", + "it", + "ko", + "pt" + ], + "x-ms-enum": { + "name": "CustomEntityLookupSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "da", + "description": "Danish" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "pt", + "description": "Portuguese" + } + ] + }, + "description": "The language codes supported for input text by CustomEntityLookupSkill." + }, + "EntityRecognitionSkillLanguage": { + "type": "string", + "enum": [ + "ar", + "cs", + "zh-Hans", + "zh-Hant", + "da", + "nl", + "en", + "fi", + "fr", + "de", + "el", + "hu", + "it", + "ja", + "ko", + "no", + "pl", + "pt-PT", + "pt-BR", + "ru", + "es", + "sv", + "tr" + ], + "x-ms-enum": { + "name": "EntityRecognitionSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "ar", + "description": "Arabic" + }, + { + "value": "cs", + "description": "Czech" + }, + { + "value": "zh-Hans", + "description": "Chinese-Simplified" + }, + { + "value": "zh-Hant", + "description": "Chinese-Traditional" + }, + { + "value": "da", + "description": "Danish" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "el", + "description": "Greek" + }, + { + "value": "hu", + "description": "Hungarian" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "no", + "description": "Norwegian (Bokmaal)" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt-PT", + "description": "Portuguese (Portugal)" + }, + { + "value": "pt-BR", + "description": "Portuguese (Brazil)" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "tr", + "description": "Turkish" + } + ] + }, + "description": "Deprecated. The language codes supported for input text by EntityRecognitionSkill.", + "x-az-search-deprecated": true + }, + "TextTranslationSkillLanguage": { + "type": "string", + "enum": [ + "af", + "ar", + "bn", + "bs", + "bg", + "yue", + "ca", + "zh-Hans", + "zh-Hant", + "hr", + "cs", + "da", + "nl", + "en", + "et", + "fj", + "fil", + "fi", + "fr", + "de", + "el", + "ht", + "he", + "hi", + "mww", + "hu", + "is", + "id", + "it", + "ja", + "sw", + "tlh", + "tlh-Latn", + "tlh-Piqd", + "ko", + "lv", + "lt", + "mg", + "ms", + "mt", + "nb", + "fa", + "pl", + "pt", + "pt-br", + "pt-PT", + "otq", + "ro", + "ru", + "sm", + "sr-Cyrl", + "sr-Latn", + "sk", + "sl", + "es", + "sv", + "ty", + "ta", + "te", + "th", + "to", + "tr", + "uk", + "ur", + "vi", + "cy", + "yua", + "ga", + "kn", + "mi", + "ml", + "pa" + ], + "x-ms-enum": { + "name": "TextTranslationSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "af", + "description": "Afrikaans" + }, + { + "value": "ar", + "description": "Arabic" + }, + { + "value": "bn", + "description": "Bangla" + }, + { + "value": "bs", + "description": "Bosnian (Latin)" + }, + { + "value": "bg", + "description": "Bulgarian" + }, + { + "value": "yue", + "description": "Cantonese (Traditional)" + }, + { + "value": "ca", + "description": "Catalan" + }, + { + "value": "zh-Hans", + "description": "Chinese Simplified" + }, + { + "value": "zh-Hant", + "description": "Chinese Traditional" + }, + { + "value": "hr", + "description": "Croatian" + }, + { + "value": "cs", + "description": "Czech" + }, + { + "value": "da", + "description": "Danish" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "et", + "description": "Estonian" + }, + { + "value": "fj", + "description": "Fijian" + }, + { + "value": "fil", + "description": "Filipino" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "el", + "description": "Greek" + }, + { + "value": "ht", + "description": "Haitian Creole" + }, + { + "value": "he", + "description": "Hebrew" + }, + { + "value": "hi", + "description": "Hindi" + }, + { + "value": "mww", + "description": "Hmong Daw" + }, + { + "value": "hu", + "description": "Hungarian" + }, + { + "value": "is", + "description": "Icelandic" + }, + { + "value": "id", + "description": "Indonesian" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "sw", + "description": "Kiswahili" + }, + { + "value": "tlh", + "description": "Klingon" + }, + { + "value": "tlh-Latn", + "description": "Klingon (Latin script)" + }, + { + "value": "tlh-Piqd", + "description": "Klingon (Klingon script)" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "lv", + "description": "Latvian" + }, + { + "value": "lt", + "description": "Lithuanian" + }, + { + "value": "mg", + "description": "Malagasy" + }, + { + "value": "ms", + "description": "Malay" + }, + { + "value": "mt", + "description": "Maltese" + }, + { + "value": "nb", + "description": "Norwegian" + }, + { + "value": "fa", + "description": "Persian" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "pt", + "description": "Portuguese" + }, + { + "value": "pt-br", + "description": "Portuguese (Brazil)" + }, + { + "value": "pt-PT", + "description": "Portuguese (Portugal)" + }, + { + "value": "otq", + "description": "Queretaro Otomi" + }, + { + "value": "ro", + "description": "Romanian" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "sm", + "description": "Samoan" + }, + { + "value": "sr-Cyrl", + "description": "Serbian (Cyrillic)" + }, + { + "value": "sr-Latn", + "description": "Serbian (Latin)" + }, + { + "value": "sk", + "description": "Slovak" + }, + { + "value": "sl", + "description": "Slovenian" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "ty", + "description": "Tahitian" + }, + { + "value": "ta", + "description": "Tamil" + }, + { + "value": "te", + "description": "Telugu" + }, + { + "value": "th", + "description": "Thai" + }, + { + "value": "to", + "description": "Tongan" + }, + { + "value": "tr", + "description": "Turkish" + }, + { + "value": "uk", + "description": "Ukrainian" + }, + { + "value": "ur", + "description": "Urdu" + }, + { + "value": "vi", + "description": "Vietnamese" + }, + { + "value": "cy", + "description": "Welsh" + }, + { + "value": "yua", + "description": "Yucatec Maya" + }, + { + "value": "ga", + "description": "Irish" + }, + { + "value": "kn", + "description": "Kannada" + }, + { + "value": "mi", + "description": "Maori" + }, + { + "value": "ml", + "description": "Malayalam" + }, + { + "value": "pa", + "description": "Punjabi" + } + ] + }, + "description": "The language codes supported for input text by TextTranslationSkill." + }, + "ImageAnalysisSkillLanguage": { + "type": "string", + "enum": [ + "ar", + "az", + "bg", + "bs", + "ca", + "cs", + "cy", + "da", + "de", + "el", + "en", + "es", + "et", + "eu", + "fi", + "fr", + "ga", + "gl", + "he", + "hi", + "hr", + "hu", + "id", + "it", + "ja", + "kk", + "ko", + "lt", + "lv", + "mk", + "ms", + "nb", + "nl", + "pl", + "prs", + "pt-BR", + "pt", + "pt-PT", + "ro", + "ru", + "sk", + "sl", + "sr-Cyrl", + "sr-Latn", + "sv", + "th", + "tr", + "uk", + "vi", + "zh", + "zh-Hans", + "zh-Hant" + ], + "x-ms-enum": { + "name": "ImageAnalysisSkillLanguage", + "modelAsString": true, + "values": [ + { + "value": "ar", + "description": "Arabic" + }, + { + "value": "az", + "description": "Azerbaijani" + }, + { + "value": "bg", + "description": "Bulgarian" + }, + { + "value": "bs", + "description": "Bosnian Latin" + }, + { + "value": "ca", + "description": "Catalan" + }, + { + "value": "cs", + "description": "Czech" + }, + { + "value": "cy", + "description": "Welsh" + }, + { + "value": "da", + "description": "Danish" + }, + { + "value": "de", + "description": "German" + }, + { + "value": "el", + "description": "Greek" + }, + { + "value": "en", + "description": "English" + }, + { + "value": "es", + "description": "Spanish" + }, + { + "value": "et", + "description": "Estonian" + }, + { + "value": "eu", + "description": "Basque" + }, + { + "value": "fi", + "description": "Finnish" + }, + { + "value": "fr", + "description": "French" + }, + { + "value": "ga", + "description": "Irish" + }, + { + "value": "gl", + "description": "Galician" + }, + { + "value": "he", + "description": "Hebrew" + }, + { + "value": "hi", + "description": "Hindi" + }, + { + "value": "hr", + "description": "Croatian" + }, + { + "value": "hu", + "description": "Hungarian" + }, + { + "value": "id", + "description": "Indonesian" + }, + { + "value": "it", + "description": "Italian" + }, + { + "value": "ja", + "description": "Japanese" + }, + { + "value": "kk", + "description": "Kazakh" + }, + { + "value": "ko", + "description": "Korean" + }, + { + "value": "lt", + "description": "Lithuanian" + }, + { + "value": "lv", + "description": "Latvian" + }, + { + "value": "mk", + "description": "Macedonian" + }, + { + "value": "ms", + "description": "Malay Malaysia" + }, + { + "value": "nb", + "description": "Norwegian (Bokmal)" + }, + { + "value": "nl", + "description": "Dutch" + }, + { + "value": "pl", + "description": "Polish" + }, + { + "value": "prs", + "description": "Dari" + }, + { + "value": "pt-BR", + "description": "Portuguese-Brazil" + }, + { + "value": "pt", + "description": "Portuguese-Portugal" + }, + { + "value": "pt-PT", + "description": "Portuguese-Portugal" + }, + { + "value": "ro", + "description": "Romanian" + }, + { + "value": "ru", + "description": "Russian" + }, + { + "value": "sk", + "description": "Slovak" + }, + { + "value": "sl", + "description": "Slovenian" + }, + { + "value": "sr-Cyrl", + "description": "Serbian - Cyrillic RS" + }, + { + "value": "sr-Latn", + "description": "Serbian - Latin RS" + }, + { + "value": "sv", + "description": "Swedish" + }, + { + "value": "th", + "description": "Thai" + }, + { + "value": "tr", + "description": "Turkish" + }, + { + "value": "uk", + "description": "Ukrainian" + }, + { + "value": "vi", + "description": "Vietnamese" + }, + { + "value": "zh", + "description": "Chinese Simplified" + }, + { + "value": "zh-Hans", + "description": "Chinese Simplified" + }, + { + "value": "zh-Hant", + "description": "Chinese Traditional" + } + ] + }, + "description": "The language codes supported for input by ImageAnalysisSkill." + }, + "SynonymMap": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the synonym map." + }, + "format": { + "type": "string", + "enum": [ + "solr" + ], + "x-ms-enum": { + "name": "SynonymMapFormat", + "modelAsString": false, + "values": [ + { + "value": "solr", + "name": "Solr", + "description": "Selects the SOLR format for synonym maps." + } + ] + }, + "description": "The format of the synonym map. Only the 'solr' format is currently supported." + }, + "synonyms": { + "type": "string", + "description": "A series of synonym rules in the specified synonym map format. The rules must be separated by newlines.", + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Create-Synonym-Map#SynonymMapFormat" + } + }, + "encryptionKey": { + "$ref": "#/definitions/SearchResourceEncryptionKey", + "description": "A description of an encryption key that you create in Azure Key Vault. This key is used to provide an additional level of encryption-at-rest for your data when you want full assurance that no one, not even Microsoft, can decrypt your data. Once you have encrypted your data, it will always remain encrypted. The search service will ignore attempts to set this property to null. You can change this property as needed if you want to rotate your encryption key; Your data will be unaffected. Encryption with customer-managed keys is not available for free search services, and is only available for paid services created on or after January 1, 2019.", + "externalDocs": { + "url": "https://aka.ms/azure-search-encryption-with-cmk" + }, + "x-nullable": true + }, + "@odata.etag": { + "x-ms-client-name": "ETag", + "type": "string", + "description": "The ETag of the synonym map." + } + }, + "required": [ + "name", + "format", + "synonyms" + ], + "description": "Represents a synonym map definition." + }, + "ListSynonymMapsResult": { + "properties": { + "value": { + "x-ms-client-name": "SynonymMaps", + "type": "array", + "readOnly": true, + "items": { + "$ref": "#/definitions/SynonymMap" + }, + "description": "The synonym maps in the Search service." + } + }, + "required": [ + "value" + ], + "description": "Response from a List SynonymMaps request. If successful, it includes the full definitions of all synonym maps." + }, + "SearchResourceEncryptionKey": { + "properties": { + "keyVaultKeyName": { + "x-ms-client-name": "keyName", + "type": "string", + "description": "The name of your Azure Key Vault key to be used to encrypt your data at rest." + }, + "keyVaultKeyVersion": { + "x-ms-client-name": "keyVersion", + "type": "string", + "description": "The version of your Azure Key Vault key to be used to encrypt your data at rest." + }, + "keyVaultUri": { + "x-ms-client-name": "vaultUri", + "type": "string", + "description": "The URI of your Azure Key Vault, also referred to as DNS name, that contains the key to be used to encrypt your data at rest. An example URI might be `https://my-keyvault-name.vault.azure.net`." + }, + "accessCredentials": { + "$ref": "#/definitions/AzureActiveDirectoryApplicationCredentials", + "description": "Optional Azure Active Directory credentials used for accessing your Azure Key Vault. Not required if using managed identity instead.", + "externalDocs": { + "url": "https://aka.ms/azure-search-msi" + } + } + }, + "required": [ + "keyVaultKeyName", + "keyVaultKeyVersion", + "keyVaultUri" + ], + "description": "A customer-managed encryption key in Azure Key Vault. Keys that you create and manage can be used to encrypt or decrypt data-at-rest, such as indexes and synonym maps." + }, + "AzureActiveDirectoryApplicationCredentials": { + "properties": { + "applicationId": { + "type": "string", + "description": "An AAD Application ID that was granted the required access permissions to the Azure Key Vault that is to be used when encrypting your data at rest. The Application ID should not be confused with the Object ID for your AAD Application." + }, + "applicationSecret": { + "type": "string", + "description": "The authentication key of the specified AAD application." + } + }, + "required": [ + "applicationId" + ], + "description": "Credentials of a registered application created for your search service, used for authenticated access to the encryption keys stored in Azure Key Vault." + }, + "ServiceStatistics": { + "properties": { + "counters": { + "$ref": "#/definitions/ServiceCounters", + "description": "Service level resource counters." + }, + "limits": { + "$ref": "#/definitions/ServiceLimits", + "description": "Service level general limits." + } + }, + "required": [ + "counters", + "limits" + ], + "description": "Response from a get service statistics request. If successful, it includes service level counters and limits." + }, + "ServiceCounters": { + "properties": { + "documentCount": { + "x-ms-client-name": "documentCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of documents across all indexes in the service." + }, + "indexesCount": { + "x-ms-client-name": "indexCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of indexes." + }, + "indexersCount": { + "x-ms-client-name": "indexerCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of indexers." + }, + "dataSourcesCount": { + "x-ms-client-name": "dataSourceCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of data sources." + }, + "storageSize": { + "x-ms-client-name": "storageSizeCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total size of used storage in bytes." + }, + "synonymMaps": { + "x-ms-client-name": "synonymMapCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of synonym maps." + }, + "skillsetCount": { + "x-ms-client-name": "skillsetCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total number of skillsets." + }, + "vectorIndexSize": { + "x-ms-client-name": "vectorIndexSizeCounter", + "$ref": "#/definitions/ResourceCounter", + "description": "Total memory consumption of all vector indexes within the service, in bytes." + } + }, + "required": [ + "documentCount", + "indexesCount", + "indexersCount", + "dataSourcesCount", + "storageSize", + "synonymMaps", + "skillsetCount", + "vectorIndexSize" + ], + "description": "Represents service-level resource counters and quotas." + }, + "ServiceLimits": { + "properties": { + "maxFieldsPerIndex": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The maximum allowed fields per index." + }, + "maxFieldNestingDepthPerIndex": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The maximum depth which you can nest sub-fields in an index, including the top-level complex field. For example, a/b/c has a nesting depth of 3." + }, + "maxComplexCollectionFieldsPerIndex": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The maximum number of fields of type Collection(Edm.ComplexType) allowed in an index." + }, + "maxComplexObjectsInCollectionsPerDocument": { + "type": "integer", + "format": "int32", + "x-nullable": true, + "description": "The maximum number of objects in complex collections allowed per document." + }, + "maxStoragePerIndex": { + "x-ms-client-name": "maxStoragePerIndexInBytes", + "type": "integer", + "format": "int64", + "x-nullable": true, + "description": "The maximum amount of storage in bytes allowed per index." + } + }, + "description": "Represents various service level limits." + }, + "ResourceCounter": { + "properties": { + "usage": { + "type": "integer", + "format": "int64", + "x-nullable": false, + "description": "The resource usage amount." + }, + "quota": { + "type": "integer", + "format": "int64", + "x-nullable": true, + "description": "The resource amount quota." + } + }, + "required": [ + "usage" + ], + "description": "Represents a resource's usage and quota." + }, + "SemanticSettings": { + "x-ms-client-name": "SemanticSearch", + "properties": { + "defaultConfiguration": { + "x-ms-client-name": "defaultConfigurationName", + "type": "string", + "description": "Allows you to set the name of a default semantic configuration in your index, making it optional to pass it on as a query parameter every time." + }, + "configurations": { + "type": "array", + "items": { + "$ref": "#/definitions/SemanticConfiguration" + }, + "description": "The semantic configurations for the index." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/semantic-search-overview" + }, + "description": "Defines parameters for a search index that influence semantic capabilities." + }, + "SemanticConfiguration": { + "properties": { + "name": { + "externalDocs": { + "url": "https://learn.microsoft.com/rest/api/searchservice/Naming-rules" + }, + "type": "string", + "description": "The name of the semantic configuration.", + "x-nullable": false + }, + "prioritizedFields": { + "$ref": "#/definitions/PrioritizedFields", + "x-nullable": false, + "description": "Describes the title, content, and keyword fields to be used for semantic ranking, captions, highlights, and answers. At least one of the three sub properties (titleField, prioritizedKeywordsFields and prioritizedContentFields) need to be set." + } + }, + "required": [ + "name", + "prioritizedFields" + ], + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/semantic-search-overview" + }, + "description": "Defines a specific configuration to be used in the context of semantic capabilities." + }, + "PrioritizedFields": { + "x-ms-client-name": "SemanticPrioritizedFields", + "properties": { + "titleField": { + "$ref": "#/definitions/SemanticField", + "description": "Defines the title field to be used for semantic ranking, captions, highlights, and answers. If you don't have a title field in your index, leave this blank." + }, + "prioritizedContentFields": { + "x-ms-client-name": "contentFields", + "type": "array", + "items": { + "$ref": "#/definitions/SemanticField" + }, + "description": "Defines the content fields to be used for semantic ranking, captions, highlights, and answers. For the best result, the selected fields should contain text in natural language form. The order of the fields in the array represents their priority. Fields with lower priority may get truncated if the content is long." + }, + "prioritizedKeywordsFields": { + "x-ms-client-name": "keywordsFields", + "type": "array", + "items": { + "$ref": "#/definitions/SemanticField" + }, + "description": "Defines the keyword fields to be used for semantic ranking, captions, highlights, and answers. For the best result, the selected fields should contain a list of keywords. The order of the fields in the array represents their priority. Fields with lower priority may get truncated if the content is long." + } + }, + "externalDocs": { + "url": "https://learn.microsoft.com/azure/search/semantic-search-overview" + }, + "description": "Describes the title, content, and keywords fields to be used for semantic ranking, captions, highlights, and answers." + }, + "SemanticField": { + "properties": { + "fieldName": { + "type": "string", + "description": "", + "x-nullable": false + } + }, + "required": [ + "fieldName" + ], + "description": "A field that is used as part of the semantic configuration." + } + }, + "parameters": { + "ApiVersionParameter": { + "name": "api-version", + "in": "query", + "required": true, + "type": "string", + "description": "Client Api Version." + }, + "ClientRequestIdParameter": { + "name": "x-ms-client-request-id", + "in": "header", + "required": false, + "type": "string", + "format": "uuid", + "description": "The tracking ID sent with the request to help with debugging.", + "x-ms-client-request-id": true, + "x-ms-parameter-grouping": { + "name": "request-options" + }, + "x-ms-parameter-location": "method" + }, + "IfMatchParameter": { + "name": "If-Match", + "in": "header", + "required": false, + "type": "string", + "description": "Defines the If-Match condition. The operation will be performed only if the ETag on the server matches this value.", + "x-ms-parameter-location": "method" + }, + "IfNoneMatchParameter": { + "name": "If-None-Match", + "in": "header", + "required": false, + "type": "string", + "description": "Defines the If-None-Match condition. The operation will be performed only if the ETag on the server does not match this value.", + "x-ms-parameter-location": "method" + }, + "PreferHeaderParameter": { + "name": "Prefer", + "in": "header", + "required": true, + "type": "string", + "enum": [ + "return=representation" + ], + "description": "For HTTP PUT requests, instructs the service to return the created/updated resource on success.", + "x-ms-parameter-location": "method" + }, + "EndpointParameter": { + "name": "endpoint", + "in": "path", + "required": true, + "type": "string", + "x-ms-skip-url-encoding": true, + "description": "The endpoint URL of the search service.", + "x-ms-parameter-location": "client" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding-all.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding-all.bicep new file mode 100644 index 000000000..11ae483c9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding-all.bicep @@ -0,0 +1,364 @@ +// ------------------ +// PARAMETERS +// ------------------ + +@description('API Center Service Name') +param apiCenterServiceName string + +@description('API Center Workspace Name') +param apiCenterWorkspaceName string = 'default' + +@description('API Center MCP Environment Name') +param apiCenterMCPEnvironment string = 'mcp-dev' + +@description('API Center API Environment Name') +param apiCenterAPIEnvironment string = 'api-dev' + +@description('Whether MCP sample APIs are deployed') +param isMCPSampleDeployed bool = false + +@description('APIM Gateway URL') +param apimGatewayUrl string + +@description('Enable Azure AI Search API Center registration') +param enableAzureAISearch bool = false + +@description('Enable AI Model Inference API Center registration') +param enableAIModelInference bool = true + +@description('Enable OpenAI Realtime API Center registration') +param enableOpenAIRealtime bool = true + +@description('Enable Document Intelligence API Center registration') +param enableDocumentIntelligence bool = true + +// ------------------ +// MCP API Center Onboarding +// ------------------ + +var weatherMCPCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'Developer Tools'] + Vendor: 'Internal' + Type: 'AI Gateway' + Icon: 'https://cdn-icons-png.flaticon.com/512/1163/1163661.png' +} +module weatherMCPApiCenter './api-center-onboarding.bicep' = if (isMCPSampleDeployed) { + name: 'weather-mcp-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterMCPEnvironment + apiName: 'weather-mcp' + apiDisplayName: 'Weather MCP Development' + apiDescription: 'MCP server for weather data operations for given location (Development)' + apiKind: 'mcp' + lifecycleStage: 'development' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'weather-mcp-definition' + definitionDisplayName: 'Weather MCP Definition' + definitionDescription: 'Weather MCP Definition for version 1.0.0' + deploymentName: 'weather-mcp-deployment' + deploymentDisplayName: 'Weather MCP Deployment' + deploymentDescription: 'Weather MCP Deployment for version 1.0.0 and environment Development' + gatewayUrl: apimGatewayUrl + apiPath: 'weather-mcp' + customProperties: weatherMCPCustomProperties + documentationUrl: 'https://example.com/weather-mcp-docs' + } +} + +var microsoftLearnMCPProperties = { + Visibility: true + Categories: ['Developer Tools', 'Productivity'] + Vendor: 'Microsoft' + Type: 'Remote' + Icon: 'https://learn.microsoft.com/media/logos/logo-ms-social.png' +} +module microsoftLearnMCPApiCenter './api-center-onboarding.bicep' = if (isMCPSampleDeployed) { + name: 'ms-learn-mcp-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterMCPEnvironment + apiName: 'ms-learn-mcp' + apiDisplayName: 'Microsoft Learn MCP' + apiDescription: 'Microsoft Learn MCP Server' + apiKind: 'mcp' + lifecycleStage: 'development' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'ms-learn-mcp-definition' + definitionDisplayName: 'Microsoft Learn MCP Definition' + definitionDescription: 'Microsoft Learn MCP Definition for version 1.0.0' + deploymentName: 'ms-learn-mcp-deployment' + deploymentDisplayName: 'Microsoft Learn MCP Deployment' + deploymentDescription: 'Microsoft Learn MCP Deployment for version 1.0.0 and environment development' + gatewayUrl: apimGatewayUrl + apiPath: 'ms-learn-mcp' + customProperties: microsoftLearnMCPProperties + documentationUrl: 'https://learn.microsoft.com/mcp' + } +} + +// ------------------ +// API Center Onboarding - Regular APIs +// ------------------ + +var openAIApiCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'OpenAI'] + Vendor: 'Microsoft' + Type: 'AI Service' + Icon: 'https://cdn.openai.com/API/logo-assets/openai-logo.svg' +} +module openAIApiCenter './api-center-onboarding.bicep' = { + name: 'openai-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'azure-openai-service-api' + apiDisplayName: 'Azure OpenAI API' + apiDescription: 'Azure OpenAI API for accessing GPT models and other AI capabilities' + apiKind: 'REST' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'azure-openai-service-api-definition' + definitionDisplayName: 'Azure OpenAI API Definition' + definitionDescription: 'Azure OpenAI API Definition for version 1.0.0' + deploymentName: 'azure-openai-service-api-deployment' + deploymentDisplayName: 'Azure OpenAI API Deployment' + deploymentDescription: 'Azure OpenAI API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'openai' + customProperties: openAIApiCustomProperties + documentationUrl: 'https://learn.microsoft.com/azure/ai-services/openai/' + } +} + +var aiSearchCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'Search'] + Vendor: 'Microsoft' + Type: 'AI Service' + Icon: 'https://learn.microsoft.com/media/logos/logo-ms-social.png' +} +module aiSearchApiCenter './api-center-onboarding.bicep' = if (enableAzureAISearch) { + name: 'ai-search-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'azure-ai-search-index-api' + apiDisplayName: 'Azure AI Search Index API' + apiDescription: 'Azure AI Search Index Client APIs for search operations' + apiKind: 'REST' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'azure-ai-search-index-api-definition' + definitionDisplayName: 'Azure AI Search Index API Definition' + definitionDescription: 'Azure AI Search Index API Definition for version 1.0.0' + deploymentName: 'azure-ai-search-index-api-deployment' + deploymentDisplayName: 'Azure AI Search Index API Deployment' + deploymentDescription: 'Azure AI Search Index API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'search' + customProperties: aiSearchCustomProperties + documentationUrl: 'https://learn.microsoft.com/azure/search/' + } +} + +var aiModelInferenceCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'Model Inference'] + Vendor: 'Microsoft' + Type: 'AI Service' + Icon: 'https://learn.microsoft.com/media/logos/logo-ms-social.png' +} +module aiModelInferenceApiCenter './api-center-onboarding.bicep' = if (enableAIModelInference) { + name: 'ai-model-inference-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'ai-model-inference-api' + apiDisplayName: 'AI Model Inference API' + apiDescription: 'Access to AI inference models published through Azure AI Foundry' + apiKind: 'REST' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'ai-model-inference-api-definition' + definitionDisplayName: 'AI Model Inference API Definition' + definitionDescription: 'AI Model Inference API Definition for version 1.0.0' + deploymentName: 'ai-model-inference-api-deployment' + deploymentDisplayName: 'AI Model Inference API Deployment' + deploymentDescription: 'AI Model Inference API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'models' + customProperties: aiModelInferenceCustomProperties + documentationUrl: 'https://learn.microsoft.com/en-us/rest/api/aifoundry/modelinference/' + } +} + +var openAIRealtimeCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'OpenAI', 'Real-time'] + Vendor: 'Microsoft' + Type: 'AI Service' + Icon: 'https://cdn.openai.com/API/logo-assets/openai-logo.svg' +} +module openAIRealtimeApiCenter './api-center-onboarding.bicep' = if (enableOpenAIRealtime) { + name: 'openai-realtime-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'openai-realtime-ws-api' + apiDisplayName: 'Azure OpenAI Realtime API' + apiDescription: 'Access Azure OpenAI Realtime API for real-time voice and text conversion' + apiKind: 'websocket' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'openai-realtime-ws-api-definition' + definitionDisplayName: 'Azure OpenAI Realtime API Definition' + definitionDescription: 'Azure OpenAI Realtime API Definition for version 1.0.0' + deploymentName: 'openai-realtime-ws-api-deployment' + deploymentDisplayName: 'Azure OpenAI Realtime API Deployment' + deploymentDescription: 'Azure OpenAI Realtime API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'openai/realtime' + customProperties: openAIRealtimeCustomProperties + documentationUrl: 'https://learn.microsoft.com/en-us/azure/ai-foundry/openai/realtime-audio-quickstart?tabs=keyless%2Cwindows' + } +} + +var documentIntelligenceCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'Document Processing'] + Vendor: 'Microsoft' + Type: 'AI Service' + Icon: 'https://learn.microsoft.com/media/logos/logo-ms-social.png' +} +module documentIntelligenceLegacyApiCenter './api-center-onboarding.bicep' = if (enableDocumentIntelligence) { + name: 'doc-intel-legacy-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'document-intelligence-api-legacy' + apiDisplayName: 'Document Intelligence API (Legacy)' + apiDescription: 'Uses /formrecognizer path. Extracts content, layout, and structured data from documents' + apiKind: 'REST' + lifecycleStage: 'deprecated' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'document-intelligence-api-legacy-definition' + definitionDisplayName: 'Document Intelligence API (Legacy) Definition' + definitionDescription: 'Document Intelligence API (Legacy) Definition for version 1.0.0' + deploymentName: 'document-intelligence-api-legacy-deployment' + deploymentDisplayName: 'Document Intelligence API (Legacy) Deployment' + deploymentDescription: 'Document Intelligence API (Legacy) Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'formrecognizer' + customProperties: documentIntelligenceCustomProperties + documentationUrl: 'https://learn.microsoft.com/azure/ai-services/document-intelligence/' + } +} + +module documentIntelligenceApiCenter './api-center-onboarding.bicep' = if (enableDocumentIntelligence) { + name: 'doc-intel-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'document-intelligence-api' + apiDisplayName: 'Document Intelligence API' + apiDescription: 'Uses /documentintelligence path. Extracts content, layout, and structured data from documents' + apiKind: 'REST' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'document-intelligence-api-definition' + definitionDisplayName: 'Document Intelligence API Definition' + definitionDescription: 'Document Intelligence API Definition for version 1.0.0' + deploymentName: 'document-intelligence-api-deployment' + deploymentDisplayName: 'Document Intelligence API Deployment' + deploymentDescription: 'Document Intelligence API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'documentintelligence' + customProperties: documentIntelligenceCustomProperties + documentationUrl: 'https://learn.microsoft.com/azure/ai-services/document-intelligence/' + } +} + +var universalLLMCustomProperties = { + Visibility: true + Categories: ['AI/ML', 'LLM', 'Multi-Provider'] + Vendor: 'Internal' + Type: 'AI Gateway' + Icon: 'https://learn.microsoft.com/media/logos/logo-ms-social.png' +} +module universalLLMApiCenter './api-center-onboarding.bicep' = { + name: 'universal-llm-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'universal-llm-api' + apiDisplayName: 'Universal LLM API' + apiDescription: 'Universal LLM API to route requests to different LLM providers including Azure OpenAI and AI Foundry' + apiKind: 'REST' + lifecycleStage: 'production' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'universal-llm-api-definition' + definitionDisplayName: 'Universal LLM API Definition' + definitionDescription: 'Universal LLM API Definition for version 1.0.0' + deploymentName: 'universal-llm-api-deployment' + deploymentDisplayName: 'Universal LLM API Deployment' + deploymentDescription: 'Universal LLM API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'llm' + customProperties: universalLLMCustomProperties + documentationUrl: 'https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator' + } +} + +var weatherAPICustomProperties = { + Visibility: true + Categories: ['Sample', 'Weather'] + Vendor: 'Internal' + Type: 'Sample API' + Icon: 'https://cdn-icons-png.flaticon.com/512/1163/1163661.png' +} +module weatherAPIApiCenter './api-center-onboarding.bicep' = if (isMCPSampleDeployed) { + name: 'weather-api-center' + params: { + apicServiceName: apiCenterServiceName + apicWorkspaceName: apiCenterWorkspaceName + environmentName: apiCenterAPIEnvironment + apiName: 'weather-api' + apiDisplayName: 'Weather API' + apiDescription: 'Weather API for getting dynamic weather information for a given location' + apiKind: 'REST' + lifecycleStage: 'development' + versionName: '1-0-0' + versionDisplayName: '1.0.0' + definitionName: 'weather-api-definition' + definitionDisplayName: 'Weather API Definition' + definitionDescription: 'Weather API Definition for version 1.0.0' + deploymentName: 'weather-api-deployment' + deploymentDisplayName: 'Weather API Deployment' + deploymentDescription: 'Weather API Deployment for version 1.0.0' + gatewayUrl: apimGatewayUrl + apiPath: 'weather' + customProperties: weatherAPICustomProperties + documentationUrl: 'https://example.com/weather-api-docs' + } +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding.bicep new file mode 100644 index 000000000..7c9c5e152 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api-center-onboarding.bicep @@ -0,0 +1,163 @@ +// ------------------ +// PARAMETERS +// ------------------ + +@description('API Center Service Name') +param apicServiceName string + +@description('API Center Workspace Name') +param apicWorkspaceName string = 'default' + +@description('API Center Environment Name (e.g., dev, staging, production)') +param environmentName string + +@description('API/MCP Name (used as resource identifier)') +param apiName string + +@description('API/MCP Display Name') +param apiDisplayName string + +@description('API/MCP Description') +param apiDescription string + +@description('API Type: api, graphql, grpc, soap, websocket, webhook, mcp') +param apiKind string = 'rest' + +@description('Lifecycle Stage: design, development, testing, preview, production, deprecated, retired') +param lifecycleStage string = 'development' + +@description('API Version Name (e.g., 1-0-0, v1, v2)') +param versionName string = '1-0-0' + +@description('API Version Display Name (e.g., 1.0.0, v1, v2)') +param versionDisplayName string = '1.0.0' + +@description('API Definition Name') +param definitionName string = '${apiName}-definition' + +@description('API Definition Display Name') +param definitionDisplayName string = '${apiDisplayName} Definition' + +@description('API Definition Description') +param definitionDescription string = '${apiDisplayName} Definition for version ${versionName}' + +@description('API Deployment Name') +param deploymentName string = '${apiName}-deployment' + +@description('API Deployment Display Name') +param deploymentDisplayName string = '${apiDisplayName} Deployment' + +@description('API Deployment Description') +param deploymentDescription string = '${apiDisplayName} Deployment for version ${versionName} and environment ${environmentName}' + +@description('API Gateway URL (e.g., https://apim-gateway.azure-api.net)') +param gatewayUrl string + +@description('API Path (e.g., openai, weather-mcp, ms-learn-mcp)') +param apiPath string + +@description('Custom Properties for API Center metadata') +param customProperties object = {} + +@description('External Documentation URL') +param documentationUrl string = '' + +@description('API Contacts') +param contacts array = [] + +@description('Deployment State: active, inactive') +param deploymentState string = 'active' + +// ------------------ +// RESOURCES +// ------------------ + +resource apiCenterService 'Microsoft.ApiCenter/services@2024-06-01-preview' existing = { + name: apicServiceName +} + +resource apiCenterWorkspace 'Microsoft.ApiCenter/services/workspaces@2024-06-01-preview' existing = { + parent: apiCenterService + name: apicWorkspaceName +} + +// Register API in API Center +resource apiCenterAPI 'Microsoft.ApiCenter/services/workspaces/apis@2024-06-01-preview' = { + parent: apiCenterWorkspace + name: apiName + properties: { + title: apiDisplayName + kind: apiKind + externalDocumentation: empty(documentationUrl) ? [] : [ + { + description: apiDescription + title: apiDisplayName + url: documentationUrl + } + ] + contacts: contacts + customProperties: customProperties + summary: apiDescription + description: apiDescription + } +} + +// Register API Version +resource apiVersion 'Microsoft.ApiCenter/services/workspaces/apis/versions@2024-06-01-preview' = { + parent: apiCenterAPI + name: versionName + properties: { + title: versionDisplayName + lifecycleStage: lifecycleStage + } +} + +// Register API Definition +resource apiDefinition 'Microsoft.ApiCenter/services/workspaces/apis/versions/definitions@2024-06-01-preview' = { + parent: apiVersion + name: definitionName + properties: { + description: definitionDescription + title: definitionDisplayName + } +} + +// Register API Deployment +resource apiDeployment 'Microsoft.ApiCenter/services/workspaces/apis/deployments@2024-06-01-preview' = { + parent: apiCenterAPI + name: deploymentName + properties: { + description: deploymentDescription + title: deploymentDisplayName + environmentId: '/workspaces/default/environments/${environmentName}' + definitionId: '/workspaces/${apicWorkspaceName}/apis/${apiName}/versions/${versionName}/definitions/${definitionName}' + state: deploymentState + server: { + runtimeUri: [ + '${gatewayUrl}/${apiPath}' + ] + } + } + dependsOn: [ + apiDefinition + ] +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('API Center API Name') +output apiCenterApiName string = apiCenterAPI.name + +@description('API Center API Resource ID') +output apiCenterApiId string = apiCenterAPI.id + +@description('API Center API Version Name') +output apiCenterApiVersionName string = apiVersion.name + +@description('API Center API Definition Name') +output apiCenterApiDefinitionName string = apiDefinition.name + +@description('API Center API Deployment Name') +output apiCenterApiDeploymentName string = apiDeployment.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api.bicep new file mode 100644 index 000000000..166a62669 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/api.bicep @@ -0,0 +1,226 @@ +@description('The name of the API') +@minLength(1) +@maxLength(63) +param apiName string + +@description('The display name of the API') +@minLength(1) +@maxLength(63) +param apiDispalyName string + +@description('The contents of the OpenAPI definition') +@minLength(1) +param openApiSpecification string + +@description('The XML Policy document for the API') +@minLength(1) +param policyDocument string + +@description('The name of the API Management service to deploy the API to.') +@minLength(1) +param serviceName string + +@description('The API description (if blank, use the name of the API)') +param apiDescription string = '' + +@description('The relative path for the API (if different to the API name)') +param path string = '' + +@description('The (optional) service URL') +param serviceUrl string = '' + +@description('Set to true if a subscription is required') +param subscriptionRequired bool = true + +@description('API Revision number. Default is 1') +param apiRevision string = '1' + +@description('Ability to override the subscription key name. Default is Ocp-Apim-Subscription-Key') +param subscriptionKeyName string = '' + +param enableAPIDeployment bool = true + +param apimLoggerId string = 'azuremonitor' + +param enableAPIDiagnostics bool + +// Assume the content format is JSON format if the ending is .json - otherwise, it's YAML +var contentFormat = startsWith(openApiSpecification, '{') ? 'openapi+json' : 'openapi' + +@description('The type of the API') +@allowed([ + 'http' + 'soap' + 'graphql' + 'websocket' +]) +param apiType string = 'http' + +@description('The protocols supported by the API') +@allowed([ + 'http' + 'https' + 'ws' + 'wss' +]) +param apiProtocols array = [ + 'https' +] + +resource apimService 'Microsoft.ApiManagement/service@2022-08-01' existing = { + name: serviceName +} + +var isWebSotcketAPI = contains(apiProtocols, 'ws') || contains(apiProtocols, 'wss') + +var logSettings = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens' , 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} + +resource apiDefinition 'Microsoft.ApiManagement/service/apis@2022-08-01' = if(enableAPIDeployment && !isWebSotcketAPI) { + name: apiName + parent: apimService + properties: { + path: (path == '') ? apiName : path + apiRevision: apiRevision + description: (apiDescription == '') ? apiName : apiDescription + displayName: apiDispalyName + format: (openApiSpecification != 'NA') ? contentFormat : null + value: (openApiSpecification != 'NA') ? openApiSpecification : null + subscriptionRequired: subscriptionRequired + subscriptionKeyParameterNames: { + header: empty(subscriptionKeyName) ? 'Ocp-Apim-Subscription-Key' : subscriptionKeyName + } + type: apiType + protocols: apiProtocols + serviceUrl: (serviceUrl == '') ? 'https://to-be-replaced-by-policy' : serviceUrl + } +} + + +// If the API is a WebSocket API, we need to create it differently (both the api and policy) +resource apiDefinitionWebSocket 'Microsoft.ApiManagement/service/apis@2022-08-01' = if(enableAPIDeployment && isWebSotcketAPI) { + name: apiName + parent: apimService + properties: { + path: (path == '') ? apiName : path + apiRevision: apiRevision + description: (apiDescription == '') ? apiName : apiDescription + displayName: apiDispalyName + subscriptionRequired: subscriptionRequired + subscriptionKeyParameterNames: { + header: empty(subscriptionKeyName) ? 'Ocp-Apim-Subscription-Key' : subscriptionKeyName + } + type: apiType + protocols: apiProtocols + serviceUrl: (serviceUrl == '') ? 'https://to-be-replaced-by-policy' : serviceUrl + } +} + +resource apiDefinitionWebSocketHandshake 'Microsoft.ApiManagement/service/apis/operations@2024-06-01-preview' existing = if(enableAPIDeployment && isWebSotcketAPI && policyDocument != 'NA') { + name: 'onHandshake' + parent: apiDefinitionWebSocket +} + +// https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/apis/policies +resource apiDefinitionWebSocketHandshakePolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2024-06-01-preview' = if(enableAPIDeployment && isWebSotcketAPI && policyDocument != 'NA') { + name: 'policy' + parent: apiDefinitionWebSocketHandshake + properties: { + format: 'rawxml' + value: policyDocument + } +} + + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2022-08-01' = if(enableAPIDeployment && policyDocument != 'NA' && !isWebSotcketAPI) { + name: 'policy' + parent: apiDefinition + properties: { + format: 'rawxml' + value: policyDocument + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2024-06-01-preview' = if(enableAPIDiagnostics && enableAPIDeployment && !isWebSotcketAPI) { + parent: apiDefinition + name: 'azuremonitor' + properties: { + alwaysLog: 'allErrors' + verbosity: 'Information' + logClientIp: true + loggerId: resourceId(resourceGroup().name, 'Microsoft.ApiManagement/service/loggers', apimService.name, apimLoggerId) + sampling: { + samplingType: 'fixed' + percentage: json('100') + } + frontend: { + request: { + headers: [] + body: { + bytes: 0 + } + } + response: { + headers: [] + body: { + bytes: 0 + } + } + } + backend: { + request: { + headers: [] + body: { + bytes: 0 + } + } + response: { + headers: [] + body: { + bytes: 0 + } + } + } + largeLanguageModel: { + logs: 'enabled' + requests: { + messages: 'all' + maxSizeInBytes: 262144 + } + responses: { + messages: 'all' + maxSizeInBytes: 262144 + } + } + } +} + +resource apiDiagnosticsAppInsights 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = if (enableAPIDiagnostics && enableAPIDeployment && !isWebSotcketAPI) { + name: 'applicationinsights' + parent: apiDefinition + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: resourceId(resourceGroup().name, 'Microsoft.ApiManagement/service/loggers', apimService.name, 'appinsights-logger') + metrics: true + verbosity: 'Information' + sampling: { + samplingType: 'fixed' + percentage: 100 + } + frontend: { + request: logSettings + response: logSettings + } + backend: { + request: logSettings + response: logSettings + } + } +} + +output id string = (enableAPIDeployment) ? apiDefinition.id : '' +output path string = (enableAPIDeployment) ? apiDefinition.properties.path : '' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/apim.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/apim.bicep new file mode 100644 index 000000000..e3bbf507a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/apim.bicep @@ -0,0 +1,938 @@ +param name string +param location string = resourceGroup().location +param tags object = {} +param entraAuth bool = false + +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@minLength(1) +param publisherName string = 'n/a' + +param sku string = 'Developer' +var isV2SKU = sku == 'StandardV2' || sku == 'PremiumV2' +param skuCount int = 1 +param applicationInsightsName string + +param managedIdentityName string +param clientAppId string = ' ' +param tenantId string = tenant().tenantId +param audience string = 'https://cognitiveservices.azure.com/.default' +param eventHubName string +param eventHubEndpoint string + +param eventHubPIIName string +param eventHubPIIEndpoint string + +param enableAzureAISearch bool = false +param aiSearchInstances array + +param enableAIModelInference bool = true + +param enableOpenAIRealtime bool = true + +param enableDocumentIntelligence bool = false + +param enablePIIAnonymization bool = true + +param contentSafetyServiceUrl string +param aiLanguageServiceUrl string + + +// Networking +param apimNetworkType string = 'External' +param apimSubnetId string + +param apimV2PrivateDnsZoneName string = 'privatelink.azure-api.net' +param apimV2PrivateEndpointName string +param dnsZoneRG string = '' +param dnsSubscriptionId string = '' +param privateEndpointSubnetId string +param usePrivateEndpoint bool = false +param apimV2PublicNetworkAccess bool = true + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubscriptionId) +param dnsZoneResourceId string = '' + + + +// MCP Samples (Weather API, Weather MCP, MS Learn MCP) +param isMCPSampleDeployed bool = false + +/** + * LLM Backend Configuration + * This parameter defines all LLM backends and their supported models for dynamic routing. + * Each backend can be: + * - AI Foundry: Deployed Azure AI Foundry project with model deployments + * - Azure OpenAI: Azure OpenAI service endpoints + * - Other LLM providers: External LLM endpoints with appropriate authentication + * + * Structure: + * - backendId: Unique identifier for the backend (used in APIM backend resource name) + * - backendType: Type of backend ('ai-foundry', 'azure-openai', 'external') + * - endpoint: Base URL for the backend service + * - authScheme: Authentication method ('managedIdentity', 'apiKey', 'token') + * - supportedModels: Array of model objects with: + * - name: Model name (required) + * - sku: (Optional) SKU name for deployment (default: 'Standard') + * - capacity: (Optional) Capacity/TPM quota (default: 100) + * - modelFormat: (Optional) Model format identifier (default: 'OpenAI') + * - modelVersion: (Optional) Version of the model (default: '1') + * - retirementDate: (Optional) Retirement date in YYYY-MM-DD format + * - apiVersion: (Optional) API version for OpenAI-type requests (default: '2024-02-15-preview') + * - timeout: (Optional) Request timeout in seconds (default: 120) + * - inferenceApiVersion: (Optional) API version for inference-type requests + * - priority: (Optional) Priority for load balancing (1-5, default 1) + * - weight: (Optional) Weight for load balancing (1-1000, default 1) + */ +@description('Configuration array for LLM backends supporting multiple providers and models') +param llmBackendConfig array = [] + +@description('Enable APIM cache configuration backed by Azure Managed Redis') +param enableRedisCache bool = false + +@secure() +@description('Runtime connection string to the Azure Managed Redis instance (used for APIM caches).') +param redisCacheConnectionString string = '' + +@description('Resource ID of the Azure Managed Redis instance (optional, used for traceability)') +param redisCacheResourceId string = '' + +@description('APIM cache entity name for the Redis-backed cache') +param apimRedisCacheName string = 'redis-cache' + +@description('Azure Monitor diagnostic log settings for inference APIs (frontend/backend request/response headers & body bytes, and LLM log settings).') +param azureMonitorLogSettings object = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +@description('Application Insights diagnostic log settings for inference APIs (headers to capture and body bytes).') +param appInsightsLogSettings object = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} + +@description('Enable an APIM backend that targets the AI Foundry embeddings endpoint') +param enableEmbeddingsBackend bool = false + +@description('Enable the Unified AI Wildcard API (3rd API alongside Azure OpenAI and Universal LLM)') +param enableUnifiedAiApi bool = true + +@description('Enable JWT authentication support across all APIs (creates JWT named values and security-handler fragment)') +param enableJwtAuth bool = false + +@description('JWT Tenant ID (required when enableJwtAuth is true and not using Entra module)') +param jwtTenantId string = '' + +@description('JWT App Registration Client ID (required when enableJwtAuth is true and not using Entra module)') +param jwtAppRegistrationId string = '' + +@description('URL for the AI Foundry embeddings endpoint (should be /models/embeddings on the primary Foundry resource)') +param embeddingsBackendUrl string = '' + +@description('APIM backend ID for the embeddings backend') +param embeddingsBackendId string = 'foundry-embeddings' + +var apimPublicNetworkAccess = apimV2PublicNetworkAccess ? 'Enabled' : 'Disabled' + +var openAiApiUamiNamedValue = 'uami-client-id' +var openAiApiEntraNamedValue = 'entra-auth' +var openAiApiClientNamedValue = 'client-id' +var openAiApiTenantNamedValue = 'tenant-id' +var openAiApiAudienceNamedValue = 'audience' + +var apiManagementMinApiVersion = '2021-08-01' +var apiManagementMinApiVersionV2 = '2024-05-01' + +// Add this variable near the top with other variables +// var apimZones = sku == 'Premium' && skuCount > 1 ? ['1','2','3'] : [] +// Replace the existing apimZones variable +var apimZones = (sku == 'Premium' && skuCount > 1) ? (skuCount == 2 ? ['1','2'] : ['1','2','3']) : [] + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: managedIdentityName +} + +resource apimService 'Microsoft.ApiManagement/service@2024-05-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + virtualNetworkType: isV2SKU ? 'External' : apimNetworkType + publicNetworkAccess: isV2SKU ? apimPublicNetworkAccess : 'Enabled' + virtualNetworkConfiguration: apimNetworkType != 'None' || isV2SKU ? { + subnetResourceId: apimSubnetId + } : null + apiVersionConstraint: { + minApiVersion: isV2SKU? apiManagementMinApiVersionV2 : apiManagementMinApiVersion + } + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } + zones: apimZones +} + +module privateEndpoint '../networking/private-endpoint.bicep' = if (isV2SKU && usePrivateEndpoint) { + name: '${name}-pe' + params: { + groupIds: [ + 'Gateway' + ] + dnsZoneName: apimV2PrivateDnsZoneName + name: apimV2PrivateEndpointName + privateLinkServiceId: apimService.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: privateEndpointSubnetId + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + } +} + +module apimAiSearchIndexApi './api.bicep' = if (enableAzureAISearch) { + name: 'azure-ai-search-index-api' + params: { + serviceName: apimService.name + apiName: 'azure-ai-search-index-api' + path: 'search' + apiRevision: '1' + apiDispalyName: 'Azure AI Search Index API (index services)' + subscriptionRequired: entraAuth ? false:true + subscriptionKeyName: 'api-key' + openApiSpecification: loadTextContent('./ai-search-api/ai-search-index-2024-07-01-api-spec.json') + apiDescription: 'Azure AI Search Index Client APIs' + policyDocument: loadTextContent('./policies/ai-search-index-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +module apimOpenAIRealTimetApi './api.bicep' = if (enableOpenAIRealtime) { + name: 'openai-realtime-ws-api' + params: { + serviceName: apimService.name + apiName: 'openai-realtime-ws-api' + path: 'openai/realtime' + apiRevision: '1' + apiDispalyName: 'Azure OpenAI Realtime API' + subscriptionRequired: entraAuth ? false : true + subscriptionKeyName: 'api-key' + openApiSpecification: 'NA' + apiDescription: 'Access Azure OpenAI Realtime API for real-time voice and text conversion.' + policyDocument: loadTextContent('./policies/openai-realtime-policy.xml') + enableAPIDeployment: true + serviceUrl: 'wss://to-be-replaced-by-policy' + apiType: 'websocket' + apiProtocols: ['wss'] + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +module apimDocumentIntelligenceLegacy './api.bicep' = if (enableDocumentIntelligence) { + name: 'document-intelligence-api-legacy' + params: { + serviceName: apimService.name + apiName: 'document-intelligence-api-legacy' + path: 'formrecognizer' + apiRevision: '1' + apiDispalyName: 'Document Intelligence API (Legacy)' + subscriptionRequired: entraAuth ? false:true + subscriptionKeyName: 'Ocp-Apim-Subscription-Key' + openApiSpecification: loadTextContent('./doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml') + apiDescription: 'Uses (/formrecognizer) url path. Extracts content, layout, and structured data from documents.' + policyDocument: loadTextContent('./policies/doc-intelligence-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +module apimDocumentIntelligence './api.bicep' = if (enableDocumentIntelligence) { + name: 'document-intelligence-api' + params: { + serviceName: apimService.name + apiName: 'document-intelligence-api' + path: 'documentintelligence' + apiRevision: '1' + apiDispalyName: 'Document Intelligence API' + subscriptionRequired: entraAuth ? false:true + subscriptionKeyName: 'Ocp-Apim-Subscription-Key' + openApiSpecification: loadTextContent('./doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml') + apiDescription: 'Uses (/documentintelligence) url path. Extracts content, layout, and structured data from documents.' + policyDocument: loadTextContent('./policies/doc-intelligence-api-policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + policyFragments + ] +} + +/** + * Dynamic LLM Backend Creation + * Creates individual backends for each LLM endpoint defined in llmBackendConfig + * Supports AI Foundry, Azure OpenAI, and external LLM providers + */ +module llmBackends './llm-backends.bicep' = { + name: 'llm-backends' + params: { + apimServiceName: apimService.name + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + configureCircuitBreaker: true + tags: tags + } + dependsOn: [ + policyFragments + ] +} + +/** + * Dynamic Backend Pool Creation + * Groups backends by supported models to enable load balancing and failover + * Only creates pools for models supported by multiple backends + */ +module llmBackendPools './llm-backend-pools.bicep' = { + name: 'llm-backend-pools' + params: { + apimServiceName: apimService.name + backendDetails: llmBackends.outputs.backendDetails + tags: tags + } +} + +/** + * Dynamic LLM Policy Fragments + * Generates policy fragments with backend pool configurations for routing logic + * Updates set-backend-pools and set-backend-authorization fragments dynamically + * Also generates get-available-models fragment with model metadata + */ +module llmPolicyFragments './llm-policy-fragments.bicep' = { + name: 'llm-policy-fragments' + params: { + apimServiceName: apimService.name + policyFragmentConfig: llmBackendPools.outputs.policyFragmentConfig + managedIdentityClientId: managedIdentity.properties.clientId + llmBackendConfig: llmBackendConfig + } + dependsOn: [ + policyFragments + ] +} + +resource redisCache 'Microsoft.ApiManagement/service/caches@2024-06-01-preview' = if (enableRedisCache) { + name: apimRedisCacheName + parent: apimService + properties: { + connectionString: redisCacheConnectionString + useFromLocation: 'default' + description: 'Azure Managed Redis cache for APIM Semantic Cache' + } +} + +resource embeddingsBackend 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = if (enableEmbeddingsBackend) { + name: embeddingsBackendId + parent: apimService + properties: { + description: 'AI Foundry embeddings backend' + url: embeddingsBackendUrl + protocol: 'http' + credentials: { + managedIdentity: { + clientId: managedIdentity.properties.clientId + resource: 'https://cognitiveservices.azure.com' + } + } + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +} + +module apiUniversalLLM './inference-api.bicep' = { + name: 'universal-llm-api' + params: { + apiManagementName: apimService.name + inferenceAPIName: 'universal-llm-api' + inferenceAPIPath: '' + inferenceAPIType: 'OpenAIV1' + inferenceAPIDisplayName: 'Universal LLM API' + inferenceAPIDescription: 'Universal LLM API to route requests to different LLM providers including Azure OpenAI, AI Foundry and 3rd party models.' + allowSubscriptionKey: entraAuth ? false:true + apimLoggerId: apimAzMonitorLogger.id + policyXml: loadTextContent('./policies/universal-llm-api-policy-v2.xml') + azureMonitorLogSettings: azureMonitorLogSettings + appInsightsLogSettings: appInsightsLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + ] +} + +module apimOpenaiApi './inference-api.bicep' = { + name: 'azure-openai-api' + params: { + apiManagementName: apimService.name + inferenceAPIName: 'azure-openai-api' + inferenceAPIPath: '' + inferenceAPIType: 'AzureOpenAI' + inferenceAPIDisplayName: 'Azure OpenAI API' + inferenceAPIDescription: 'Azure OpenAI API to route requests to different LLM providers including Azure OpenAI, AI Foundry and 3rd party models.' + allowSubscriptionKey: entraAuth ? false:true + apimLoggerId: apimAzMonitorLogger.id + policyXml: loadTextContent('./policies/azure-open-ai-api-policy.xml') + azureMonitorLogSettings: azureMonitorLogSettings + appInsightsLogSettings: appInsightsLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + apiUniversalLLM + ] +} + +////// Unified AI Wildcard API ///////////// + +module apiUnifiedAI './unified-ai-api.bicep' = if (enableUnifiedAiApi) { + name: 'unified-ai-api' + params: { + apiManagementName: apimService.name + enabled: enableUnifiedAiApi + apimLoggerId: apimAzMonitorLogger.id + azureMonitorLogSettings: azureMonitorLogSettings + } + dependsOn: [ + policyFragments + llmBackends + llmBackendPools + llmPolicyFragments + apimOpenaiApi + ] +} + +////// JWT Authentication Named Values ///////////// +// These named values support JWT authentication across all APIs (Azure OpenAI, +// Universal LLM, and Unified AI). The security-handler fragment is included in all +// API policies and references these named values via {{...}} syntax. +// When enableJwtAuth is false, placeholders are used so deployment passes. +// JWT enforcement is controlled per-product via the 'jwtRequired' context variable +// set in each Access Contract's product policy. + +var jwtTenantIdValue = !empty(jwtTenantId) ? jwtTenantId : subscription().tenantId +var jwtAppRegIdValue = !empty(jwtAppRegistrationId) ? jwtAppRegistrationId : 'not-configured' + +resource jwtTenantIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'JWT-TenantId' + parent: apimService + properties: { + displayName: 'JWT-TenantId' + value: enableJwtAuth ? jwtTenantIdValue : 'not-configured' + } +} + +resource jwtAppRegistrationIdNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'JWT-AppRegistrationId' + parent: apimService + properties: { + displayName: 'JWT-AppRegistrationId' + value: enableJwtAuth ? jwtAppRegIdValue : 'not-configured' + } +} + +resource jwtIssuerNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'JWT-Issuer' + parent: apimService + properties: { + displayName: 'JWT-Issuer' + value: enableJwtAuth ? '${environment().authentication.loginEndpoint}${jwtTenantIdValue}/v2.0' : 'not-configured' + } +} + +resource jwtOpenIdConfigUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'JWT-OpenIdConfigUrl' + parent: apimService + properties: { + displayName: 'JWT-OpenIdConfigUrl' + value: enableJwtAuth ? '${environment().authentication.loginEndpoint}${jwtTenantIdValue}/v2.0/.well-known/openid-configuration' : 'not-configured' + } +} + +////// AI Foundry Integration Requirements ///////////// + +// Typed resource reference for the Universal LLM API (created by module above) +resource universalLLMApi 'Microsoft.ApiManagement/service/apis@2022-08-01' existing = { + name: 'universal-llm-api' + parent: apimService + dependsOn: [ + apiUniversalLLM + ] +} + +resource universalLlmDeploymentOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'deployments' + parent: universalLLMApi +} + +resource universalLlmDeploymentByNameOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'deployment-by-name' + parent: universalLLMApi +} + +resource universalLlmListModelsOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'listModels' + parent: universalLLMApi +} + +resource universalLlmRetrieveModelOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'retrieveModel' + parent: universalLLMApi +} + +resource openAIApi 'Microsoft.ApiManagement/service/apis@2022-08-01' existing = { + name: 'azure-openai-api' + parent: apimService + dependsOn: [ + apimOpenaiApi + ] +} + +resource openAIDeploymentOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'deployments' + parent: openAIApi +} + +resource openAIDeploymentByNameOperation 'Microsoft.ApiManagement/service/apis/operations@2022-08-01' existing = { + name: 'deployment-by-name' + parent: openAIApi +} + +resource universalLlmDeploymentOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: universalLlmDeploymentOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployments-policy.xml') + } +} + +resource universalLlmDeploymentByNameOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: universalLlmDeploymentByNameOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployment-by-name-policy.xml') + } +} + +resource universalLlmListModelsOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: universalLlmListModelsOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployments-policy.xml') + } +} + +resource universalLlmRetrieveModelOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: universalLlmRetrieveModelOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployment-by-name-policy.xml') + } +} + +resource openAIDeploymentOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: openAIDeploymentOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployments-policy.xml') + } +} + +resource openAIDeploymentByNameOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2022-08-01' = { + name: 'policy' + parent: openAIDeploymentByNameOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/universal-llm-api-deployment-by-name-policy.xml') + } +} + +//////////// End of AI Foundry Integration Requirements ///////////// + +resource aiSearchBackends 'Microsoft.ApiManagement/service/backends@2022-08-01' = [for (aiSearchInstance, i) in aiSearchInstances: if(enableAzureAISearch) { + name: aiSearchInstance.name + parent: apimService + properties: { + description: aiSearchInstance.description + url: aiSearchInstance.url + protocol: 'http' + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +}] + +resource contentSafetyBackend 'Microsoft.ApiManagement/service/backends@2024-05-01' = { + name: 'content-safety-backend' + parent: apimService + properties: { + description: 'Content Safety Service Backend' + url: contentSafetyServiceUrl + protocol: 'http' + tls: { + validateCertificateChain: true + validateCertificateName: true + } + credentials: { + managedIdentity: { + clientId: managedIdentity.properties.clientId + resource: 'https://cognitiveservices.azure.com' + } + } + } +} + +resource apimOpenaiApiUamiNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: openAiApiUamiNamedValue + parent: apimService + properties: { + displayName: openAiApiUamiNamedValue + secret: true + value: managedIdentity.properties.clientId + } +} + +resource apiopenAiApiEntraNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: openAiApiEntraNamedValue + parent: apimService + properties: { + displayName: openAiApiEntraNamedValue + secret: false + value: entraAuth + } +} +resource apiopenAiApiClientNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: openAiApiClientNamedValue + parent: apimService + properties: { + displayName: openAiApiClientNamedValue + secret: true + value: !empty(clientAppId) ? clientAppId : 'not-configured' + } +} +resource apiopenAiApiTenantNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: openAiApiTenantNamedValue + parent: apimService + properties: { + displayName: openAiApiTenantNamedValue + secret: true + value: !empty(tenantId) ? tenantId : tenant().tenantId + } +} +resource apimOpenaiApiAudienceNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: openAiApiAudienceNamedValue + parent: apimService + properties: { + displayName: openAiApiAudienceNamedValue + secret: true + value: !empty(audience) ? audience : 'https://cognitiveservices.azure.com/.default' + } +} + +resource piiServiceUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: 'piiServiceUrl' + parent: apimService + properties: { + displayName: 'piiServiceUrl' + secret: false + value: aiLanguageServiceUrl + } +} + +resource piiServiceKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: 'piiServiceKey' + parent: apimService + properties: { + displayName: 'piiServiceKey' + secret: true + value: 'replace-with-language-service-key-if-needed' + } +} + +resource contentSafetyServiceUrlNamedValue 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = { + name: 'contentSafetyServiceUrl' + parent: apimService + properties: { + displayName: 'contentSafetyServiceUrl' + secret: false + value: contentSafetyServiceUrl + } +} + +// Policy Fragments Module +module policyFragments './policy-fragments.bicep' = { + name: 'apim-policy-fragments' + params: { + apimServiceName: apimService.name + enablePIIAnonymization: enablePIIAnonymization + enableAIModelInference: enableAIModelInference + enableUnifiedAiApi: enableUnifiedAiApi + } + dependsOn: [ + apiopenAiApiClientNamedValue + apiopenAiApiEntraNamedValue + apimOpenaiApiAudienceNamedValue + apiopenAiApiTenantNamedValue + ehUsageLogger + ehPIIUsageLogger + piiServiceUrlNamedValue + piiServiceKeyNamedValue + jwtTenantIdNamedValue + jwtAppRegistrationIdNamedValue + jwtIssuerNamedValue + jwtOpenIdConfigUrlNamedValue + ] +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2024-05-01' = { + name: 'appinsights-logger' + parent: apimService + properties: { + credentials: { + connectionString: applicationInsights.properties.ConnectionString + } + description: 'Application Insights logger for API observability' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource apimAzMonitorLogger 'Microsoft.ApiManagement/service/loggers@2024-10-01-preview' = { + parent: apimService + name: 'azuremonitor' + properties: { + loggerType: 'azureMonitor' + isBuffered: false // Set to false to ensure logs are sent immediately + description: 'Azure Monitor logger for Log Analytics' + } +} + +resource apimAppInsights 'Microsoft.ApiManagement/service/diagnostics@2024-05-01' = { + parent: apimService + name: 'applicationinsights' + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + verbosity: 'information' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + samplingType: 'fixed' + percentage: 100 + } + frontend: { + request: { + body: { + bytes: 0 + } + } + response: { + body: { + bytes: 0 + } + } + } + backend: { + request: { + body: { + bytes: 0 + } + } + response: { + body: { + bytes: 0 + } + } + } + } +} + +resource ehUsageLogger 'Microsoft.ApiManagement/service/loggers@2022-08-01' = { + name: 'usage-eventhub-logger' + parent: apimService + properties: { + loggerType: 'azureEventHub' + description: 'Event Hub logger for OpenAI usage metrics' + credentials: { + name: eventHubName + endpointAddress: replace(eventHubEndpoint, 'https://', '') + identityClientId: managedIdentity.properties.clientId + } + } +} + +resource ehPIIUsageLogger 'Microsoft.ApiManagement/service/loggers@2022-08-01' = if (enablePIIAnonymization) { + name: 'pii-usage-eventhub-logger' + parent: apimService + properties: { + loggerType: 'azureEventHub' + description: 'Event Hub logger for PII usage metrics and logs' + credentials: { + name: eventHubPIIName + endpointAddress: replace(eventHubPIIEndpoint, 'https://', '') + identityClientId: managedIdentity.properties.clientId + } + } +} + +resource apimDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + scope: apimService + name: 'apimDiagnosticSettings' + properties: { + workspaceId: applicationInsights.properties.WorkspaceResourceId + logAnalyticsDestinationType: 'Dedicated' + logs: [ + { + categoryGroup: 'AllLogs' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +// Sample MCP resources +module weatherAPI './api.bicep' = if (isMCPSampleDeployed) { + name: 'weather-api' + params: { + serviceName: apimService.name + apiName: 'weather-api' + path: 'weather' + apiRevision: '1' + apiDispalyName: 'Weather API' + subscriptionRequired: false + subscriptionKeyName: 'api-key' + openApiSpecification: loadTextContent('./sample/weather/openapi.json') + apiDescription: 'Weather API for getting dynamic weather information for a given location.' + policyDocument: loadTextContent('./sample/weather/policy.xml') + enableAPIDeployment: true + enableAPIDiagnostics: false + } + dependsOn: [ + ] +} + +module weatherMCP './mcp-from-api.bicep' = if (isMCPSampleDeployed) { + name: 'weather-mcp' + params: { + apimServiceName: apimService.name + appInsightsLoggerName: 'appinsights-logger' + apiName: 'weather-api' + operationNames: ['get-weather'] + mcpPath: 'weather-mcp' + mcpName: 'weather-mcp' + mcpDisplayName: 'Weather MCP Development' + mcpDescription: 'MCP server for weather data operations for given location (Development)' + } + dependsOn: [ + weatherAPI + apimLogger + ] +} + +module microsoftLearnMCPServer 'mcp-existing.bicep' = if (isMCPSampleDeployed) { + name: 'microsoftLearnMCPServer' + params: { + apimServiceName: apimService.name + backendName: 'ms-learn-mcp-server' + backendDescription: 'Microsoft Learn MCP Server' + backendURL: 'https://learn.microsoft.com/api/mcp' + mcpApiName: 'ms-learn-mcp' + mcpDisplayName: 'Microsoft Learn MCP' + mcpDescription: 'Microsoft Learn MCP Server' + mcpPath: 'ms-learn-mcp' + mcpPolicyXml: '' + } + dependsOn: [ + apimLogger + ] +} + +@description('The name of the deployed API Management service.') +output apimName string = apimService.name + +@description('The path for the OpenAI API in the deployed API Management service.') +output apimOpenaiApiPath string = apimOpenaiApi.outputs.path + +@description('Gateway URL for the deployed API Management resource.') +output apimGatewayUrl string = apimService.properties.gatewayUrl + +output apimIdentityClientId string = managedIdentity.properties.principalId diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml new file mode 100644 index 000000000..532e6a87d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30-compressed.openapi.yaml @@ -0,0 +1,3067 @@ +openapi: 3.0.1 +info: + title: Azure Document Intelligence + description: 'Extracts content, layout, and structured data from documents.' + version: '1.0' +servers: + - url: https://REPLACED-BY-POLICY/documentintelligence +paths: + /documentClassifiers: + get: + summary: DocumentClassifiers_ListClassifiers + description: List all document classifiers. + operationId: DocumentClassifiers_ListClassifiers + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentClassifierDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentClassifiers/{classifierId}': + get: + summary: DocumentClassifiers_GetClassifier + description: Gets detailed document classifier information. + operationId: DocumentClassifiers_GetClassifier + parameters: + - $ref: '#/components/parameters/ClassifierIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentClassifierDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + delete: + summary: DocumentClassifiers_DeleteClassifier + description: Deletes document classifier. + operationId: DocumentClassifiers_DeleteClassifier + parameters: + - $ref: '#/components/parameters/ClassifierIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentClassifiers/{classifierId}:analyze': + post: + summary: DocumentClassifiers_ClassifyDocumentFromStream + description: "Classifies document with document classifier. This operation can be used in two ways:\n1. With a binary document stream in the request body (current endpoint)\n2. With a ClassifyDocumentRequest object containing URL or base64-encoded document (add '_overload=classifyDocument' query parameter)" + operationId: DocumentClassifiers_ClassifyDocumentFromStream + parameters: + - $ref: '#/components/parameters/ClassifierIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/StringIndexTypeParameter' + - $ref: '#/components/parameters/SplitParameter' + - $ref: '#/components/parameters/PagesParameter' + - name: _overload + in: query + description: Optional parameter to specify the operation variant. Use 'classifyDocument' for URL or base64 encoded document input. + schema: + enum: + - classifyDocument + type: string + requestBody: + description: Input content. Can be either a binary document stream or a ClassifyDocumentRequest object depending on the _overload parameter. + content: + application/octet-stream: + schema: + $ref: '#/components/schemas/Input' + application/pdf: + schema: + $ref: '#/components/schemas/Input' + image/jpeg: + schema: + $ref: '#/components/schemas/Input' + image/png: + schema: + $ref: '#/components/schemas/Input' + image/tiff: + schema: + $ref: '#/components/schemas/Input' + image/bmp: + schema: + $ref: '#/components/schemas/Input' + image/heif: + schema: + $ref: '#/components/schemas/Input' + text/html: + schema: + $ref: '#/components/schemas/Input' + application/vnd.openxmlformats-officedocument.wordprocessingml.document: + schema: + $ref: '#/components/schemas/Input' + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + $ref: '#/components/schemas/Input' + application/vnd.openxmlformats-officedocument.presentationml.presentation: + schema: + $ref: '#/components/schemas/Input' + application/json: + schema: + $ref: '#/components/schemas/Input' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeOperation + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentClassifiers/{classifierId}:copyTo': + post: + summary: DocumentClassifiers_CopyClassifierTo + description: 'Copies document classifier to the target resource, region, and classifierId.' + operationId: DocumentClassifiers_CopyClassifierTo + parameters: + - $ref: '#/components/parameters/ClassifierIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Copy to request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ClassifierCopyAuthorization' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentClassifierCopyToOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentClassifiers/{classifierId}/analyzeResults/{resultId}': + get: + summary: DocumentClassifiers_GetClassifyResult + description: Gets the result of document classifier. + operationId: DocumentClassifiers_GetClassifyResult + parameters: + - $ref: '#/components/parameters/ClassifierIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeOperation' + '500': + $ref: '#/components/responses/ErrorResponse' + /documentClassifiers:authorizeCopy: + post: + summary: DocumentClassifiers_AuthorizeClassifierCopy + description: "Generates authorization to copy a document classifier to this location with\nspecified classifierId and optional description." + operationId: DocumentClassifiers_AuthorizeClassifierCopy + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Authorize copy request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizeClassifierCopyRequest' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ClassifierCopyAuthorization' + '500': + $ref: '#/components/responses/ErrorResponse' + /documentClassifiers:build: + post: + summary: DocumentClassifiers_BuildClassifier + description: Builds a custom document classifier. + operationId: DocumentClassifiers_BuildClassifier + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Build request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/BuildDocumentClassifierRequest' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentClassifierBuildOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + /documentModels: + get: + summary: DocumentModels_ListModels + description: List all document models + operationId: DocumentModels_ListModels + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentModelDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}:analyze': + post: + summary: DocumentModels_AnalyzeDocument + description: "Analyzes document with document model. This operation can be used in two ways:\n1. With a binary document stream in the request body (when _overload is not specified)\n2. With an AnalyzeDocumentRequest object containing URL or base64-encoded document (add '_overload=analyzeDocument' query parameter)" + operationId: DocumentModels_AnalyzeDocument + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/PagesParameter' + - $ref: '#/components/parameters/LocaleParameter' + - $ref: '#/components/parameters/StringIndexTypeParameter' + - $ref: '#/components/parameters/FeaturesParameter' + - $ref: '#/components/parameters/QueryFieldsParameter' + - $ref: '#/components/parameters/OutputContentFormatParameter' + - $ref: '#/components/parameters/OutputParameter' + - name: _overload + in: query + description: Optional parameter to specify the operation variant. Use 'analyzeDocument' for URL or base64 encoded document input. + schema: + enum: + - analyzeDocument + type: string + requestBody: + description: Input content. Can be either a binary document stream or an AnalyzeDocumentRequest object depending on the _overload parameter. + content: + application/json: + schema: + $ref: '#/components/schemas/Input' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeOperation + schema: + type: string + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}:analyzeBatch': + post: + summary: DocumentModels_AnalyzeBatchDocuments + description: Analyzes batch documents with document model. + operationId: DocumentModels_AnalyzeBatchDocuments + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/PagesParameter' + - $ref: '#/components/parameters/LocaleParameter' + - $ref: '#/components/parameters/StringIndexTypeParameter' + - $ref: '#/components/parameters/FeaturesParameter' + - $ref: '#/components/parameters/QueryFieldsParameter' + - $ref: '#/components/parameters/OutputContentFormatParameter' + - $ref: '#/components/parameters/OutputParameter' + requestBody: + description: Analyze batch request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeBatchDocumentsRequest' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeBatchOperation + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}': + get: + summary: DocumentModels_GetModel + description: Gets detailed document model information. + operationId: DocumentModels_GetModel + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentModelDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + delete: + summary: DocumentModels_DeleteModel + description: Deletes document model. + operationId: DocumentModels_DeleteModel + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}:copyTo': + post: + summary: DocumentModels_CopyModelTo + description: 'Copies document model to the target resource, region, and modelId.' + operationId: DocumentModels_CopyModelTo + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Copy to request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCopyAuthorization' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelCopyToOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}/analyzeBatchResults': + get: + summary: DocumentModels_ListAnalyzeBatchResults + description: List batch document analysis results. + operationId: DocumentModels_ListAnalyzeBatchResults + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/PagedAnalyzeBatchOperation' + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}/analyzeBatchResults/{resultId}': + get: + summary: DocumentModels_GetAnalyzeBatchResult + description: Gets the result of batch document analysis. + operationId: DocumentModels_GetAnalyzeBatchResult + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeBatchOperation' + '500': + $ref: '#/components/responses/ErrorResponse' + delete: + summary: DocumentModels_DeleteAnalyzeBatchResult + description: Mark the batch document analysis result for deletion. + operationId: DocumentModels_DeleteAnalyzeBatchResult + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful.' + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}': + get: + summary: DocumentModels_GetAnalyzeResult + description: Gets the result of document analysis. + operationId: DocumentModels_GetAnalyzeResult + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeOperation' + '500': + $ref: '#/components/responses/ErrorResponse' + delete: + summary: DocumentModels_DeleteAnalyzeResult + description: Mark the result of document analysis for deletion. + operationId: DocumentModels_DeleteAnalyzeResult + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful.' + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}/figures/{figureId}': + get: + summary: DocumentModels_GetAnalyzeResultFigure + description: Gets the generated cropped image of specified figure from document analysis. + operationId: DocumentModels_GetAnalyzeResultFigure + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/FigureIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + image/png: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ImagePngResp' + application/json: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ApplicationJ' + '500': + $ref: '#/components/responses/ErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}/pdf': + get: + summary: DocumentModels_GetAnalyzeResultPdf + description: Gets the generated searchable PDF output from document analysis. + operationId: DocumentModels_GetAnalyzeResultPdf + parameters: + - $ref: '#/components/parameters/ModelIdParameter' + - $ref: '#/components/parameters/ResultIdParameter' + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/pdf: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationPdfResponse' + application/json: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationJsonResponse' + '500': + $ref: '#/components/responses/ErrorResponse' + /documentModels:authorizeCopy: + post: + summary: DocumentModels_AuthorizeModelCopy + description: "Generates authorization to copy a document model to this location with\nspecified modelId and optional description." + operationId: DocumentModels_AuthorizeModelCopy + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Authorize copy request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizeCopyRequest' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCopyAuthorization' + '500': + $ref: '#/components/responses/ErrorResponse' + /documentModels:build: + post: + summary: DocumentModels_BuildModel + description: Builds a custom document analysis model. + operationId: DocumentModels_BuildModel + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Build request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/BuildDocumentModelRequest' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelBuildOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + /documentModels:compose: + post: + summary: DocumentModels_ComposeModel + description: Creates a new document model from document types of existing document models. + operationId: DocumentModels_ComposeModel + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + requestBody: + description: Compose request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ComposeDocumentModelRequest' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelComposeOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + $ref: '#/components/responses/ErrorResponse' + /info: + get: + summary: MiscellaneousOperations_GetResourceDetails + description: Return information about the current resource. + operationId: MiscellaneousOperations_GetResourceDetails + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceResourceDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + /operations: + get: + summary: MiscellaneousOperations_ListOperations + description: Lists all operations. + operationId: MiscellaneousOperations_ListOperations + parameters: + - $ref: '#/components/parameters/ApiVersionParameter' + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentIntelligenceOperationDetails' + '500': + $ref: '#/components/responses/ErrorResponse' + '/operations/{operationId}': + get: + summary: MiscellaneousOperations_GetOperation + description: "Gets operation info. Different operation types can be retrieved:\n1. General operation: no _overload parameter needed\n2. Document Model Build operation: use _overload=getDocumentModelBuildOperation\n3. Document Model Compose operation: use _overload=getDocumentModelComposeOperation\n4. Document Model Copy To operation: use _overload=getDocumentModelCopyToOperation\n5. Document Classifier Copy To operation: use _overload=getDocumentClassifierCopyToOperation\n6. Document Classifier Build operation: use _overload=getDocumentClassifierBuildOperation" + operationId: MiscellaneousOperations_GetOperation + parameters: + - name: operationId + in: path + description: Operation ID + required: true + schema: + type: string + - $ref: '#/components/parameters/ApiVersionParameter' + - name: _overload + in: query + description: Optional parameter to specify the operation type to retrieve. + schema: + enum: + - getOperation + - getDocumentModelBuildOperation + - getDocumentModelComposeOperation + - getDocumentModelCopyToOperation + - getDocumentClassifierCopyToOperation + - getDocumentClassifierBuildOperation + type: string + - $ref: '#/components/parameters/ClientRequestIdParameter' + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + '500': + $ref: '#/components/responses/ErrorResponse' +components: + parameters: + ApiVersionParameter: + name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + ClientRequestIdParameter: + name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + ClassifierIdParameter: + name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + ModelIdParameter: + name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + ResultIdParameter: + name: resultId + in: path + description: Operation result ID. + required: true + schema: + type: string + format: uuid + PagesParameter: + name: pages + in: query + description: '1-based page numbers to analyze. Ex. "1-3,5,7-9"' + schema: + pattern: '^(\d+(-\d+)?)(,\s*(\d+(-\d+)?))*$' + type: string + StringIndexTypeParameter: + name: stringIndexType + in: query + description: Method used to compute string offset and length. + schema: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + default: textElements + SplitParameter: + name: split + in: query + description: Document splitting mode. + schema: + enum: + - auto + - none + - perPage + type: string + default: none + LocaleParameter: + name: locale + in: query + description: "Locale hint for text recognition and document analysis. Value may contain only\nthe language code (ex. \"en\", \"fr\") or BCP 47 language tag (ex. \"en-US\")." + schema: + type: string + FeaturesParameter: + name: features + in: query + description: List of optional analysis features. + schema: + type: array + items: + enum: + - ocrHighResolution + - languages + - barcodes + - formulas + - keyValuePairs + - styleFont + - queryFields + type: string + x-ms-enum: + name: DocumentAnalysisFeature + modelAsString: true + values: + - name: ocrHighResolution + value: ocrHighResolution + description: Perform OCR at a higher resolution to handle documents with fine print. + - name: languages + value: languages + description: Enable the detection of the text content language. + - name: barcodes + value: barcodes + description: Enable the detection of barcodes in the document. + - name: formulas + value: formulas + description: Enable the detection of mathematical expressions in the document. + - name: keyValuePairs + value: keyValuePairs + description: Enable the detection of general key value pairs (form fields) in the document. + - name: styleFont + value: styleFont + description: Enable the recognition of various font styles. + - name: queryFields + value: queryFields + description: Enable the extraction of additional fields via the queryFields query parameter. + QueryFieldsParameter: + name: queryFields + in: query + description: 'List of additional fields to extract. Ex. "NumberOfGuests,StoreNumber"' + schema: + type: array + items: + type: string + OutputContentFormatParameter: + name: outputContentFormat + in: query + description: Format of the analyze result top-level content. + schema: + enum: + - text + - markdown + type: string + default: text + OutputParameter: + name: output + in: query + description: Additional outputs to generate during analysis. + schema: + type: array + items: + enum: + - pdf + - figures + type: string + x-ms-enum: + name: AnalyzeOutputOption + modelAsString: true + values: + - name: pdf + value: pdf + description: Generate searchable PDF output. + - name: figures + value: figures + description: Generate cropped images of detected figures. + FigureIdParameter: + name: figureId + in: path + description: Figure ID. + required: true + schema: + type: string + headers: + ClientRequestIdHeader: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + ErrorResponse: + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + NoContentResponse: + description: 'There is no content to send for this request, but the headers may be useful.' + headers: + x-ms-client-request-id: + $ref: '#/components/headers/ClientRequestIdHeader' + content: + application/json: {} + AcceptedResponse: + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of the operation + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: {} + schemas: + AddressValue: + type: object + properties: + houseNumber: + type: string + description: House or building number. + poBox: + type: string + description: Post office box number. + road: + type: string + description: Street name. + city: + type: string + description: 'Name of city, town, village, etc.' + state: + type: string + description: First-level administrative division. + postalCode: + type: string + description: Postal code used for mail sorting. + countryRegion: + type: string + description: Country/region. + streetAddress: + type: string + description: 'Street-level address, excluding city, state, countryRegion, and postalCode.' + unit: + type: string + description: Apartment or office number + cityDistrict: + type: string + description: "Districts or boroughs within a city, such as Brooklyn in New York City or City\nof Westminster in London." + stateDistrict: + type: string + description: Second-level administrative division used in certain locales. + suburb: + type: string + description: 'Unofficial neighborhood name, like Chinatown.' + house: + type: string + description: 'Build name, such as World Trade Center.' + level: + type: string + description: 'Floor number, such as 3F.' + description: Address field value. + AnalyzeBatchDocumentsRequest: + required: + - resultContainerUrl + type: object + properties: + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + resultContainerUrl: + type: string + description: Azure Blob Storage container URL where analyze result files will be stored. + format: uri + resultPrefix: + type: string + description: Blob name prefix of result files. + overwriteExisting: + type: boolean + description: Overwrite existing analyze result files? + default: false + description: Batch document analysis parameters. + AnalyzeBatchOperation: + required: + - status + - createdDateTime + - lastUpdatedDateTime + type: object + properties: + resultId: + $ref: '#/components/schemas/Azure.Core.uuid' + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + createdDateTime: + type: string + description: Date and time (UTC) when the operation was submitted. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + percentCompleted: + maximum: 100 + minimum: 0 + type: integer + description: Operation progress (0-100). + format: int32 + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + result: + $ref: '#/components/schemas/AnalyzeBatchResult' + description: Status and result of the analyze batch operation. + AnalyzeBatchOperationDetail: + required: + - status + - sourceUrl + type: object + properties: + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + sourceUrl: + type: string + description: URL of the source document. + format: uri + resultUrl: + type: string + description: URL of the analyze result JSON. + format: uri + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Operation detail for a document in a batch analysis. + AnalyzeBatchResult: + required: + - succeededCount + - failedCount + - skippedCount + type: object + properties: + succeededCount: + type: integer + description: Number of documents that completed with status succeeded. + format: int32 + failedCount: + type: integer + description: Number of documents that completed with status failed. + format: int32 + skippedCount: + type: integer + description: Number of documents that completed with status skipped. + format: int32 + details: + type: array + items: + $ref: '#/components/schemas/AnalyzeBatchOperationDetail' + description: Operation detail for each document in the batch. + x-ms-identifiers: [ ] + description: Batch document analysis result. + AnalyzeDocumentRequest: + type: object + properties: + urlSource: + type: string + description: Document URL to analyze. Either urlSource or base64Source must be specified. + format: uri + base64Source: + type: string + description: "Base64 encoding of the document to analyze. Either urlSource or base64Source\nmust be specified." + format: byte + description: Document analysis parameters. + AnalyzeOperation: + required: + - status + - createdDateTime + - lastUpdatedDateTime + type: object + properties: + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + createdDateTime: + type: string + description: Date and time (UTC) when the analyze operation was submitted. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + analyzeResult: + $ref: '#/components/schemas/AnalyzeResult' + description: Status and result of the analyze operation. + AnalyzeResult: + required: + - apiVersion + - modelId + - stringIndexType + - content + - pages + type: object + properties: + apiVersion: + type: string + description: API version used to produce this result. + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Document model ID used to produce this result. + stringIndexType: + $ref: '#/components/schemas/StringIndexType' + contentFormat: + $ref: '#/components/schemas/DocumentContentFormat' + content: + type: string + description: "Concatenate string representation of all textual and visual elements in reading\norder." + pages: + type: array + items: + $ref: '#/components/schemas/DocumentPage' + description: Analyzed pages. + x-ms-identifiers: [ ] + paragraphs: + type: array + items: + $ref: '#/components/schemas/DocumentParagraph' + description: Extracted paragraphs. + x-ms-identifiers: [ ] + tables: + type: array + items: + $ref: '#/components/schemas/DocumentTable' + description: Extracted tables. + x-ms-identifiers: [ ] + figures: + type: array + items: + $ref: '#/components/schemas/DocumentFigure' + description: Extracted figures. + sections: + type: array + items: + $ref: '#/components/schemas/DocumentSection' + description: Extracted sections. + x-ms-identifiers: [ ] + keyValuePairs: + type: array + items: + $ref: '#/components/schemas/DocumentKeyValuePair' + description: Extracted key-value pairs. + x-ms-identifiers: [ ] + styles: + type: array + items: + $ref: '#/components/schemas/DocumentStyle' + description: Extracted font styles. + x-ms-identifiers: [ ] + languages: + type: array + items: + $ref: '#/components/schemas/DocumentLanguage' + description: Detected languages. + x-ms-identifiers: [ ] + documents: + type: array + items: + $ref: '#/components/schemas/AnalyzedDocument' + description: Extracted documents. + x-ms-identifiers: [ ] + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered. + x-ms-identifiers: [ ] + description: Document analysis result. + AnalyzedDocument: + required: + - docType + - spans + - confidence + type: object + properties: + docType: + maxLength: 64 + minLength: 2 + type: string + description: Document type. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the document. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the document in the reading order concatenated content. + x-ms-identifiers: [ ] + fields: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentField' + description: Dictionary of named field values. + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the document. + format: float + description: An object describing the location and semantic content of a document. + AuthorizeClassifierCopyRequest: + required: + - classifierId + type: object + properties: + classifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + description: + maxLength: 4096 + type: string + description: Document classifier description. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document classifier. + description: Request body to authorize document classifier copy. + AuthorizeCopyRequest: + required: + - modelId + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + description: Request body to authorize document model copy. + Azure.Core.uuid: + type: string + description: Universally Unique Identifier + format: uuid + AzureBlobContentSource: + required: + - containerUrl + type: object + properties: + containerUrl: + type: string + description: Azure Blob Storage container URL. + format: uri + prefix: + type: string + description: Blob name prefix. + description: Azure Blob Storage content. + AzureBlobFileListContentSource: + required: + - containerUrl + - fileList + type: object + properties: + containerUrl: + type: string + description: Azure Blob Storage container URL. + format: uri + fileList: + type: string + description: Path to a JSONL file within the container specifying a subset of documents. + description: File list in Azure Blob Storage. + BoundingRegion: + required: + - pageNumber + - polygon + type: object + properties: + pageNumber: + minimum: 1 + type: integer + description: 1-based page number of page containing the bounding region. + format: int32 + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon on the page, or the entire page if not specified.\nCoordinates specified relative to the top-left of the page. The numbers\nrepresent the x, y values of the polygon vertices, clockwise from the left\n(-180 degrees inclusive) relative to the element orientation." + description: Bounding polygon on a specific page of the input. + BuildDocumentClassifierRequest: + required: + - classifierId + - docTypes + type: object + properties: + classifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + description: + maxLength: 4096 + type: string + description: Document classifier description. + baseClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Base classifierId on top of which to train the classifier. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/ClassifierDocumentTypeDetails' + description: List of document types to classify against. + allowOverwrite: + type: boolean + description: Allow overwriting an existing classifier with the same name. + default: false + description: Request body to build a new custom document classifier. + BuildDocumentModelRequest: + required: + - modelId + - buildMode + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + maxTrainingHours: + type: number + description: Max number of V100-equivalent GPU hours to use for model training. Default=0.5. + format: float + default: 0.5 + allowOverwrite: + type: boolean + description: Allow overwriting an existing model with the same name. + default: false + description: Request body to build a new custom document model. + ClassifierCopyAuthorization: + required: + - targetResourceId + - targetResourceRegion + - targetClassifierId + - targetClassifierLocation + - accessToken + - expirationDateTime + type: object + properties: + targetResourceId: + type: string + description: ID of the target Azure resource where the document classifier should be copied to. + targetResourceRegion: + type: string + description: "Location of the target Azure resource where the document classifier should be copied\nto." + targetClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Identifier of the target document classifier. + targetClassifierLocation: + type: string + description: URL of the copied document classifier in the target account. + format: uri + accessToken: + type: string + description: Token used to authorize the request. + expirationDateTime: + type: string + description: Date/time when the access token expires. + format: date-time + description: "Authorization to copy a document classifier to the specified target resource and\nclassifierId." + ClassifierDocumentTypeDetails: + type: object + properties: + sourceKind: + $ref: '#/components/schemas/ContentSourceKind' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + description: Classifier document type info. + ClassifyDocumentRequest: + type: object + properties: + urlSource: + type: string + description: Document URL to classify. Either urlSource or base64Source must be specified. + format: uri + base64Source: + type: string + description: "Base64 encoding of the document to classify. Either urlSource or base64Source\nmust be specified." + format: byte + description: Document classification parameters. + ComposeDocumentModelRequest: + required: + - modelId + - classifierId + - docTypes + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + classifierId: + type: string + description: Custom classifier to split and classify the input file. + split: + enum: + - auto + - none + - perPage + type: string + description: File splitting behavior. + default: none + x-ms-enum: + name: SplitMode + modelAsString: true + values: + - name: auto + value: auto + description: Automatically split file into documents. + - name: none + value: none + description: Treat the entire file as a single document. + - name: perPage + value: perPage + description: Treat each page in the file as a separate document. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentTypeDetails' + description: Dictionary mapping supported docTypes to the corresponding document models. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + description: Request body to create a composed document model from component document models. + ContentSourceKind: + enum: + - url + - base64 + - azureBlob + - azureBlobFileList + type: string + description: Type of content source. + x-ms-enum: + name: ContentSourceKind + modelAsString: true + values: + - name: url + value: url + description: Content at a specific URL. + - name: base64 + value: base64 + description: Content represented via Base64 encoding. + - name: azureBlob + value: azureBlob + description: Files in a path within an Azure Blob Storage container. + - name: azureBlobFileList + value: azureBlobFileList + description: A file list specifying individual files in an Azure Blob Storage container. + CurrencyValue: + required: + - amount + type: object + properties: + amount: + type: number + description: Currency amount. + format: double + currencySymbol: + type: string + description: 'Currency symbol label, if any.' + currencyCode: + type: string + description: 'Resolved currency code (ISO 4217), if any.' + description: Currency field value. + CustomDocumentModelsDetails: + required: + - count + - limit + type: object + properties: + count: + type: integer + description: Number of custom document models in the current resource. + format: int32 + limit: + type: integer + description: Maximum number of custom document models supported in the current resource. + format: int32 + description: Details regarding custom document models. + DocumentAnalysisFeature: + enum: + - ocrHighResolution + - languages + - barcodes + - formulas + - keyValuePairs + - styleFont + - queryFields + type: string + description: Document analysis features to enable. + x-ms-enum: + name: DocumentAnalysisFeature + modelAsString: true + values: + - name: ocrHighResolution + value: ocrHighResolution + description: Perform OCR at a higher resolution to handle documents with fine print. + - name: languages + value: languages + description: Enable the detection of the text content language. + - name: barcodes + value: barcodes + description: Enable the detection of barcodes in the document. + - name: formulas + value: formulas + description: Enable the detection of mathematical expressions in the document. + - name: keyValuePairs + value: keyValuePairs + description: Enable the detection of general key value pairs (form fields) in the document. + - name: styleFont + value: styleFont + description: Enable the recognition of various font styles. + - name: queryFields + value: queryFields + description: Enable the extraction of additional fields via the queryFields query parameter. + DocumentBarcode: + required: + - kind + - value + - span + - confidence + type: object + properties: + kind: + $ref: '#/components/schemas/DocumentBarcodeKind' + value: + type: string + description: Barcode value. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the barcode, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the barcode. + format: float + description: A barcode object. + DocumentBarcodeKind: + enum: + - QRCode + - PDF417 + - UPCA + - UPCE + - Code39 + - Code128 + - EAN8 + - EAN13 + - DataBar + - Code93 + - Codabar + - DataBarExpanded + - ITF + - MicroQRCode + - Aztec + - DataMatrix + - MaxiCode + type: string + description: Barcode kind. + x-ms-enum: + name: DocumentBarcodeKind + modelAsString: true + values: + - name: QRCode + value: QRCode + description: 'QR code, as defined in ISO/IEC 18004:2015.' + - name: PDF417 + value: PDF417 + description: 'PDF417, as defined in ISO 15438.' + - name: UPCA + value: UPCA + description: GS1 12-digit Universal Product Code. + - name: UPCE + value: UPCE + description: GS1 6-digit Universal Product Code. + - name: Code39 + value: Code39 + description: 'Code 39 barcode, as defined in ISO/IEC 16388:2007.' + - name: Code128 + value: Code128 + description: 'Code 128 barcode, as defined in ISO/IEC 15417:2007.' + - name: EAN8 + value: EAN8 + description: GS1 8-digit International Article Number (European Article Number). + - name: EAN13 + value: EAN13 + description: GS1 13-digit International Article Number (European Article Number). + - name: DataBar + value: DataBar + description: GS1 DataBar barcode. + - name: Code93 + value: Code93 + description: 'Code 93 barcode, as defined in ANSI/AIM BC5-1995.' + - name: Codabar + value: Codabar + description: 'Codabar barcode, as defined in ANSI/AIM BC3-1995.' + - name: DataBarExpanded + value: DataBarExpanded + description: GS1 DataBar Expanded barcode. + - name: ITF + value: ITF + description: 'Interleaved 2 of 5 barcode, as defined in ANSI/AIM BC2-1995.' + - name: MicroQRCode + value: MicroQRCode + description: 'Micro QR code, as defined in ISO/IEC 23941:2022.' + - name: Aztec + value: Aztec + description: 'Aztec code, as defined in ISO/IEC 24778:2008.' + - name: DataMatrix + value: DataMatrix + description: 'Data matrix code, as defined in ISO/IEC 16022:2006.' + - name: MaxiCode + value: MaxiCode + description: 'MaxiCode, as defined in ISO/IEC 16023:2000.' + DocumentBuildMode: + enum: + - template + - neural + type: string + description: Custom document model build mode. + x-ms-enum: + name: DocumentBuildMode + modelAsString: true + values: + - name: template + value: template + description: Target documents with similar visual templates. + - name: neural + value: neural + description: Support documents with diverse visual templates. + DocumentCaption: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Content of the caption. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the caption. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the caption in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the caption. + description: A caption object describing a table or figure. + DocumentClassifierBuildOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentClassifierBuild + DocumentClassifierCopyToOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentClassifierCopyTo + DocumentClassifierDetails: + required: + - classifierId + - createdDateTime + - apiVersion + - docTypes + type: object + properties: + classifierId: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + x-ms-mutability: + - read + - create + description: + maxLength: 4096 + type: string + description: Document classifier description. + createdDateTime: + type: string + description: Date and time (UTC) when the document classifier was created. + format: date-time + expirationDateTime: + type: string + description: Date and time (UTC) when the document classifier will expire. + format: date-time + modifiedDateTime: + type: string + description: Date and time (UTC) when the document model was last modified. + format: date-time + readOnly: true + apiVersion: + type: string + description: API version used to create this document classifier. + baseClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Base classifierId on top of which the classifier was trained. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/ClassifierDocumentTypeDetails' + description: List of document types to classify against. + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered while building the classifier. + x-ms-identifiers: [ ] + description: Document classifier info. + DocumentContentFormat: + enum: + - text + - markdown + type: string + description: Format of the content in analyzed result. + x-ms-enum: + name: DocumentContentFormat + modelAsString: true + values: + - name: text + value: text + description: Plain text representation of the document content without any formatting. + - name: markdown + value: markdown + description: "Markdown representation of the document content with section headings, tables,\netc." + DocumentElement: + type: string + description: "An JSON pointer referencing an element of the document, such as paragraph,\ntable, section, etc." + DocumentField: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/DocumentFieldType' + valueString: + type: string + description: String value. + valueDate: + type: string + description: Date value in YYYY-MM-DD format (ISO 8601). + format: date + valueTime: + type: string + description: Time value in hh:mm:ss format (ISO 8601). + format: time + valuePhoneNumber: + type: string + description: Phone number value in E.164 format (ex. +19876543210). + valueNumber: + type: number + description: Floating point value. + format: double + valueInteger: + type: integer + description: Integer value. + format: int64 + valueSelectionMark: + $ref: '#/components/schemas/DocumentSelectionMarkState' + valueSignature: + $ref: '#/components/schemas/DocumentSignatureType' + valueCountryRegion: + type: string + description: 3-letter country code value (ISO 3166-1 alpha-3). + valueArray: + type: array + items: + $ref: '#/components/schemas/DocumentField' + description: Array of field values. + x-ms-identifiers: [ ] + valueObject: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentField' + description: Dictionary of named field values. + valueCurrency: + $ref: '#/components/schemas/CurrencyValue' + valueAddress: + $ref: '#/components/schemas/AddressValue' + valueBoolean: + type: boolean + description: Boolean value. + valueSelectionGroup: + type: array + items: + type: string + description: Selection group value. + content: + type: string + description: Field content. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the field. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the field in the reading order concatenated content. + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the field. + format: float + description: An object representing the content and location of a field value. + DocumentFieldSchema: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/DocumentFieldType' + description: + maxLength: 4096 + type: string + description: Field description. + example: + type: string + description: Example field content. + items: + $ref: '#/components/schemas/DocumentFieldSchema' + properties: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentFieldSchema' + description: Named sub-fields of the object field. + description: Description of the field semantic schema using a JSON Schema style syntax. + DocumentFieldType: + enum: + - string + - date + - time + - phoneNumber + - number + - integer + - selectionMark + - countryRegion + - signature + - array + - object + - currency + - address + - boolean + - selectionGroup + type: string + description: Semantic data type of the field value. + x-ms-enum: + name: DocumentFieldType + modelAsString: true + values: + - name: string + value: string + description: Plain text. + - name: date + value: date + description: 'Date, normalized to ISO 8601 (YYYY-MM-DD) format.' + - name: time + value: time + description: 'Time, normalized to ISO 8601 (hh:mm:ss) format.' + - name: phoneNumber + value: phoneNumber + description: 'Phone number, normalized to E.164 (+{CountryCode}{SubscriberNumber}) format.' + - name: number + value: number + description: 'Floating point number, normalized to double precision floating point.' + - name: integer + value: integer + description: 'Integer number, normalized to 64-bit signed integer.' + - name: selectionMark + value: selectionMark + description: Is field selected? + - name: countryRegion + value: countryRegion + description: 'Country/region, normalized to ISO 3166-1 alpha-3 format (ex. USA).' + - name: signature + value: signature + description: Is signature present? + - name: array + value: array + description: List of subfields of the same type. + - name: object + value: object + description: Named list of subfields of potentially different types. + - name: currency + value: currency + description: Currency amount with optional currency symbol and unit. + - name: address + value: address + description: Parsed address. + - name: boolean + value: boolean + description: 'Boolean value, normalized to true or false.' + - name: selectionGroup + value: selectionGroup + description: Array of selected string values. + DocumentFigure: + required: + - spans + type: object + properties: + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the figure. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the figure in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: 'Child elements of the figure, excluding any caption or footnotes.' + caption: + $ref: '#/components/schemas/DocumentCaption' + footnotes: + type: array + items: + $ref: '#/components/schemas/DocumentFootnote' + description: List of footnotes associated with the figure. + x-ms-identifiers: [ ] + id: + type: string + description: Figure ID. + description: An object representing a figure in the document. + DocumentFontStyle: + enum: + - normal + - italic + type: string + description: Font style. + x-ms-enum: + name: DocumentFontStyle + modelAsString: true + values: + - name: normal + value: normal + description: Characters are represented normally. + - name: italic + value: italic + description: Characters are visually slanted to the right. + DocumentFontWeight: + enum: + - normal + - bold + type: string + description: Font weight. + x-ms-enum: + name: DocumentFontWeight + modelAsString: true + values: + - name: normal + value: normal + description: Characters are represented normally. + - name: bold + value: bold + description: Characters are represented with thicker strokes. + DocumentFootnote: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Content of the footnote. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the footnote. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the footnote in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the footnote. + description: A footnote object describing a table or figure. + DocumentFormula: + required: + - kind + - value + - span + - confidence + type: object + properties: + kind: + $ref: '#/components/schemas/DocumentFormulaKind' + value: + type: string + description: LaTex expression describing the formula. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the formula, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the formula. + format: float + description: A formula object. + DocumentFormulaKind: + enum: + - inline + - display + type: string + description: Formula kind. + x-ms-enum: + name: DocumentFormulaKind + modelAsString: true + values: + - name: inline + value: inline + description: A formula embedded within the content of a paragraph. + - name: display + value: display + description: A formula in display mode that takes up an entire line. + DocumentIntelligenceError: + required: + - code + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + message: + type: string + description: A human-readable representation of the error. + target: + type: string + description: The target of the error. + details: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: An array of details about specific errors that led to this reported error. + x-ms-identifiers: [ ] + innererror: + $ref: '#/components/schemas/DocumentIntelligenceInnerError' + description: The error object. + DocumentIntelligenceErrorResponse: + required: + - error + type: object + properties: + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Error response object. + DocumentIntelligenceInnerError: + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + message: + type: string + description: A human-readable representation of the error. + innererror: + $ref: '#/components/schemas/DocumentIntelligenceInnerError' + description: An object containing more specific information about the error. + DocumentIntelligenceOperationDetails: + required: + - operationId + - status + - createdDateTime + - lastUpdatedDateTime + - kind + - resourceLocation + type: object + properties: + operationId: + type: string + description: Operation ID + x-ms-mutability: + - read + - create + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + percentCompleted: + maximum: 100 + minimum: 0 + type: integer + description: Operation progress (0-100). + format: int32 + createdDateTime: + type: string + description: Date and time (UTC) when the operation was created. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + kind: + $ref: '#/components/schemas/OperationKind' + resourceLocation: + type: string + description: URL of the resource targeted by this operation. + format: uri + apiVersion: + type: string + description: API version used to create this operation. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Operation info. + discriminator: + propertyName: kind + DocumentIntelligenceOperationStatus: + enum: + - notStarted + - running + - failed + - succeeded + - canceled + - skipped + + type: string + description: Operation status. + x-ms-enum: + name: DocumentIntelligenceOperationStatus + modelAsString: true + values: + - name: notStarted + value: notStarted + description: The operation has not started yet. + - name: running + value: running + description: The operation is in progress. + - name: failed + value: failed + description: The operation has failed. + - name: succeeded + value: succeeded + description: The operation has succeeded. + - name: canceled + value: canceled + description: The operation has been canceled. + - name: skipped + value: skipped + description: The operation has been skipped. + DocumentIntelligenceResourceDetails: + required: + - customDocumentModels + type: object + properties: + customDocumentModels: + $ref: '#/components/schemas/CustomDocumentModelsDetails' + description: General information regarding the current resource. + DocumentIntelligenceWarning: + required: + - code + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of warning codes. + message: + type: string + description: A human-readable representation of the warning. + target: + type: string + description: The target of the error. + description: The error object. + DocumentKeyValueElement: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Concatenated content of the key-value element in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the key-value element. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the key-value element in the reading order concatenated content. + x-ms-identifiers: [ ] + description: An object representing the field key or value in a key-value pair. + DocumentKeyValuePair: + required: + - key + - confidence + type: object + properties: + key: + $ref: '#/components/schemas/DocumentKeyValueElement' + value: + $ref: '#/components/schemas/DocumentKeyValueElement' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the key-value pair. + format: float + description: "An object representing a form field with distinct field label (key) and field\nvalue (may be empty)." + DocumentLanguage: + required: + - locale + - spans + - confidence + type: object + properties: + locale: + type: string + description: "Detected language. Value may an ISO 639-1 language code (ex. \"en\", \"fr\")\nor BCP 47 language tag (ex. \"zh-Hans\")." + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: "Location of the text elements in the concatenated content the language applies\nto." + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly identifying the language. + format: float + description: An object representing the detected language for a given text span. + DocumentLine: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Concatenated content of the contained elements in reading order. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the line, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the line in the reading order concatenated content. + x-ms-identifiers: [ ] + description: "A content line object consisting of an adjacent sequence of content elements,\nsuch as words and selection marks." + DocumentModelBuildOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelBuild + DocumentModelComposeOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelCompose + DocumentModelCopyToOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelCopyTo + DocumentModelDetails: + required: + - modelId + - createdDateTime + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + x-ms-mutability: + - read + - create + description: + maxLength: 4096 + type: string + description: Document model description. + createdDateTime: + type: string + description: Date and time (UTC) when the document model was created. + format: date-time + readOnly: true + expirationDateTime: + type: string + description: Date and time (UTC) when the document model will expire. + format: date-time + readOnly: true + modifiedDateTime: + type: string + description: Date and time (UTC) when the document model was last modified. + format: date-time + readOnly: true + apiVersion: + type: string + description: API version used to create this document model. + readOnly: true + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + classifierId: + type: string + description: 'For composed models, the custom classifier to split and classify the input file.' + split: + enum: + - auto + - none + - perPage + type: string + description: 'For composed models, the file splitting behavior.' + default: none + x-ms-enum: + name: SplitMode + modelAsString: true + values: + - name: auto + value: auto + description: Automatically split file into documents. + - name: none + value: none + description: Treat the entire file as a single document. + - name: perPage + value: perPage + description: Treat each page in the file as a separate document. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentTypeDetails' + description: Supported document types. + readOnly: true + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered while building the model. + readOnly: true + x-ms-identifiers: [ ] + trainingHours: + type: number + description: Number of V100-equivalent GPU hours consumed for model training. + format: float + readOnly: true + description: Document model info. + DocumentPage: + required: + - pageNumber + - spans + type: object + properties: + pageNumber: + minimum: 1 + type: integer + description: 1-based page number in the input document. + format: int32 + angle: + maximum: 180 + type: number + description: "The general orientation of the content in clockwise direction, measured in\ndegrees between (-180, 180]." + format: float + width: + minimum: 0 + type: number + description: 'The width of the image/PDF in pixels/inches, respectively.' + format: float + height: + minimum: 0 + type: number + description: 'The height of the image/PDF in pixels/inches, respectively.' + format: float + unit: + $ref: '#/components/schemas/LengthUnit' + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the page in the reading order concatenated content. + x-ms-identifiers: [ ] + words: + type: array + items: + $ref: '#/components/schemas/DocumentWord' + description: Extracted words from the page. + x-ms-identifiers: [ ] + selectionMarks: + type: array + items: + $ref: '#/components/schemas/DocumentSelectionMark' + description: Extracted selection marks from the page. + x-ms-identifiers: [ ] + lines: + type: array + items: + $ref: '#/components/schemas/DocumentLine' + description: "Extracted lines from the page, potentially containing both textual and visual\nelements." + x-ms-identifiers: [ ] + barcodes: + type: array + items: + $ref: '#/components/schemas/DocumentBarcode' + description: Extracted barcodes from the page. + x-ms-identifiers: [ ] + formulas: + type: array + items: + $ref: '#/components/schemas/DocumentFormula' + description: Extracted formulas from the page. + x-ms-identifiers: [ ] + description: Content and layout elements extracted from a page from the input. + DocumentParagraph: + required: + - content + - spans + type: object + properties: + role: + $ref: '#/components/schemas/ParagraphRole' + content: + type: string + description: Concatenated content of the paragraph in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the paragraph. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the paragraph in the reading order concatenated content. + x-ms-identifiers: [ ] + description: "A paragraph object consisting with contiguous lines generally with common\nalignment and spacing." + DocumentSection: + required: + - spans + type: object + properties: + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the section in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the section. + description: An object representing a section in the document. + DocumentSelectionMark: + required: + - state + - span + - confidence + type: object + properties: + state: + $ref: '#/components/schemas/DocumentSelectionMarkState' + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the selection mark, with coordinates specified relative\nto the top-left of the page. The numbers represent the x, y values of the\npolygon vertices, clockwise from the left (-180 degrees inclusive) relative\nto the element orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the selection mark. + format: float + description: "A selection mark object representing check boxes, radio buttons, and other\nelements indicating a selection." + DocumentSelectionMarkState: + enum: + - selected + - unselected + type: string + description: State of the selection mark. + x-ms-enum: + name: DocumentSelectionMarkState + modelAsString: true + values: + - name: selected + value: selected + description: "The selection mark is selected, often indicated by a check ✓ or cross X inside\nthe selection mark." + - name: unselected + value: unselected + description: The selection mark is not selected. + DocumentSignatureType: + enum: + - signed + - unsigned + type: string + description: Presence of signature. + x-ms-enum: + name: DocumentSignatureType + modelAsString: true + values: + - name: signed + value: signed + description: A signature is detected. + - name: unsigned + value: unsigned + description: No signatures are detected. + DocumentSpan: + required: + - offset + - length + type: object + properties: + offset: + minimum: 0 + type: integer + description: Zero-based index of the content represented by the span. + format: int32 + length: + minimum: 0 + type: integer + description: Number of characters in the content represented by the span. + format: int32 + description: "Contiguous region of the concatenated content property, specified as an offset\nand length." + DocumentStyle: + required: + - spans + - confidence + type: object + properties: + isHandwritten: + type: boolean + description: Is content handwritten? + similarFontFamily: + type: string + description: "Visually most similar font from among the set of supported font families, with\nfallback fonts following CSS convention (ex. 'Arial, sans-serif')." + fontStyle: + $ref: '#/components/schemas/DocumentFontStyle' + fontWeight: + $ref: '#/components/schemas/DocumentFontWeight' + color: + pattern: '^#[0-9a-f]{6}$' + type: string + description: 'Foreground color in #rrggbb hexadecimal format.' + backgroundColor: + pattern: '^#[0-9a-f]{6}$' + type: string + description: 'Background color in #rrggbb hexadecimal format..' + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the text elements in the concatenated content the style applies to. + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly identifying the style. + format: float + description: An object representing observed text styles. + DocumentTable: + required: + - rowCount + - columnCount + - cells + - spans + type: object + properties: + rowCount: + minimum: 1 + type: integer + description: Number of rows in the table. + format: int32 + columnCount: + minimum: 1 + type: integer + description: Number of columns in the table. + format: int32 + cells: + type: array + items: + $ref: '#/components/schemas/DocumentTableCell' + description: Cells contained within the table. + x-ms-identifiers: [ ] + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the table. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the table in the reading order concatenated content. + x-ms-identifiers: [ ] + caption: + $ref: '#/components/schemas/DocumentCaption' + footnotes: + type: array + items: + $ref: '#/components/schemas/DocumentFootnote' + description: List of footnotes associated with the table. + x-ms-identifiers: [ ] + description: A table object consisting table cells arranged in a rectangular layout. + DocumentTableCell: + required: + - rowIndex + - columnIndex + - content + - spans + type: object + properties: + kind: + enum: + - content + - rowHeader + - columnHeader + - stubHead + - description + type: string + description: Table cell kind. + default: content + x-ms-enum: + name: DocumentTableCellKind + modelAsString: true + values: + - name: content + value: content + description: Contains the main content/data. + - name: rowHeader + value: rowHeader + description: Describes the content of the row. + - name: columnHeader + value: columnHeader + description: Describes the content of the column. + - name: stubHead + value: stubHead + description: 'Describes the row headers, usually located at the top left corner of a table.' + - name: description + value: description + description: Describes the content in (parts of) the table. + rowIndex: + type: integer + description: Row index of the cell. + format: int32 + columnIndex: + type: integer + description: Column index of the cell. + format: int32 + rowSpan: + minimum: 1 + type: integer + description: Number of rows spanned by this cell. + format: int32 + default: 1 + columnSpan: + minimum: 1 + type: integer + description: Number of columns spanned by this cell. + format: int32 + default: 1 + content: + type: string + description: Concatenated content of the table cell in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the table cell. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the table cell in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the table cell. + description: An object representing the location and content of a table cell. + DocumentTypeDetails: + type: object + properties: + description: + maxLength: 4096 + type: string + description: Document model description. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + fieldSchema: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentFieldSchema' + description: Description of the document semantic schema using a JSON Schema style syntax. + fieldConfidence: + type: object + additionalProperties: + type: number + format: float + description: Estimated confidence for each field. + modelId: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Document model to use for analyzing documents with specified type. + confidenceThreshold: + type: number + description: Only perform analysis if docType confidence is above threshold. + format: float + features: + type: array + items: + $ref: '#/components/schemas/DocumentAnalysisFeature' + description: List of optional analysis features. + queryFields: + type: array + items: + type: string + description: 'List of additional fields to extract. Ex. "NumberOfGuests,StoreNumber"' + maxDocumentsToAnalyze: + type: integer + description: Maximum number of documents of specified type to analyze. Default=all. + format: int32 + description: Document type info. + DocumentWord: + required: + - content + - span + - confidence + type: object + properties: + content: + type: string + description: Text content of the word. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the word, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the word. + format: float + description: "A word object consisting of a contiguous sequence of characters. For non-space\ndelimited languages, such as Chinese, Japanese, and Korean, each character is\nrepresented as its own word." + LengthUnit: + enum: + - pixel + - inch + type: string + description: 'The unit used by the width, height, and polygon properties. For images, the unit is "pixel". For PDF, the unit is "inch".' + x-ms-enum: + name: LengthUnit + modelAsString: true + values: + - name: pixel + value: pixel + description: Length unit for image files. + - name: inch + value: inch + description: Length unit for PDF files. + ModelCopyAuthorization: + required: + - targetResourceId + - targetResourceRegion + - targetModelId + - targetModelLocation + - accessToken + - expirationDateTime + type: object + properties: + targetResourceId: + type: string + description: ID of the target Azure resource where the document model should be copied to. + targetResourceRegion: + type: string + description: "Location of the target Azure resource where the document model should be copied\nto." + targetModelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Identifier of the target document model. + targetModelLocation: + type: string + description: URL of the copied document model in the target account. + format: uri + accessToken: + type: string + description: Token used to authorize the request. + expirationDateTime: + type: string + description: Date/time when the access token expires. + format: date-time + description: "Authorization to copy a document model to the specified target resource and\nmodelId." + OperationKind: + enum: + - documentModelBuild + - documentModelCompose + - documentModelCopyTo + - documentClassifierCopyTo + - documentClassifierBuild + type: string + description: Type of operation. + x-ms-enum: + name: OperationKind + modelAsString: true + values: + - name: documentModelBuild + value: documentModelBuild + description: Build a new custom document model. + - name: documentModelCompose + value: documentModelCompose + description: Compose a new custom document model from existing models. + - name: documentModelCopyTo + value: documentModelCopyTo + description: "Copy an existing document model to potentially a different resource, region, or\nsubscription." + - name: documentClassifierCopyTo + value: documentClassifierCopyTo + description: "Copy an existing document classifier to potentially a different resource, region, or\nsubscription." + - name: documentClassifierBuild + value: documentClassifierBuild + description: Build a new custom classifier model. + PagedAnalyzeBatchOperation: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/AnalyzeBatchOperation' + description: The AnalyzeBatchOperation items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of AnalyzeBatchOperation items + PagedDocumentClassifierDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: The DocumentClassifierDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentClassifierDetails items + PagedDocumentIntelligenceOperationDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + description: The DocumentIntelligenceOperationDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentIntelligenceOperationDetails items + PagedDocumentModelDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentModelDetails' + description: The DocumentModelDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentModelDetails items + ParagraphRole: + enum: + - pageHeader + - pageFooter + - pageNumber + - title + - sectionHeading + - footnote + - formulaBlock + type: string + description: Semantic role of the paragraph. + x-ms-enum: + name: ParagraphRole + modelAsString: true + values: + - name: pageHeader + value: pageHeader + description: Text near the top edge of the page. + - name: pageFooter + value: pageFooter + description: Text near the bottom edge of the page. + - name: pageNumber + value: pageNumber + description: Page number. + - name: title + value: title + description: Top-level title describing the entire document. + - name: sectionHeading + value: sectionHeading + description: Sub heading describing a section of the document. + - name: footnote + value: footnote + description: A note usually placed after the main content on a page. + - name: formulaBlock + value: formulaBlock + description: 'A block of formulas, often with shared alignment.' + StringIndexType: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + description: Method used to compute string offset and length. + x-ms-enum: + name: StringIndexType + modelAsString: true + values: + - name: textElements + value: textElements + description: "User-perceived display character, or grapheme cluster, as defined by Unicode\n8.0.0." + - name: unicodeCodePoint + value: unicodeCodePoint + description: Character unit represented by a single unicode code point. Used by Python 3. + - name: utf16CodeUnit + value: utf16CodeUnit + description: "Character unit represented by a 16-bit Unicode code unit. Used by JavaScript,\nJava, and .NET." + Input: + type: object + DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ImagePngResp: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ApplicationJ: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationPdfResponse: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationJsonResponse: + type: file + securitySchemes: + apiKeyHeader: + type: apiKey + name: Ocp-Apim-Subscription-Key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.json new file mode 100644 index 000000000..0ffd9637c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.json @@ -0,0 +1,4807 @@ +{ + "swagger": "2.0", + "info": { + "title": "Document Intelligence", + "version": "2024-11-30", + "description": "Extracts content, layout, and structured data from documents.", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "x-ms-parameterized-host": { + "hostTemplate": "{endpoint}/documentintelligence", + "useSchemePrefix": false, + "parameters": [ + { + "name": "endpoint", + "in": "path", + "description": "The Document Intelligence service endpoint.", + "required": true, + "type": "string", + "format": "uri", + "x-ms-skip-url-encoding": true + } + ] + }, + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "ApiKeyAuth": [] + }, + { + "OAuth2Auth": [ + "https://cognitiveservices.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Ocp-Apim-Subscription-Key", + "in": "header" + }, + "OAuth2Auth": { + "type": "oauth2", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://cognitiveservices.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "paths": { + "/documentClassifiers": { + "get": { + "operationId": "DocumentClassifiers_ListClassifiers", + "description": "List all document classifiers.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedDocumentClassifierDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Document Classifiers": { + "$ref": "./examples/GetDocumentClassifiers.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/documentClassifiers/{classifierId}": { + "get": { + "operationId": "DocumentClassifiers_GetClassifier", + "description": "Gets detailed document classifier information.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "classifierId", + "in": "path", + "description": "Unique document classifier name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/DocumentClassifierDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Document Classifier": { + "$ref": "./examples/GetDocumentClassifier.json" + } + } + }, + "delete": { + "operationId": "DocumentClassifiers_DeleteClassifier", + "description": "Deletes document classifier.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "classifierId", + "in": "path", + "description": "Unique document classifier name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "204": { + "description": "There is no content to send for this request, but the headers may be useful. ", + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Delete Document Classifier": { + "$ref": "./examples/DeleteDocumentClassifier.json" + } + } + } + }, + "/documentClassifiers/{classifierId}:analyze": { + "post": { + "operationId": "DocumentClassifiers_ClassifyDocumentFromStream", + "description": "Classifies document with document classifier. This operation can be used in two ways:\n1. With a binary document stream in the request body (current endpoint)\n2. With a ClassifyDocumentRequest object containing URL or base64-encoded document (add '_overload=classifyDocument' query parameter)", + "consumes": [ + "application/octet-stream", + "application/pdf", + "image/jpeg", + "image/png", + "image/tiff", + "image/bmp", + "image/heif", + "text/html", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/DocumentClassifierAnalyzeRequestParams.classifierId" + }, + { + "$ref": "#/parameters/DocumentClassifierAnalyzeRequestParams.stringIndexType" + }, + { + "$ref": "#/parameters/DocumentClassifierAnalyzeRequestParams.split" + }, + { + "$ref": "#/parameters/DocumentClassifierAnalyzeRequestParams.pages" + }, + { + "name": "_overload", + "in": "query", + "description": "Optional parameter to specify the operation variant. Use 'classifyDocument' for URL or base64 encoded document input.", + "required": false, + "type": "string", + "enum": ["classifyDocument"] + }, + { + "name": "input", + "in": "body", + "description": "Input content. Can be either a binary document stream or a ClassifyDocumentRequest object depending on the _overload parameter.", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of AnalyzeOperation" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Classify Document from Stream": { + "$ref": "./examples/ClassifyDocument_Stream.json" + }, + "Classify Document from Url": { + "$ref": "./examples/ClassifyDocument_Url.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentClassifiers/{classifierId}:copyTo": { + "post": { + "operationId": "DocumentClassifiers_CopyClassifierTo", + "description": "Copies document classifier to the target resource, region, and classifierId.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "classifierId", + "in": "path", + "description": "Unique document classifier name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "copyToRequest", + "in": "body", + "description": "Copy to request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/ClassifierCopyAuthorization" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of DocumentClassifierCopyToOperationDetails" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Copy Document Classifier To": { + "$ref": "./examples/CopyDocumentClassifierTo.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentClassifiers/{classifierId}/analyzeResults/{resultId}": { + "get": { + "operationId": "DocumentClassifiers_GetClassifyResult", + "description": "Gets the result of document classifier.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "classifierId", + "in": "path", + "description": "Unique document classifier name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/AnalyzeOperation" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Classify Document Result": { + "$ref": "./examples/GetClassifyDocumentResult.json" + } + } + } + }, + "/documentClassifiers:authorizeCopy": { + "post": { + "operationId": "DocumentClassifiers_AuthorizeClassifierCopy", + "description": "Generates authorization to copy a document classifier to this location with\nspecified classifierId and optional description.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "authorizeCopyRequest", + "in": "body", + "description": "Authorize copy request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/AuthorizeClassifierCopyRequest" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/ClassifierCopyAuthorization" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Authorize Copy of Document Classifier": { + "$ref": "./examples/AuthorizeCopyDocumentClassifier.json" + } + } + } + }, + "/documentClassifiers:build": { + "post": { + "operationId": "DocumentClassifiers_BuildClassifier", + "description": "Builds a custom document classifier.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "buildRequest", + "in": "body", + "description": "Build request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/BuildDocumentClassifierRequest" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of DocumentClassifierBuildOperationDetails" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Build Document Classifier": { + "$ref": "./examples/BuildDocumentClassifier.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentModels": { + "get": { + "operationId": "DocumentModels_ListModels", + "description": "List all document models", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedDocumentModelDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Document Models": { + "$ref": "./examples/GetDocumentModels.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/documentModels/{modelId}:analyze": { + "post": { + "operationId": "DocumentModels_AnalyzeDocument", + "description": "Analyzes document with document model. This operation can be used in two ways:\n1. With a binary document stream in the request body (when _overload is not specified)\n2. With an AnalyzeDocumentRequest object containing URL or base64-encoded document (add '_overload=analyzeDocument' query parameter)", + "consumes": [ + "application/octet-stream", + "application/pdf", + "image/jpeg", + "image/png", + "image/tiff", + "image/bmp", + "image/heif", + "text/html", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.modelId" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.pages" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.locale" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.stringIndexType" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.features" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.queryFields" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.outputContentFormat" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.output" + }, + { + "name": "_overload", + "in": "query", + "description": "Optional parameter to specify the operation variant. Use 'analyzeDocument' for URL or base64 encoded document input.", + "required": false, + "type": "string", + "enum": ["analyzeDocument"] + }, + { + "name": "input", + "in": "body", + "description": "Input content. Can be either a binary document stream or an AnalyzeDocumentRequest object depending on the _overload parameter.", + "required": true, + "schema": { + "type": "object" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of AnalyzeOperation" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Analyze Document from Stream": { + "$ref": "./examples/AnalyzeDocument_Stream.json" + }, + "Analyze Document from Base64": { + "$ref": "./examples/AnalyzeDocument_Base64.json" + }, + "Analyze Document from Url": { + "$ref": "./examples/AnalyzeDocument_Url.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentModels/{modelId}:analyzeBatch": { + "post": { + "operationId": "DocumentModels_AnalyzeBatchDocuments", + "description": "Analyzes batch documents with document model.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.modelId" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.pages" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.locale" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.stringIndexType" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.features" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.queryFields" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.outputContentFormat" + }, + { + "$ref": "#/parameters/DocumentModelAnalyzeRequestParams.output" + }, + { + "name": "analyzeBatchRequest", + "in": "body", + "description": "Analyze batch request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/AnalyzeBatchDocumentsRequest" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of AnalyzeBatchOperation" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Analyze Batch Documents": { + "$ref": "./examples/AnalyzeBatchDocuments.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentModels/{modelId}": { + "get": { + "operationId": "DocumentModels_GetModel", + "description": "Gets detailed document model information.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/DocumentModelDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Custom Document Model": { + "$ref": "./examples/GetDocumentModel_Custom.json" + }, + "Get Prebuilt Document Model": { + "$ref": "./examples/GetDocumentModel_Prebuilt.json" + } + } + }, + "delete": { + "operationId": "DocumentModels_DeleteModel", + "description": "Deletes document model.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "204": { + "description": "There is no content to send for this request, but the headers may be useful. ", + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Delete Document Model": { + "$ref": "./examples/DeleteDocumentModel.json" + } + } + } + }, + "/documentModels/{modelId}:copyTo": { + "post": { + "operationId": "DocumentModels_CopyModelTo", + "description": "Copies document model to the target resource, region, and modelId.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "copyToRequest", + "in": "body", + "description": "Copy to request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/ModelCopyAuthorization" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of DocumentModelCopyToOperationDetails" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Copy Document Model To": { + "$ref": "./examples/CopyDocumentModelTo.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentModels/{modelId}/analyzeBatchResults": { + "get": { + "operationId": "DocumentModels_ListAnalyzeBatchResults", + "description": "List batch document analysis results.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedAnalyzeBatchOperation" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "List Analyze Batch Documents Results": { + "$ref": "./examples/GetAnalyzeBatchDocumentsResults.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/documentModels/{modelId}/analyzeBatchResults/{resultId}": { + "get": { + "operationId": "DocumentModels_GetAnalyzeBatchResult", + "description": "Gets the result of batch document analysis.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze batch operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/AnalyzeBatchOperation" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Analyze Batch Documents Result": { + "$ref": "./examples/GetAnalyzeBatchDocumentsResult.json" + } + } + }, + "delete": { + "operationId": "DocumentModels_DeleteAnalyzeBatchResult", + "description": "Mark the batch document analysis result for deletion.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze batch operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "There is no content to send for this request, but the headers may be useful." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Delete Analyze Batch Documents Result": { + "$ref": "./examples/DeleteAnalyzeBatchDocumentsResult.json" + } + } + } + }, + "/documentModels/{modelId}/analyzeResults/{resultId}": { + "get": { + "operationId": "DocumentModels_GetAnalyzeResult", + "description": "Gets the result of document analysis.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/AnalyzeOperation" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Analyze Document Result": { + "$ref": "./examples/GetAnalyzeDocumentResult.json" + } + } + }, + "delete": { + "operationId": "DocumentModels_DeleteAnalyzeResult", + "description": "Mark the result of document analysis for deletion.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "There is no content to send for this request, but the headers may be useful." + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Delete Analyze Result": { + "$ref": "./examples/DeleteAnalyzeDocumentResult.json" + } + } + } + }, + "/documentModels/{modelId}/analyzeResults/{resultId}/figures/{figureId}": { + "get": { + "operationId": "DocumentModels_GetAnalyzeResultFigure", + "description": "Gets the generated cropped image of specified figure from document analysis.", + "produces": [ + "image/png", + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "figureId", + "in": "path", + "description": "Figure ID.", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "file" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Analyze Document Result Figure": { + "$ref": "./examples/GetAnalyzeDocumentResultFigure.json" + } + } + } + }, + "/documentModels/{modelId}/analyzeResults/{resultId}/pdf": { + "get": { + "operationId": "DocumentModels_GetAnalyzeResultPdf", + "description": "Gets the generated searchable PDF output from document analysis.", + "produces": [ + "application/pdf", + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + { + "name": "resultId", + "in": "path", + "description": "Analyze operation result ID.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "type": "file" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Analyze Document Result PDF": { + "$ref": "./examples/GetAnalyzeDocumentResultFPdf.json" + } + } + } + }, + "/documentModels:authorizeCopy": { + "post": { + "operationId": "DocumentModels_AuthorizeModelCopy", + "description": "Generates authorization to copy a document model to this location with\nspecified modelId and optional description.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "authorizeCopyRequest", + "in": "body", + "description": "Authorize copy request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/AuthorizeCopyRequest" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/ModelCopyAuthorization" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Authorize Copy of Document Model": { + "$ref": "./examples/AuthorizeCopyDocumentModel.json" + } + } + } + }, + "/documentModels:build": { + "post": { + "operationId": "DocumentModels_BuildModel", + "description": "Builds a custom document analysis model.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "buildRequest", + "in": "body", + "description": "Build request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/BuildDocumentModelRequest" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of DocumentModelBuildOperationDetails" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Build Document Model": { + "$ref": "./examples/BuildDocumentModel.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/documentModels:compose": { + "post": { + "operationId": "DocumentModels_ComposeModel", + "description": "Creates a new document model from document types of existing document models.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "composeRequest", + "in": "body", + "description": "Compose request parameters.", + "required": true, + "schema": { + "$ref": "#/definitions/ComposeDocumentModelRequest" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location of an instance of DocumentModelComposeOperationDetails" + }, + "Retry-After": { + "type": "integer", + "format": "int32", + "description": "The Retry-After header can indicate how long the client should wait before polling the operation status." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Compose Document Model": { + "$ref": "./examples/ComposeDocumentModel.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/info": { + "get": { + "operationId": "MiscellaneousOperations_GetResourceDetails", + "description": "Return information about the current resource.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceResourceDetails" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Resource Details": { + "$ref": "./examples/GetResourceDetails.json" + } + } + } + }, + "/operations": { + "get": { + "operationId": "MiscellaneousOperations_ListOperations", + "description": "Lists all operations.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/PagedDocumentIntelligenceOperationDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Operations": { + "$ref": "./examples/GetOperations.json" + } + }, + "x-ms-pageable": { + "nextLinkName": "nextLink" + } + } + }, + "/operations/{operationId}": { + "get": { + "operationId": "MiscellaneousOperations_GetOperation", + "description": "Gets operation info. Different operation types can be retrieved:\n1. General operation: no _overload parameter needed\n2. Document Model Build operation: use _overload=getDocumentModelBuildOperation\n3. Document Model Compose operation: use _overload=getDocumentModelComposeOperation\n4. Document Model Copy To operation: use _overload=getDocumentModelCopyToOperation\n5. Document Classifier Copy To operation: use _overload=getDocumentClassifierCopyToOperation\n6. Document Classifier Build operation: use _overload=getDocumentClassifierBuildOperation", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "operationId", + "in": "path", + "description": "Operation ID", + "required": true, + "type": "string" + }, + { + "name": "_overload", + "in": "query", + "description": "Optional parameter to specify the operation type to retrieve.", + "required": false, + "type": "string", + "enum": [ + "getOperation", + "getDocumentModelBuildOperation", + "getDocumentModelComposeOperation", + "getDocumentModelCopyToOperation", + "getDocumentClassifierCopyToOperation", + "getDocumentClassifierBuildOperation" + ] + }, + { + "$ref": "#/parameters/Azure.Core.ClientRequestIdHeader" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + }, + "headers": { + "x-ms-client-request-id": { + "type": "string", + "format": "uuid", + "description": "An opaque, globally-unique, client-generated string identifier for the request." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/DocumentIntelligenceErrorResponse" + } + } + }, + "x-ms-examples": { + "Get Operation": { + "$ref": "./examples/GetOperation.json" + }, + "Get Operation - Document Model Build": { + "$ref": "./examples/GetOperation_DocumentModelBuild.json" + }, + "Get Operation - Document Model Compose": { + "$ref": "./examples/GetOperation_DocumentModelCompose.json" + }, + "Get Operation - Document Model Copy To": { + "$ref": "./examples/GetOperation_DocumentModelCopyTo.json" + }, + "Get Operation - Document Classifier Copy To": { + "$ref": "./examples/GetOperation_DocumentClassifierCopyTo.json" + }, + "Get Operation - Document Classifier Build": { + "$ref": "./examples/GetOperation_DocumentClassifierBuild.json" + } + } + } + } + }, + "x-ms-paths": {}, + "definitions": { + "AddressValue": { + "type": "object", + "description": "Address field value.", + "properties": { + "houseNumber": { + "type": "string", + "description": "House or building number." + }, + "poBox": { + "type": "string", + "description": "Post office box number." + }, + "road": { + "type": "string", + "description": "Street name." + }, + "city": { + "type": "string", + "description": "Name of city, town, village, etc." + }, + "state": { + "type": "string", + "description": "First-level administrative division." + }, + "postalCode": { + "type": "string", + "description": "Postal code used for mail sorting." + }, + "countryRegion": { + "type": "string", + "description": "Country/region." + }, + "streetAddress": { + "type": "string", + "description": "Street-level address, excluding city, state, countryRegion, and postalCode." + }, + "unit": { + "type": "string", + "description": "Apartment or office number" + }, + "cityDistrict": { + "type": "string", + "description": "Districts or boroughs within a city, such as Brooklyn in New York City or City\nof Westminster in London." + }, + "stateDistrict": { + "type": "string", + "description": "Second-level administrative division used in certain locales." + }, + "suburb": { + "type": "string", + "description": "Unofficial neighborhood name, like Chinatown." + }, + "house": { + "type": "string", + "description": "Build name, such as World Trade Center." + }, + "level": { + "type": "string", + "description": "Floor number, such as 3F." + } + } + }, + "AnalyzeBatchDocumentsRequest": { + "type": "object", + "description": "Batch document analysis parameters.", + "properties": { + "azureBlobSource": { + "$ref": "#/definitions/AzureBlobContentSource", + "description": "Azure Blob Storage location containing the batch documents. Either\nazureBlobSource or azureBlobFileListSource must be specified." + }, + "azureBlobFileListSource": { + "$ref": "#/definitions/AzureBlobFileListContentSource", + "description": "Azure Blob Storage file list specifying the batch documents. Either\nazureBlobSource or azureBlobFileListSource must be specified." + }, + "resultContainerUrl": { + "type": "string", + "format": "uri", + "description": "Azure Blob Storage container URL where analyze result files will be stored." + }, + "resultPrefix": { + "type": "string", + "description": "Blob name prefix of result files." + }, + "overwriteExisting": { + "type": "boolean", + "description": "Overwrite existing analyze result files?", + "default": false + } + }, + "required": [ + "resultContainerUrl" + ] + }, + "AnalyzeBatchOperation": { + "type": "object", + "description": "Status and result of the analyze batch operation.", + "properties": { + "resultId": { + "$ref": "#/definitions/Azure.Core.uuid", + "description": "Analyze batch operation result ID." + }, + "status": { + "$ref": "#/definitions/DocumentIntelligenceOperationStatus", + "description": "Operation status. notStarted, running, succeeded, or failed" + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the operation was submitted." + }, + "lastUpdatedDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the status was last updated." + }, + "percentCompleted": { + "type": "integer", + "format": "int32", + "description": "Operation progress (0-100).", + "minimum": 0, + "maximum": 100 + }, + "error": { + "$ref": "#/definitions/DocumentIntelligenceError", + "description": "Encountered error during batch document analysis." + }, + "result": { + "$ref": "#/definitions/AnalyzeBatchResult", + "description": "Batch document analysis result." + } + }, + "required": [ + "status", + "createdDateTime", + "lastUpdatedDateTime" + ] + }, + "AnalyzeBatchOperationDetail": { + "type": "object", + "description": "Operation detail for a document in a batch analysis.", + "properties": { + "status": { + "$ref": "#/definitions/DocumentIntelligenceOperationStatus", + "description": "Analyze status. succeeded, failed, or skipped" + }, + "sourceUrl": { + "type": "string", + "format": "uri", + "description": "URL of the source document." + }, + "resultUrl": { + "type": "string", + "format": "uri", + "description": "URL of the analyze result JSON." + }, + "error": { + "$ref": "#/definitions/DocumentIntelligenceError", + "description": "Encountered error." + } + }, + "required": [ + "status", + "sourceUrl" + ] + }, + "AnalyzeBatchResult": { + "type": "object", + "description": "Batch document analysis result.", + "properties": { + "succeededCount": { + "type": "integer", + "format": "int32", + "description": "Number of documents that completed with status succeeded." + }, + "failedCount": { + "type": "integer", + "format": "int32", + "description": "Number of documents that completed with status failed." + }, + "skippedCount": { + "type": "integer", + "format": "int32", + "description": "Number of documents that completed with status skipped." + }, + "details": { + "type": "array", + "description": "Operation detail for each document in the batch.", + "items": { + "$ref": "#/definitions/AnalyzeBatchOperationDetail" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "succeededCount", + "failedCount", + "skippedCount" + ] + }, + "AnalyzeDocumentRequest": { + "type": "object", + "description": "Document analysis parameters.", + "properties": { + "urlSource": { + "type": "string", + "format": "uri", + "description": "Document URL to analyze. Either urlSource or base64Source must be specified." + }, + "base64Source": { + "type": "string", + "format": "byte", + "description": "Base64 encoding of the document to analyze. Either urlSource or base64Source\nmust be specified." + } + } + }, + "AnalyzeOperation": { + "type": "object", + "description": "Status and result of the analyze operation.", + "properties": { + "status": { + "$ref": "#/definitions/DocumentIntelligenceOperationStatus", + "description": "Operation status. notStarted, running, succeeded, or failed" + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the analyze operation was submitted." + }, + "lastUpdatedDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the status was last updated." + }, + "error": { + "$ref": "#/definitions/DocumentIntelligenceError", + "description": "Encountered error during document analysis." + }, + "analyzeResult": { + "$ref": "#/definitions/AnalyzeResult", + "description": "Document analysis result." + } + }, + "required": [ + "status", + "createdDateTime", + "lastUpdatedDateTime" + ] + }, + "AnalyzeResult": { + "type": "object", + "description": "Document analysis result.", + "properties": { + "apiVersion": { + "type": "string", + "description": "API version used to produce this result." + }, + "modelId": { + "type": "string", + "description": "Document model ID used to produce this result.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "stringIndexType": { + "$ref": "#/definitions/StringIndexType", + "description": "Method used to compute string offset and length." + }, + "contentFormat": { + "$ref": "#/definitions/DocumentContentFormat", + "description": "Format of the analyze result top-level content." + }, + "content": { + "type": "string", + "description": "Concatenate string representation of all textual and visual elements in reading\norder." + }, + "pages": { + "type": "array", + "description": "Analyzed pages.", + "items": { + "$ref": "#/definitions/DocumentPage" + }, + "x-ms-identifiers": [] + }, + "paragraphs": { + "type": "array", + "description": "Extracted paragraphs.", + "items": { + "$ref": "#/definitions/DocumentParagraph" + }, + "x-ms-identifiers": [] + }, + "tables": { + "type": "array", + "description": "Extracted tables.", + "items": { + "$ref": "#/definitions/DocumentTable" + }, + "x-ms-identifiers": [] + }, + "figures": { + "type": "array", + "description": "Extracted figures.", + "items": { + "$ref": "#/definitions/DocumentFigure" + } + }, + "sections": { + "type": "array", + "description": "Extracted sections.", + "items": { + "$ref": "#/definitions/DocumentSection" + }, + "x-ms-identifiers": [] + }, + "keyValuePairs": { + "type": "array", + "description": "Extracted key-value pairs.", + "items": { + "$ref": "#/definitions/DocumentKeyValuePair" + }, + "x-ms-identifiers": [] + }, + "styles": { + "type": "array", + "description": "Extracted font styles.", + "items": { + "$ref": "#/definitions/DocumentStyle" + }, + "x-ms-identifiers": [] + }, + "languages": { + "type": "array", + "description": "Detected languages.", + "items": { + "$ref": "#/definitions/DocumentLanguage" + }, + "x-ms-identifiers": [] + }, + "documents": { + "type": "array", + "description": "Extracted documents.", + "items": { + "$ref": "#/definitions/AnalyzedDocument" + }, + "x-ms-identifiers": [] + }, + "warnings": { + "type": "array", + "description": "List of warnings encountered.", + "items": { + "$ref": "#/definitions/DocumentIntelligenceWarning" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "apiVersion", + "modelId", + "stringIndexType", + "content", + "pages" + ] + }, + "AnalyzedDocument": { + "type": "object", + "description": "An object describing the location and semantic content of a document.", + "properties": { + "docType": { + "type": "string", + "description": "Document type.", + "minLength": 2, + "maxLength": 64 + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the document.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the document in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "fields": { + "type": "object", + "description": "Dictionary of named field values.", + "additionalProperties": { + "$ref": "#/definitions/DocumentField" + } + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the document.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "docType", + "spans", + "confidence" + ] + }, + "AuthorizeClassifierCopyRequest": { + "type": "object", + "description": "Request body to authorize document classifier copy.", + "properties": { + "classifierId": { + "type": "string", + "description": "Unique document classifier name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "description": { + "type": "string", + "description": "Document classifier description.", + "maxLength": 4096 + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document classifier.", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "classifierId" + ] + }, + "AuthorizeCopyRequest": { + "type": "object", + "description": "Request body to authorize document model copy.", + "properties": { + "modelId": { + "type": "string", + "description": "Unique document model name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "description": { + "type": "string", + "description": "Document model description.", + "maxLength": 4096 + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document model.", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "modelId" + ] + }, + "Azure.Core.uuid": { + "type": "string", + "format": "uuid", + "description": "Universally Unique Identifier" + }, + "AzureBlobContentSource": { + "type": "object", + "description": "Azure Blob Storage content.", + "properties": { + "containerUrl": { + "type": "string", + "format": "uri", + "description": "Azure Blob Storage container URL." + }, + "prefix": { + "type": "string", + "description": "Blob name prefix." + } + }, + "required": [ + "containerUrl" + ] + }, + "AzureBlobFileListContentSource": { + "type": "object", + "description": "File list in Azure Blob Storage.", + "properties": { + "containerUrl": { + "type": "string", + "format": "uri", + "description": "Azure Blob Storage container URL." + }, + "fileList": { + "type": "string", + "description": "Path to a JSONL file within the container specifying a subset of documents." + } + }, + "required": [ + "containerUrl", + "fileList" + ] + }, + "BoundingRegion": { + "type": "object", + "description": "Bounding polygon on a specific page of the input.", + "properties": { + "pageNumber": { + "type": "integer", + "format": "int32", + "description": "1-based page number of page containing the bounding region.", + "minimum": 1 + }, + "polygon": { + "type": "array", + "description": "Bounding polygon on the page, or the entire page if not specified.\nCoordinates specified relative to the top-left of the page. The numbers\nrepresent the x, y values of the polygon vertices, clockwise from the left\n(-180 degrees inclusive) relative to the element orientation.", + "items": { + "type": "number", + "format": "float" + } + } + }, + "required": [ + "pageNumber", + "polygon" + ] + }, + "BuildDocumentClassifierRequest": { + "type": "object", + "description": "Request body to build a new custom document classifier.", + "properties": { + "classifierId": { + "type": "string", + "description": "Unique document classifier name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "description": { + "type": "string", + "description": "Document classifier description.", + "maxLength": 4096 + }, + "baseClassifierId": { + "type": "string", + "description": "Base classifierId on top of which to train the classifier.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "docTypes": { + "type": "object", + "description": "List of document types to classify against.", + "additionalProperties": { + "$ref": "#/definitions/ClassifierDocumentTypeDetails" + } + }, + "allowOverwrite": { + "type": "boolean", + "description": "Allow overwriting an existing classifier with the same name.", + "default": false + } + }, + "required": [ + "classifierId", + "docTypes" + ] + }, + "BuildDocumentModelRequest": { + "type": "object", + "description": "Request body to build a new custom document model.", + "properties": { + "modelId": { + "type": "string", + "description": "Unique document model name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "description": { + "type": "string", + "description": "Document model description.", + "maxLength": 4096 + }, + "buildMode": { + "$ref": "#/definitions/DocumentBuildMode", + "description": "Custom document model build mode." + }, + "azureBlobSource": { + "$ref": "#/definitions/AzureBlobContentSource", + "description": "Azure Blob Storage location containing the training data. Either\nazureBlobSource or azureBlobFileListSource must be specified." + }, + "azureBlobFileListSource": { + "$ref": "#/definitions/AzureBlobFileListContentSource", + "description": "Azure Blob Storage file list specifying the training data. Either\nazureBlobSource or azureBlobFileListSource must be specified." + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document model.", + "additionalProperties": { + "type": "string" + } + }, + "maxTrainingHours": { + "type": "number", + "format": "float", + "description": "Max number of V100-equivalent GPU hours to use for model training. Default=0.5.", + "default": 0.5 + }, + "allowOverwrite": { + "type": "boolean", + "description": "Allow overwriting an existing model with the same name.", + "default": false + } + }, + "required": [ + "modelId", + "buildMode" + ] + }, + "ClassifierCopyAuthorization": { + "type": "object", + "description": "Authorization to copy a document classifier to the specified target resource and\nclassifierId.", + "properties": { + "targetResourceId": { + "type": "string", + "description": "ID of the target Azure resource where the document classifier should be copied to." + }, + "targetResourceRegion": { + "type": "string", + "description": "Location of the target Azure resource where the document classifier should be copied\nto." + }, + "targetClassifierId": { + "type": "string", + "description": "Identifier of the target document classifier.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "targetClassifierLocation": { + "type": "string", + "format": "uri", + "description": "URL of the copied document classifier in the target account." + }, + "accessToken": { + "type": "string", + "description": "Token used to authorize the request." + }, + "expirationDateTime": { + "type": "string", + "format": "date-time", + "description": "Date/time when the access token expires." + } + }, + "required": [ + "targetResourceId", + "targetResourceRegion", + "targetClassifierId", + "targetClassifierLocation", + "accessToken", + "expirationDateTime" + ] + }, + "ClassifierDocumentTypeDetails": { + "type": "object", + "description": "Classifier document type info.", + "properties": { + "sourceKind": { + "$ref": "#/definitions/ContentSourceKind", + "description": "Type of training data source." + }, + "azureBlobSource": { + "$ref": "#/definitions/AzureBlobContentSource", + "description": "Azure Blob Storage location containing the training data for a classifier\ndocument type. Either azureBlobSource or azureBlobFileListSource must be\nspecified." + }, + "azureBlobFileListSource": { + "$ref": "#/definitions/AzureBlobFileListContentSource", + "description": "Azure Blob Storage file list specifying the training data for a classifier\ndocument type. Either azureBlobSource or azureBlobFileListSource must be\nspecified." + } + } + }, + "ClassifyDocumentRequest": { + "type": "object", + "description": "Document classification parameters.", + "properties": { + "urlSource": { + "type": "string", + "format": "uri", + "description": "Document URL to classify. Either urlSource or base64Source must be specified." + }, + "base64Source": { + "type": "string", + "format": "byte", + "description": "Base64 encoding of the document to classify. Either urlSource or base64Source\nmust be specified." + } + } + }, + "ComposeDocumentModelRequest": { + "type": "object", + "description": "Request body to create a composed document model from component document models.", + "properties": { + "modelId": { + "type": "string", + "description": "Unique document model name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "description": { + "type": "string", + "description": "Document model description.", + "maxLength": 4096 + }, + "classifierId": { + "type": "string", + "description": "Custom classifier to split and classify the input file." + }, + "split": { + "type": "string", + "description": "File splitting behavior.", + "default": "none", + "enum": [ + "auto", + "none", + "perPage" + ], + "x-ms-enum": { + "name": "SplitMode", + "modelAsString": true, + "values": [ + { + "name": "auto", + "value": "auto", + "description": "Automatically split file into documents." + }, + { + "name": "none", + "value": "none", + "description": "Treat the entire file as a single document." + }, + { + "name": "perPage", + "value": "perPage", + "description": "Treat each page in the file as a separate document." + } + ] + } + }, + "docTypes": { + "type": "object", + "description": "Dictionary mapping supported docTypes to the corresponding document models.", + "additionalProperties": { + "$ref": "#/definitions/DocumentTypeDetails" + } + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document model.", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "modelId", + "classifierId", + "docTypes" + ] + }, + "ContentSourceKind": { + "type": "string", + "description": "Type of content source.", + "enum": [ + "url", + "base64", + "azureBlob", + "azureBlobFileList" + ], + "x-ms-enum": { + "name": "ContentSourceKind", + "modelAsString": true, + "values": [ + { + "name": "url", + "value": "url", + "description": "Content at a specific URL." + }, + { + "name": "base64", + "value": "base64", + "description": "Content represented via Base64 encoding." + }, + { + "name": "azureBlob", + "value": "azureBlob", + "description": "Files in a path within an Azure Blob Storage container." + }, + { + "name": "azureBlobFileList", + "value": "azureBlobFileList", + "description": "A file list specifying individual files in an Azure Blob Storage container." + } + ] + } + }, + "CurrencyValue": { + "type": "object", + "description": "Currency field value.", + "properties": { + "amount": { + "type": "number", + "format": "double", + "description": "Currency amount." + }, + "currencySymbol": { + "type": "string", + "description": "Currency symbol label, if any." + }, + "currencyCode": { + "type": "string", + "description": "Resolved currency code (ISO 4217), if any." + } + }, + "required": [ + "amount" + ] + }, + "CustomDocumentModelsDetails": { + "type": "object", + "description": "Details regarding custom document models.", + "properties": { + "count": { + "type": "integer", + "format": "int32", + "description": "Number of custom document models in the current resource." + }, + "limit": { + "type": "integer", + "format": "int32", + "description": "Maximum number of custom document models supported in the current resource." + } + }, + "required": [ + "count", + "limit" + ] + }, + "DocumentAnalysisFeature": { + "type": "string", + "description": "Document analysis features to enable.", + "enum": [ + "ocrHighResolution", + "languages", + "barcodes", + "formulas", + "keyValuePairs", + "styleFont", + "queryFields" + ], + "x-ms-enum": { + "name": "DocumentAnalysisFeature", + "modelAsString": true, + "values": [ + { + "name": "ocrHighResolution", + "value": "ocrHighResolution", + "description": "Perform OCR at a higher resolution to handle documents with fine print." + }, + { + "name": "languages", + "value": "languages", + "description": "Enable the detection of the text content language." + }, + { + "name": "barcodes", + "value": "barcodes", + "description": "Enable the detection of barcodes in the document." + }, + { + "name": "formulas", + "value": "formulas", + "description": "Enable the detection of mathematical expressions in the document." + }, + { + "name": "keyValuePairs", + "value": "keyValuePairs", + "description": "Enable the detection of general key value pairs (form fields) in the document." + }, + { + "name": "styleFont", + "value": "styleFont", + "description": "Enable the recognition of various font styles." + }, + { + "name": "queryFields", + "value": "queryFields", + "description": "Enable the extraction of additional fields via the queryFields query parameter." + } + ] + } + }, + "DocumentBarcode": { + "type": "object", + "description": "A barcode object.", + "properties": { + "kind": { + "$ref": "#/definitions/DocumentBarcodeKind", + "description": "Barcode kind." + }, + "value": { + "type": "string", + "description": "Barcode value." + }, + "polygon": { + "type": "array", + "description": "Bounding polygon of the barcode, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation.", + "items": { + "type": "number", + "format": "float" + } + }, + "span": { + "$ref": "#/definitions/DocumentSpan", + "description": "Location of the barcode in the reading order concatenated content." + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the barcode.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "kind", + "value", + "span", + "confidence" + ] + }, + "DocumentBarcodeKind": { + "type": "string", + "description": "Barcode kind.", + "enum": [ + "QRCode", + "PDF417", + "UPCA", + "UPCE", + "Code39", + "Code128", + "EAN8", + "EAN13", + "DataBar", + "Code93", + "Codabar", + "DataBarExpanded", + "ITF", + "MicroQRCode", + "Aztec", + "DataMatrix", + "MaxiCode" + ], + "x-ms-enum": { + "name": "DocumentBarcodeKind", + "modelAsString": true, + "values": [ + { + "name": "QRCode", + "value": "QRCode", + "description": "QR code, as defined in ISO/IEC 18004:2015." + }, + { + "name": "PDF417", + "value": "PDF417", + "description": "PDF417, as defined in ISO 15438." + }, + { + "name": "UPCA", + "value": "UPCA", + "description": "GS1 12-digit Universal Product Code." + }, + { + "name": "UPCE", + "value": "UPCE", + "description": "GS1 6-digit Universal Product Code." + }, + { + "name": "Code39", + "value": "Code39", + "description": "Code 39 barcode, as defined in ISO/IEC 16388:2007." + }, + { + "name": "Code128", + "value": "Code128", + "description": "Code 128 barcode, as defined in ISO/IEC 15417:2007." + }, + { + "name": "EAN8", + "value": "EAN8", + "description": "GS1 8-digit International Article Number (European Article Number)." + }, + { + "name": "EAN13", + "value": "EAN13", + "description": "GS1 13-digit International Article Number (European Article Number)." + }, + { + "name": "DataBar", + "value": "DataBar", + "description": "GS1 DataBar barcode." + }, + { + "name": "Code93", + "value": "Code93", + "description": "Code 93 barcode, as defined in ANSI/AIM BC5-1995." + }, + { + "name": "Codabar", + "value": "Codabar", + "description": "Codabar barcode, as defined in ANSI/AIM BC3-1995." + }, + { + "name": "DataBarExpanded", + "value": "DataBarExpanded", + "description": "GS1 DataBar Expanded barcode." + }, + { + "name": "ITF", + "value": "ITF", + "description": "Interleaved 2 of 5 barcode, as defined in ANSI/AIM BC2-1995." + }, + { + "name": "MicroQRCode", + "value": "MicroQRCode", + "description": "Micro QR code, as defined in ISO/IEC 23941:2022." + }, + { + "name": "Aztec", + "value": "Aztec", + "description": "Aztec code, as defined in ISO/IEC 24778:2008." + }, + { + "name": "DataMatrix", + "value": "DataMatrix", + "description": "Data matrix code, as defined in ISO/IEC 16022:2006." + }, + { + "name": "MaxiCode", + "value": "MaxiCode", + "description": "MaxiCode, as defined in ISO/IEC 16023:2000." + } + ] + } + }, + "DocumentBuildMode": { + "type": "string", + "description": "Custom document model build mode.", + "enum": [ + "template", + "neural" + ], + "x-ms-enum": { + "name": "DocumentBuildMode", + "modelAsString": true, + "values": [ + { + "name": "template", + "value": "template", + "description": "Target documents with similar visual templates." + }, + { + "name": "neural", + "value": "neural", + "description": "Support documents with diverse visual templates." + } + ] + } + }, + "DocumentCaption": { + "type": "object", + "description": "A caption object describing a table or figure.", + "properties": { + "content": { + "type": "string", + "description": "Content of the caption." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the caption.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the caption in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "elements": { + "type": "array", + "description": "Child elements of the caption.", + "items": { + "$ref": "#/definitions/DocumentElement" + } + } + }, + "required": [ + "content", + "spans" + ] + }, + "DocumentClassifierBuildOperationDetails": { + "type": "object", + "description": "Get Operation response object.", + "properties": { + "result": { + "$ref": "#/definitions/DocumentClassifierDetails", + "description": "Operation result upon success." + } + }, + "allOf": [ + { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + } + ], + "x-ms-discriminator-value": "documentClassifierBuild" + }, + "DocumentClassifierCopyToOperationDetails": { + "type": "object", + "description": "Get Operation response object.", + "properties": { + "result": { + "$ref": "#/definitions/DocumentClassifierDetails", + "description": "Operation result upon success." + } + }, + "allOf": [ + { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + } + ], + "x-ms-discriminator-value": "documentClassifierCopyTo" + }, + "DocumentClassifierDetails": { + "type": "object", + "description": "Document classifier info.", + "properties": { + "classifierId": { + "type": "string", + "description": "Unique document classifier name.", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$", + "x-ms-mutability": [ + "read", + "create" + ] + }, + "description": { + "type": "string", + "description": "Document classifier description.", + "maxLength": 4096 + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document classifier was created." + }, + "expirationDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document classifier will expire." + }, + "modifiedDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document model was last modified.", + "readOnly": true + }, + "apiVersion": { + "type": "string", + "description": "API version used to create this document classifier." + }, + "baseClassifierId": { + "type": "string", + "description": "Base classifierId on top of which the classifier was trained.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "docTypes": { + "type": "object", + "description": "List of document types to classify against.", + "additionalProperties": { + "$ref": "#/definitions/ClassifierDocumentTypeDetails" + } + }, + "warnings": { + "type": "array", + "description": "List of warnings encountered while building the classifier.", + "items": { + "$ref": "#/definitions/DocumentIntelligenceWarning" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "classifierId", + "createdDateTime", + "apiVersion", + "docTypes" + ] + }, + "DocumentContentFormat": { + "type": "string", + "description": "Format of the content in analyzed result.", + "enum": [ + "text", + "markdown" + ], + "x-ms-enum": { + "name": "DocumentContentFormat", + "modelAsString": true, + "values": [ + { + "name": "text", + "value": "text", + "description": "Plain text representation of the document content without any formatting." + }, + { + "name": "markdown", + "value": "markdown", + "description": "Markdown representation of the document content with section headings, tables,\netc." + } + ] + } + }, + "DocumentElement": { + "type": "string", + "description": "An JSON pointer referencing an element of the document, such as paragraph,\ntable, section, etc." + }, + "DocumentField": { + "type": "object", + "description": "An object representing the content and location of a field value.", + "properties": { + "type": { + "$ref": "#/definitions/DocumentFieldType", + "description": "Data type of the field value." + }, + "valueString": { + "type": "string", + "description": "String value." + }, + "valueDate": { + "type": "string", + "format": "date", + "description": "Date value in YYYY-MM-DD format (ISO 8601)." + }, + "valueTime": { + "type": "string", + "format": "time", + "description": "Time value in hh:mm:ss format (ISO 8601)." + }, + "valuePhoneNumber": { + "type": "string", + "description": "Phone number value in E.164 format (ex. +19876543210)." + }, + "valueNumber": { + "type": "number", + "format": "double", + "description": "Floating point value." + }, + "valueInteger": { + "type": "integer", + "format": "int64", + "description": "Integer value." + }, + "valueSelectionMark": { + "$ref": "#/definitions/DocumentSelectionMarkState", + "description": "Selection mark value." + }, + "valueSignature": { + "$ref": "#/definitions/DocumentSignatureType", + "description": "Presence of signature." + }, + "valueCountryRegion": { + "type": "string", + "description": "3-letter country code value (ISO 3166-1 alpha-3)." + }, + "valueArray": { + "type": "array", + "description": "Array of field values.", + "items": { + "$ref": "#/definitions/DocumentField" + }, + "x-ms-identifiers": [] + }, + "valueObject": { + "type": "object", + "description": "Dictionary of named field values.", + "additionalProperties": { + "$ref": "#/definitions/DocumentField" + } + }, + "valueCurrency": { + "$ref": "#/definitions/CurrencyValue", + "description": "Currency value." + }, + "valueAddress": { + "$ref": "#/definitions/AddressValue", + "description": "Address value." + }, + "valueBoolean": { + "type": "boolean", + "description": "Boolean value." + }, + "valueSelectionGroup": { + "type": "array", + "description": "Selection group value.", + "items": { + "type": "string" + } + }, + "content": { + "type": "string", + "description": "Field content." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the field.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the field in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the field.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "type" + ] + }, + "DocumentFieldSchema": { + "type": "object", + "description": "Description of the field semantic schema using a JSON Schema style syntax.", + "properties": { + "type": { + "$ref": "#/definitions/DocumentFieldType", + "description": "Semantic data type of the field value." + }, + "description": { + "type": "string", + "description": "Field description.", + "maxLength": 4096 + }, + "example": { + "type": "string", + "description": "Example field content." + }, + "items": { + "$ref": "#/definitions/DocumentFieldSchema", + "description": "Field type schema of each array element." + }, + "properties": { + "type": "object", + "description": "Named sub-fields of the object field.", + "additionalProperties": { + "$ref": "#/definitions/DocumentFieldSchema" + } + } + }, + "required": [ + "type" + ] + }, + "DocumentFieldType": { + "type": "string", + "description": "Semantic data type of the field value.", + "enum": [ + "string", + "date", + "time", + "phoneNumber", + "number", + "integer", + "selectionMark", + "countryRegion", + "signature", + "array", + "object", + "currency", + "address", + "boolean", + "selectionGroup" + ], + "x-ms-enum": { + "name": "DocumentFieldType", + "modelAsString": true, + "values": [ + { + "name": "string", + "value": "string", + "description": "Plain text." + }, + { + "name": "date", + "value": "date", + "description": "Date, normalized to ISO 8601 (YYYY-MM-DD) format." + }, + { + "name": "time", + "value": "time", + "description": "Time, normalized to ISO 8601 (hh:mm:ss) format." + }, + { + "name": "phoneNumber", + "value": "phoneNumber", + "description": "Phone number, normalized to E.164 (+{CountryCode}{SubscriberNumber}) format." + }, + { + "name": "number", + "value": "number", + "description": "Floating point number, normalized to double precision floating point." + }, + { + "name": "integer", + "value": "integer", + "description": "Integer number, normalized to 64-bit signed integer." + }, + { + "name": "selectionMark", + "value": "selectionMark", + "description": "Is field selected?" + }, + { + "name": "countryRegion", + "value": "countryRegion", + "description": "Country/region, normalized to ISO 3166-1 alpha-3 format (ex. USA)." + }, + { + "name": "signature", + "value": "signature", + "description": "Is signature present?" + }, + { + "name": "array", + "value": "array", + "description": "List of subfields of the same type." + }, + { + "name": "object", + "value": "object", + "description": "Named list of subfields of potentially different types." + }, + { + "name": "currency", + "value": "currency", + "description": "Currency amount with optional currency symbol and unit." + }, + { + "name": "address", + "value": "address", + "description": "Parsed address." + }, + { + "name": "boolean", + "value": "boolean", + "description": "Boolean value, normalized to true or false." + }, + { + "name": "selectionGroup", + "value": "selectionGroup", + "description": "Array of selected string values." + } + ] + } + }, + "DocumentFigure": { + "type": "object", + "description": "An object representing a figure in the document.", + "properties": { + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the figure.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the figure in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "elements": { + "type": "array", + "description": "Child elements of the figure, excluding any caption or footnotes.", + "items": { + "$ref": "#/definitions/DocumentElement" + } + }, + "caption": { + "$ref": "#/definitions/DocumentCaption", + "description": "Caption associated with the figure." + }, + "footnotes": { + "type": "array", + "description": "List of footnotes associated with the figure.", + "items": { + "$ref": "#/definitions/DocumentFootnote" + }, + "x-ms-identifiers": [] + }, + "id": { + "type": "string", + "description": "Figure ID." + } + }, + "required": [ + "spans" + ] + }, + "DocumentFontStyle": { + "type": "string", + "description": "Font style.", + "enum": [ + "normal", + "italic" + ], + "x-ms-enum": { + "name": "DocumentFontStyle", + "modelAsString": true, + "values": [ + { + "name": "normal", + "value": "normal", + "description": "Characters are represented normally." + }, + { + "name": "italic", + "value": "italic", + "description": "Characters are visually slanted to the right." + } + ] + } + }, + "DocumentFontWeight": { + "type": "string", + "description": "Font weight.", + "enum": [ + "normal", + "bold" + ], + "x-ms-enum": { + "name": "DocumentFontWeight", + "modelAsString": true, + "values": [ + { + "name": "normal", + "value": "normal", + "description": "Characters are represented normally." + }, + { + "name": "bold", + "value": "bold", + "description": "Characters are represented with thicker strokes." + } + ] + } + }, + "DocumentFootnote": { + "type": "object", + "description": "A footnote object describing a table or figure.", + "properties": { + "content": { + "type": "string", + "description": "Content of the footnote." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the footnote.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the footnote in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "elements": { + "type": "array", + "description": "Child elements of the footnote.", + "items": { + "$ref": "#/definitions/DocumentElement" + } + } + }, + "required": [ + "content", + "spans" + ] + }, + "DocumentFormula": { + "type": "object", + "description": "A formula object.", + "properties": { + "kind": { + "$ref": "#/definitions/DocumentFormulaKind", + "description": "Formula kind." + }, + "value": { + "type": "string", + "description": "LaTex expression describing the formula." + }, + "polygon": { + "type": "array", + "description": "Bounding polygon of the formula, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation.", + "items": { + "type": "number", + "format": "float" + } + }, + "span": { + "$ref": "#/definitions/DocumentSpan", + "description": "Location of the formula in the reading order concatenated content." + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the formula.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "kind", + "value", + "span", + "confidence" + ] + }, + "DocumentFormulaKind": { + "type": "string", + "description": "Formula kind.", + "enum": [ + "inline", + "display" + ], + "x-ms-enum": { + "name": "DocumentFormulaKind", + "modelAsString": true, + "values": [ + { + "name": "inline", + "value": "inline", + "description": "A formula embedded within the content of a paragraph." + }, + { + "name": "display", + "value": "display", + "description": "A formula in display mode that takes up an entire line." + } + ] + } + }, + "DocumentIntelligenceError": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/DocumentIntelligenceError" + }, + "x-ms-identifiers": [] + }, + "innererror": { + "$ref": "#/definitions/DocumentIntelligenceInnerError", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "DocumentIntelligenceErrorResponse": { + "type": "object", + "description": "Error response object.", + "properties": { + "error": { + "$ref": "#/definitions/DocumentIntelligenceError", + "description": "Error info." + } + }, + "required": [ + "error" + ] + }, + "DocumentIntelligenceInnerError": { + "type": "object", + "description": "An object containing more specific information about the error.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "innererror": { + "$ref": "#/definitions/DocumentIntelligenceInnerError", + "description": "Inner error." + } + } + }, + "DocumentIntelligenceOperationDetails": { + "type": "object", + "description": "Operation info.", + "properties": { + "operationId": { + "type": "string", + "description": "Operation ID", + "x-ms-mutability": [ + "read", + "create" + ] + }, + "status": { + "$ref": "#/definitions/DocumentIntelligenceOperationStatus", + "description": "Operation status. notStarted, running, completed, or failed" + }, + "percentCompleted": { + "type": "integer", + "format": "int32", + "description": "Operation progress (0-100).", + "minimum": 0, + "maximum": 100 + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the operation was created." + }, + "lastUpdatedDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the status was last updated." + }, + "kind": { + "$ref": "#/definitions/OperationKind", + "description": "Type of operation." + }, + "resourceLocation": { + "type": "string", + "format": "uri", + "description": "URL of the resource targeted by this operation." + }, + "apiVersion": { + "type": "string", + "description": "API version used to create this operation." + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document model.", + "additionalProperties": { + "type": "string" + } + }, + "error": { + "$ref": "#/definitions/DocumentIntelligenceError", + "description": "Encountered error." + } + }, + "discriminator": "kind", + "required": [ + "operationId", + "status", + "createdDateTime", + "lastUpdatedDateTime", + "kind", + "resourceLocation" + ] + }, + "DocumentIntelligenceOperationStatus": { + "type": "string", + "description": "Operation status.", + "enum": [ + "notStarted", + "running", + "failed", + "succeeded", + "canceled", + "skipped" + ], + "x-ms-enum": { + "name": "DocumentIntelligenceOperationStatus", + "modelAsString": true, + "values": [ + { + "name": "notStarted", + "value": "notStarted", + "description": "The operation has not started yet." + }, + { + "name": "running", + "value": "running", + "description": "The operation is in progress." + }, + { + "name": "failed", + "value": "failed", + "description": "The operation has failed." + }, + { + "name": "succeeded", + "value": "succeeded", + "description": "The operation has succeeded." + }, + { + "name": "canceled", + "value": "canceled", + "description": "The operation has been canceled." + }, + { + "name": "skipped", + "value": "skipped", + "description": "The operation has been skipped." + } + ] + } + }, + "DocumentIntelligenceResourceDetails": { + "type": "object", + "description": "General information regarding the current resource.", + "properties": { + "customDocumentModels": { + "$ref": "#/definitions/CustomDocumentModelsDetails", + "description": "Details regarding custom document models." + } + }, + "required": [ + "customDocumentModels" + ] + }, + "DocumentIntelligenceWarning": { + "type": "object", + "description": "The error object.", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of warning codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the warning." + }, + "target": { + "type": "string", + "description": "The target of the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "DocumentKeyValueElement": { + "type": "object", + "description": "An object representing the field key or value in a key-value pair.", + "properties": { + "content": { + "type": "string", + "description": "Concatenated content of the key-value element in reading order." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the key-value element.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the key-value element in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "content", + "spans" + ] + }, + "DocumentKeyValuePair": { + "type": "object", + "description": "An object representing a form field with distinct field label (key) and field\nvalue (may be empty).", + "properties": { + "key": { + "$ref": "#/definitions/DocumentKeyValueElement", + "description": "Field label of the key-value pair." + }, + "value": { + "$ref": "#/definitions/DocumentKeyValueElement", + "description": "Field value of the key-value pair." + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the key-value pair.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "key", + "confidence" + ] + }, + "DocumentLanguage": { + "type": "object", + "description": "An object representing the detected language for a given text span.", + "properties": { + "locale": { + "type": "string", + "description": "Detected language. Value may an ISO 639-1 language code (ex. \"en\", \"fr\")\nor BCP 47 language tag (ex. \"zh-Hans\")." + }, + "spans": { + "type": "array", + "description": "Location of the text elements in the concatenated content the language applies\nto.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly identifying the language.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "locale", + "spans", + "confidence" + ] + }, + "DocumentLine": { + "type": "object", + "description": "A content line object consisting of an adjacent sequence of content elements,\nsuch as words and selection marks.", + "properties": { + "content": { + "type": "string", + "description": "Concatenated content of the contained elements in reading order." + }, + "polygon": { + "type": "array", + "description": "Bounding polygon of the line, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation.", + "items": { + "type": "number", + "format": "float" + } + }, + "spans": { + "type": "array", + "description": "Location of the line in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "content", + "spans" + ] + }, + "DocumentModelBuildOperationDetails": { + "type": "object", + "description": "Get Operation response object.", + "properties": { + "result": { + "$ref": "#/definitions/DocumentModelDetails", + "description": "Operation result upon success." + } + }, + "allOf": [ + { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + } + ], + "x-ms-discriminator-value": "documentModelBuild" + }, + "DocumentModelComposeOperationDetails": { + "type": "object", + "description": "Get Operation response object.", + "properties": { + "result": { + "$ref": "#/definitions/DocumentModelDetails", + "description": "Operation result upon success." + } + }, + "allOf": [ + { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + } + ], + "x-ms-discriminator-value": "documentModelCompose" + }, + "DocumentModelCopyToOperationDetails": { + "type": "object", + "description": "Get Operation response object.", + "properties": { + "result": { + "$ref": "#/definitions/DocumentModelDetails", + "description": "Operation result upon success." + } + }, + "allOf": [ + { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + } + ], + "x-ms-discriminator-value": "documentModelCopyTo" + }, + "DocumentModelDetails": { + "type": "object", + "description": "Document model info.", + "properties": { + "modelId": { + "type": "string", + "description": "Unique document model name.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$", + "x-ms-mutability": [ + "read", + "create" + ] + }, + "description": { + "type": "string", + "description": "Document model description.", + "maxLength": 4096 + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document model was created.", + "readOnly": true + }, + "expirationDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document model will expire.", + "readOnly": true + }, + "modifiedDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time (UTC) when the document model was last modified.", + "readOnly": true + }, + "apiVersion": { + "type": "string", + "description": "API version used to create this document model.", + "readOnly": true + }, + "tags": { + "type": "object", + "description": "List of key-value tag attributes associated with the document model.", + "additionalProperties": { + "type": "string" + } + }, + "buildMode": { + "$ref": "#/definitions/DocumentBuildMode", + "description": "Custom document model build mode.", + "readOnly": true + }, + "azureBlobSource": { + "$ref": "#/definitions/AzureBlobContentSource", + "description": "Azure Blob Storage location containing the training data. Either\nazureBlobSource or azureBlobFileListSource must be specified.", + "readOnly": true + }, + "azureBlobFileListSource": { + "$ref": "#/definitions/AzureBlobFileListContentSource", + "description": "Azure Blob Storage file list specifying the training data. Either\nazureBlobSource or azureBlobFileListSource must be specified.", + "readOnly": true + }, + "classifierId": { + "type": "string", + "description": "For composed models, the custom classifier to split and classify the input file." + }, + "split": { + "type": "string", + "description": "For composed models, the file splitting behavior.", + "default": "none", + "enum": [ + "auto", + "none", + "perPage" + ], + "x-ms-enum": { + "name": "SplitMode", + "modelAsString": true, + "values": [ + { + "name": "auto", + "value": "auto", + "description": "Automatically split file into documents." + }, + { + "name": "none", + "value": "none", + "description": "Treat the entire file as a single document." + }, + { + "name": "perPage", + "value": "perPage", + "description": "Treat each page in the file as a separate document." + } + ] + } + }, + "docTypes": { + "type": "object", + "description": "Supported document types.", + "additionalProperties": { + "$ref": "#/definitions/DocumentTypeDetails" + }, + "readOnly": true + }, + "warnings": { + "type": "array", + "description": "List of warnings encountered while building the model.", + "items": { + "$ref": "#/definitions/DocumentIntelligenceWarning" + }, + "readOnly": true, + "x-ms-identifiers": [] + }, + "trainingHours": { + "type": "number", + "format": "float", + "description": "Number of V100-equivalent GPU hours consumed for model training.", + "readOnly": true + } + }, + "required": [ + "modelId", + "createdDateTime" + ] + }, + "DocumentPage": { + "type": "object", + "description": "Content and layout elements extracted from a page from the input.", + "properties": { + "pageNumber": { + "type": "integer", + "format": "int32", + "description": "1-based page number in the input document.", + "minimum": 1 + }, + "angle": { + "type": "number", + "format": "float", + "description": "The general orientation of the content in clockwise direction, measured in\ndegrees between (-180, 180].", + "maximum": 180 + }, + "width": { + "type": "number", + "format": "float", + "description": "The width of the image/PDF in pixels/inches, respectively.", + "minimum": 0 + }, + "height": { + "type": "number", + "format": "float", + "description": "The height of the image/PDF in pixels/inches, respectively.", + "minimum": 0 + }, + "unit": { + "$ref": "#/definitions/LengthUnit", + "description": "The unit used by the width, height, and polygon properties. For images, the\nunit is \"pixel\". For PDF, the unit is \"inch\"." + }, + "spans": { + "type": "array", + "description": "Location of the page in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "words": { + "type": "array", + "description": "Extracted words from the page.", + "items": { + "$ref": "#/definitions/DocumentWord" + }, + "x-ms-identifiers": [] + }, + "selectionMarks": { + "type": "array", + "description": "Extracted selection marks from the page.", + "items": { + "$ref": "#/definitions/DocumentSelectionMark" + }, + "x-ms-identifiers": [] + }, + "lines": { + "type": "array", + "description": "Extracted lines from the page, potentially containing both textual and visual\nelements.", + "items": { + "$ref": "#/definitions/DocumentLine" + }, + "x-ms-identifiers": [] + }, + "barcodes": { + "type": "array", + "description": "Extracted barcodes from the page.", + "items": { + "$ref": "#/definitions/DocumentBarcode" + }, + "x-ms-identifiers": [] + }, + "formulas": { + "type": "array", + "description": "Extracted formulas from the page.", + "items": { + "$ref": "#/definitions/DocumentFormula" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "pageNumber", + "spans" + ] + }, + "DocumentParagraph": { + "type": "object", + "description": "A paragraph object consisting with contiguous lines generally with common\nalignment and spacing.", + "properties": { + "role": { + "$ref": "#/definitions/ParagraphRole", + "description": "Semantic role of the paragraph." + }, + "content": { + "type": "string", + "description": "Concatenated content of the paragraph in reading order." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the paragraph.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the paragraph in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "content", + "spans" + ] + }, + "DocumentSection": { + "type": "object", + "description": "An object representing a section in the document.", + "properties": { + "spans": { + "type": "array", + "description": "Location of the section in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "elements": { + "type": "array", + "description": "Child elements of the section.", + "items": { + "$ref": "#/definitions/DocumentElement" + } + } + }, + "required": [ + "spans" + ] + }, + "DocumentSelectionMark": { + "type": "object", + "description": "A selection mark object representing check boxes, radio buttons, and other\nelements indicating a selection.", + "properties": { + "state": { + "$ref": "#/definitions/DocumentSelectionMarkState", + "description": "State of the selection mark." + }, + "polygon": { + "type": "array", + "description": "Bounding polygon of the selection mark, with coordinates specified relative\nto the top-left of the page. The numbers represent the x, y values of the\npolygon vertices, clockwise from the left (-180 degrees inclusive) relative\nto the element orientation.", + "items": { + "type": "number", + "format": "float" + } + }, + "span": { + "$ref": "#/definitions/DocumentSpan", + "description": "Location of the selection mark in the reading order concatenated content." + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the selection mark.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "state", + "span", + "confidence" + ] + }, + "DocumentSelectionMarkState": { + "type": "string", + "description": "State of the selection mark.", + "enum": [ + "selected", + "unselected" + ], + "x-ms-enum": { + "name": "DocumentSelectionMarkState", + "modelAsString": true, + "values": [ + { + "name": "selected", + "value": "selected", + "description": "The selection mark is selected, often indicated by a check ✓ or cross X inside\nthe selection mark." + }, + { + "name": "unselected", + "value": "unselected", + "description": "The selection mark is not selected." + } + ] + } + }, + "DocumentSignatureType": { + "type": "string", + "description": "Presence of signature.", + "enum": [ + "signed", + "unsigned" + ], + "x-ms-enum": { + "name": "DocumentSignatureType", + "modelAsString": true, + "values": [ + { + "name": "signed", + "value": "signed", + "description": "A signature is detected." + }, + { + "name": "unsigned", + "value": "unsigned", + "description": "No signatures are detected." + } + ] + } + }, + "DocumentSpan": { + "type": "object", + "description": "Contiguous region of the concatenated content property, specified as an offset\nand length.", + "properties": { + "offset": { + "type": "integer", + "format": "int32", + "description": "Zero-based index of the content represented by the span.", + "minimum": 0 + }, + "length": { + "type": "integer", + "format": "int32", + "description": "Number of characters in the content represented by the span.", + "minimum": 0 + } + }, + "required": [ + "offset", + "length" + ] + }, + "DocumentStyle": { + "type": "object", + "description": "An object representing observed text styles.", + "properties": { + "isHandwritten": { + "type": "boolean", + "description": "Is content handwritten?" + }, + "similarFontFamily": { + "type": "string", + "description": "Visually most similar font from among the set of supported font families, with\nfallback fonts following CSS convention (ex. 'Arial, sans-serif')." + }, + "fontStyle": { + "$ref": "#/definitions/DocumentFontStyle", + "description": "Font style." + }, + "fontWeight": { + "$ref": "#/definitions/DocumentFontWeight", + "description": "Font weight." + }, + "color": { + "type": "string", + "description": "Foreground color in #rrggbb hexadecimal format.", + "pattern": "^#[0-9a-f]{6}$" + }, + "backgroundColor": { + "type": "string", + "description": "Background color in #rrggbb hexadecimal format..", + "pattern": "^#[0-9a-f]{6}$" + }, + "spans": { + "type": "array", + "description": "Location of the text elements in the concatenated content the style applies to.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly identifying the style.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "spans", + "confidence" + ] + }, + "DocumentTable": { + "type": "object", + "description": "A table object consisting table cells arranged in a rectangular layout.", + "properties": { + "rowCount": { + "type": "integer", + "format": "int32", + "description": "Number of rows in the table.", + "minimum": 1 + }, + "columnCount": { + "type": "integer", + "format": "int32", + "description": "Number of columns in the table.", + "minimum": 1 + }, + "cells": { + "type": "array", + "description": "Cells contained within the table.", + "items": { + "$ref": "#/definitions/DocumentTableCell" + }, + "x-ms-identifiers": [] + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the table.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the table in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "caption": { + "$ref": "#/definitions/DocumentCaption", + "description": "Caption associated with the table." + }, + "footnotes": { + "type": "array", + "description": "List of footnotes associated with the table.", + "items": { + "$ref": "#/definitions/DocumentFootnote" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "rowCount", + "columnCount", + "cells", + "spans" + ] + }, + "DocumentTableCell": { + "type": "object", + "description": "An object representing the location and content of a table cell.", + "properties": { + "kind": { + "type": "string", + "description": "Table cell kind.", + "default": "content", + "enum": [ + "content", + "rowHeader", + "columnHeader", + "stubHead", + "description" + ], + "x-ms-enum": { + "name": "DocumentTableCellKind", + "modelAsString": true, + "values": [ + { + "name": "content", + "value": "content", + "description": "Contains the main content/data." + }, + { + "name": "rowHeader", + "value": "rowHeader", + "description": "Describes the content of the row." + }, + { + "name": "columnHeader", + "value": "columnHeader", + "description": "Describes the content of the column." + }, + { + "name": "stubHead", + "value": "stubHead", + "description": "Describes the row headers, usually located at the top left corner of a table." + }, + { + "name": "description", + "value": "description", + "description": "Describes the content in (parts of) the table." + } + ] + } + }, + "rowIndex": { + "type": "integer", + "format": "int32", + "description": "Row index of the cell." + }, + "columnIndex": { + "type": "integer", + "format": "int32", + "description": "Column index of the cell." + }, + "rowSpan": { + "type": "integer", + "format": "int32", + "description": "Number of rows spanned by this cell.", + "default": 1, + "minimum": 1 + }, + "columnSpan": { + "type": "integer", + "format": "int32", + "description": "Number of columns spanned by this cell.", + "default": 1, + "minimum": 1 + }, + "content": { + "type": "string", + "description": "Concatenated content of the table cell in reading order." + }, + "boundingRegions": { + "type": "array", + "description": "Bounding regions covering the table cell.", + "items": { + "$ref": "#/definitions/BoundingRegion" + }, + "x-ms-identifiers": [] + }, + "spans": { + "type": "array", + "description": "Location of the table cell in the reading order concatenated content.", + "items": { + "$ref": "#/definitions/DocumentSpan" + }, + "x-ms-identifiers": [] + }, + "elements": { + "type": "array", + "description": "Child elements of the table cell.", + "items": { + "$ref": "#/definitions/DocumentElement" + } + } + }, + "required": [ + "rowIndex", + "columnIndex", + "content", + "spans" + ] + }, + "DocumentTypeDetails": { + "type": "object", + "description": "Document type info.", + "properties": { + "description": { + "type": "string", + "description": "Document model description.", + "maxLength": 4096 + }, + "buildMode": { + "$ref": "#/definitions/DocumentBuildMode", + "description": "Custom document model build mode." + }, + "fieldSchema": { + "type": "object", + "description": "Description of the document semantic schema using a JSON Schema style syntax.", + "additionalProperties": { + "$ref": "#/definitions/DocumentFieldSchema" + } + }, + "fieldConfidence": { + "type": "object", + "description": "Estimated confidence for each field.", + "additionalProperties": { + "format": "float", + "type": "number" + } + }, + "modelId": { + "type": "string", + "description": "Document model to use for analyzing documents with specified type.", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "confidenceThreshold": { + "type": "number", + "format": "float", + "description": "Only perform analysis if docType confidence is above threshold." + }, + "features": { + "type": "array", + "description": "List of optional analysis features.", + "items": { + "$ref": "#/definitions/DocumentAnalysisFeature" + } + }, + "queryFields": { + "type": "array", + "description": "List of additional fields to extract. Ex. \"NumberOfGuests,StoreNumber\"", + "items": { + "type": "string" + } + }, + "maxDocumentsToAnalyze": { + "type": "integer", + "format": "int32", + "description": "Maximum number of documents of specified type to analyze. Default=all." + } + } + }, + "DocumentWord": { + "type": "object", + "description": "A word object consisting of a contiguous sequence of characters. For non-space\ndelimited languages, such as Chinese, Japanese, and Korean, each character is\nrepresented as its own word.", + "properties": { + "content": { + "type": "string", + "description": "Text content of the word." + }, + "polygon": { + "type": "array", + "description": "Bounding polygon of the word, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation.", + "items": { + "type": "number", + "format": "float" + } + }, + "span": { + "$ref": "#/definitions/DocumentSpan", + "description": "Location of the word in the reading order concatenated content." + }, + "confidence": { + "type": "number", + "format": "float", + "description": "Confidence of correctly extracting the word.", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "content", + "span", + "confidence" + ] + }, + "LengthUnit": { + "type": "string", + "description": "The unit used by the width, height, and polygon properties. For images, the unit is \"pixel\". For PDF, the unit is \"inch\".", + "enum": [ + "pixel", + "inch" + ], + "x-ms-enum": { + "name": "LengthUnit", + "modelAsString": true, + "values": [ + { + "name": "pixel", + "value": "pixel", + "description": "Length unit for image files." + }, + { + "name": "inch", + "value": "inch", + "description": "Length unit for PDF files." + } + ] + } + }, + "ModelCopyAuthorization": { + "type": "object", + "description": "Authorization to copy a document model to the specified target resource and\nmodelId.", + "properties": { + "targetResourceId": { + "type": "string", + "description": "ID of the target Azure resource where the document model should be copied to." + }, + "targetResourceRegion": { + "type": "string", + "description": "Location of the target Azure resource where the document model should be copied\nto." + }, + "targetModelId": { + "type": "string", + "description": "Identifier of the target document model.", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$" + }, + "targetModelLocation": { + "type": "string", + "format": "uri", + "description": "URL of the copied document model in the target account." + }, + "accessToken": { + "type": "string", + "description": "Token used to authorize the request." + }, + "expirationDateTime": { + "type": "string", + "format": "date-time", + "description": "Date/time when the access token expires." + } + }, + "required": [ + "targetResourceId", + "targetResourceRegion", + "targetModelId", + "targetModelLocation", + "accessToken", + "expirationDateTime" + ] + }, + "OperationKind": { + "type": "string", + "description": "Type of operation.", + "enum": [ + "documentModelBuild", + "documentModelCompose", + "documentModelCopyTo", + "documentClassifierCopyTo", + "documentClassifierBuild" + ], + "x-ms-enum": { + "name": "OperationKind", + "modelAsString": true, + "values": [ + { + "name": "documentModelBuild", + "value": "documentModelBuild", + "description": "Build a new custom document model." + }, + { + "name": "documentModelCompose", + "value": "documentModelCompose", + "description": "Compose a new custom document model from existing models." + }, + { + "name": "documentModelCopyTo", + "value": "documentModelCopyTo", + "description": "Copy an existing document model to potentially a different resource, region, or\nsubscription." + }, + { + "name": "documentClassifierCopyTo", + "value": "documentClassifierCopyTo", + "description": "Copy an existing document classifier to potentially a different resource, region, or\nsubscription." + }, + { + "name": "documentClassifierBuild", + "value": "documentClassifierBuild", + "description": "Build a new custom classifier model." + } + ] + } + }, + "PagedAnalyzeBatchOperation": { + "type": "object", + "description": "Paged collection of AnalyzeBatchOperation items", + "properties": { + "value": { + "type": "array", + "description": "The AnalyzeBatchOperation items on this page", + "items": { + "$ref": "#/definitions/AnalyzeBatchOperation" + }, + "x-ms-identifiers": [] + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "PagedDocumentClassifierDetails": { + "type": "object", + "description": "Paged collection of DocumentClassifierDetails items", + "properties": { + "value": { + "type": "array", + "description": "The DocumentClassifierDetails items on this page", + "items": { + "$ref": "#/definitions/DocumentClassifierDetails" + }, + "x-ms-identifiers": [] + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "PagedDocumentIntelligenceOperationDetails": { + "type": "object", + "description": "Paged collection of DocumentIntelligenceOperationDetails items", + "properties": { + "value": { + "type": "array", + "description": "The DocumentIntelligenceOperationDetails items on this page", + "items": { + "$ref": "#/definitions/DocumentIntelligenceOperationDetails" + }, + "x-ms-identifiers": [] + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "PagedDocumentModelDetails": { + "type": "object", + "description": "Paged collection of DocumentModelDetails items", + "properties": { + "value": { + "type": "array", + "description": "The DocumentModelDetails items on this page", + "items": { + "$ref": "#/definitions/DocumentModelDetails" + }, + "x-ms-identifiers": [] + }, + "nextLink": { + "type": "string", + "format": "uri", + "description": "The link to the next page of items" + } + }, + "required": [ + "value" + ] + }, + "ParagraphRole": { + "type": "string", + "description": "Semantic role of the paragraph.", + "enum": [ + "pageHeader", + "pageFooter", + "pageNumber", + "title", + "sectionHeading", + "footnote", + "formulaBlock" + ], + "x-ms-enum": { + "name": "ParagraphRole", + "modelAsString": true, + "values": [ + { + "name": "pageHeader", + "value": "pageHeader", + "description": "Text near the top edge of the page." + }, + { + "name": "pageFooter", + "value": "pageFooter", + "description": "Text near the bottom edge of the page." + }, + { + "name": "pageNumber", + "value": "pageNumber", + "description": "Page number." + }, + { + "name": "title", + "value": "title", + "description": "Top-level title describing the entire document." + }, + { + "name": "sectionHeading", + "value": "sectionHeading", + "description": "Sub heading describing a section of the document." + }, + { + "name": "footnote", + "value": "footnote", + "description": "A note usually placed after the main content on a page." + }, + { + "name": "formulaBlock", + "value": "formulaBlock", + "description": "A block of formulas, often with shared alignment." + } + ] + } + }, + "StringIndexType": { + "type": "string", + "description": "Method used to compute string offset and length.", + "enum": [ + "textElements", + "unicodeCodePoint", + "utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "textElements", + "value": "textElements", + "description": "User-perceived display character, or grapheme cluster, as defined by Unicode\n8.0.0." + }, + { + "name": "unicodeCodePoint", + "value": "unicodeCodePoint", + "description": "Character unit represented by a single unicode code point. Used by Python 3." + }, + { + "name": "utf16CodeUnit", + "value": "utf16CodeUnit", + "description": "Character unit represented by a 16-bit Unicode code unit. Used by JavaScript,\nJava, and .NET." + } + ] + } + } + }, + "parameters": { + "AnalyzeFromStreamRequestParams.input": { + "name": "input", + "in": "body", + "description": "Input content.", + "required": true, + "schema": { + "type": "string", + "format": "binary" + }, + "x-ms-parameter-location": "method" + }, + "Azure.Core.ClientRequestIdHeader": { + "name": "x-ms-client-request-id", + "in": "header", + "description": "An opaque, globally-unique, client-generated string identifier for the request.", + "required": false, + "type": "string", + "format": "uuid", + "x-ms-parameter-location": "method", + "x-ms-client-name": "clientRequestId" + }, + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + }, + "DocumentClassifierAnalyzeRequestParams.classifierId": { + "name": "classifierId", + "in": "path", + "description": "Unique document classifier name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$", + "x-ms-parameter-location": "method" + }, + "DocumentClassifierAnalyzeRequestParams.pages": { + "name": "pages", + "in": "query", + "description": "1-based page numbers to analyze. Ex. \"1-3,5,7-9\"", + "required": false, + "type": "string", + "pattern": "^(\\d+(-\\d+)?)(,\\s*(\\d+(-\\d+)?))*$", + "x-ms-parameter-location": "method" + }, + "DocumentClassifierAnalyzeRequestParams.split": { + "name": "split", + "in": "query", + "description": "Document splitting mode.", + "required": false, + "type": "string", + "default": "none", + "enum": [ + "auto", + "none", + "perPage" + ], + "x-ms-enum": { + "name": "SplitMode", + "modelAsString": true, + "values": [ + { + "name": "auto", + "value": "auto", + "description": "Automatically split file into documents." + }, + { + "name": "none", + "value": "none", + "description": "Treat the entire file as a single document." + }, + { + "name": "perPage", + "value": "perPage", + "description": "Treat each page in the file as a separate document." + } + ] + }, + "x-ms-parameter-location": "method" + }, + "DocumentClassifierAnalyzeRequestParams.stringIndexType": { + "name": "stringIndexType", + "in": "query", + "description": "Method used to compute string offset and length.", + "required": false, + "type": "string", + "default": "textElements", + "enum": [ + "textElements", + "unicodeCodePoint", + "utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "textElements", + "value": "textElements", + "description": "User-perceived display character, or grapheme cluster, as defined by Unicode\n8.0.0." + }, + { + "name": "unicodeCodePoint", + "value": "unicodeCodePoint", + "description": "Character unit represented by a single unicode code point. Used by Python 3." + }, + { + "name": "utf16CodeUnit", + "value": "utf16CodeUnit", + "description": "Character unit represented by a 16-bit Unicode code unit. Used by JavaScript,\nJava, and .NET." + } + ] + }, + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.features": { + "name": "features", + "in": "query", + "description": "List of optional analysis features.", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "ocrHighResolution", + "languages", + "barcodes", + "formulas", + "keyValuePairs", + "styleFont", + "queryFields" + ], + "x-ms-enum": { + "name": "DocumentAnalysisFeature", + "modelAsString": true, + "values": [ + { + "name": "ocrHighResolution", + "value": "ocrHighResolution", + "description": "Perform OCR at a higher resolution to handle documents with fine print." + }, + { + "name": "languages", + "value": "languages", + "description": "Enable the detection of the text content language." + }, + { + "name": "barcodes", + "value": "barcodes", + "description": "Enable the detection of barcodes in the document." + }, + { + "name": "formulas", + "value": "formulas", + "description": "Enable the detection of mathematical expressions in the document." + }, + { + "name": "keyValuePairs", + "value": "keyValuePairs", + "description": "Enable the detection of general key value pairs (form fields) in the document." + }, + { + "name": "styleFont", + "value": "styleFont", + "description": "Enable the recognition of various font styles." + }, + { + "name": "queryFields", + "value": "queryFields", + "description": "Enable the extraction of additional fields via the queryFields query parameter." + } + ] + } + }, + "collectionFormat": "csv", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.locale": { + "name": "locale", + "in": "query", + "description": "Locale hint for text recognition and document analysis. Value may contain only\nthe language code (ex. \"en\", \"fr\") or BCP 47 language tag (ex. \"en-US\").", + "required": false, + "type": "string", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.modelId": { + "name": "modelId", + "in": "path", + "description": "Unique document model name.", + "required": true, + "type": "string", + "maxLength": 64, + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.output": { + "name": "output", + "in": "query", + "description": "Additional outputs to generate during analysis.", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "pdf", + "figures" + ], + "x-ms-enum": { + "name": "AnalyzeOutputOption", + "modelAsString": true, + "values": [ + { + "name": "pdf", + "value": "pdf", + "description": "Generate searchable PDF output." + }, + { + "name": "figures", + "value": "figures", + "description": "Generate cropped images of detected figures." + } + ] + } + }, + "collectionFormat": "csv", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.outputContentFormat": { + "name": "outputContentFormat", + "in": "query", + "description": "Format of the analyze result top-level content.", + "required": false, + "type": "string", + "default": "text", + "enum": [ + "text", + "markdown" + ], + "x-ms-enum": { + "name": "DocumentContentFormat", + "modelAsString": true, + "values": [ + { + "name": "text", + "value": "text", + "description": "Plain text representation of the document content without any formatting." + }, + { + "name": "markdown", + "value": "markdown", + "description": "Markdown representation of the document content with section headings, tables,\netc." + } + ] + }, + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.pages": { + "name": "pages", + "in": "query", + "description": "1-based page numbers to analyze. Ex. \"1-3,5,7-9\"", + "required": false, + "type": "string", + "pattern": "^(\\d+(-\\d+)?)(,\\s*(\\d+(-\\d+)?))*$", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.queryFields": { + "name": "queryFields", + "in": "query", + "description": "List of additional fields to extract. Ex. \"NumberOfGuests,StoreNumber\"", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "x-ms-parameter-location": "method" + }, + "DocumentModelAnalyzeRequestParams.stringIndexType": { + "name": "stringIndexType", + "in": "query", + "description": "Method used to compute string offset and length.", + "required": false, + "type": "string", + "default": "textElements", + "enum": [ + "textElements", + "unicodeCodePoint", + "utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "textElements", + "value": "textElements", + "description": "User-perceived display character, or grapheme cluster, as defined by Unicode\n8.0.0." + }, + { + "name": "unicodeCodePoint", + "value": "unicodeCodePoint", + "description": "Character unit represented by a single unicode code point. Used by Python 3." + }, + { + "name": "utf16CodeUnit", + "value": "utf16CodeUnit", + "description": "Character unit represented by a 16-bit Unicode code unit. Used by JavaScript,\nJava, and .NET." + } + ] + }, + "x-ms-parameter-location": "method" + } + } + } \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.openapi.yaml new file mode 100644 index 000000000..280cb1ff3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/doc-intel-api/document-intelligence-2024-11-30.openapi.yaml @@ -0,0 +1,3712 @@ +openapi: 3.0.1 +info: + title: Azure Document Intelligence + description: 'Extracts content, layout, and structured data from documents.' + version: '1.0' +servers: + - url: https://REPLACED-BY-POLICY/documentintelligence +paths: + /documentClassifiers: + get: + summary: DocumentClassifiers_ListClassifiers + description: List all document classifiers. + operationId: DocumentClassifiers_ListClassifiers + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentClassifierDetails' + example: + value: + - classifierId: string + description: string + createdDateTime: string + expirationDateTime: string + modifiedDateTime: string + apiVersion: string + baseClassifierId: string + docTypes: { } + warnings: + - code: string + message: string + target: string + nextLink: string + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentClassifiers/{classifierId}': + get: + summary: DocumentClassifiers_GetClassifier + description: Gets detailed document classifier information. + operationId: DocumentClassifiers_GetClassifier + parameters: + - name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentClassifierDetails' + example: + classifierId: string + description: string + createdDateTime: string + expirationDateTime: string + modifiedDateTime: string + apiVersion: string + baseClassifierId: string + docTypes: { } + warnings: + - code: string + message: string + target: string + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + delete: + summary: DocumentClassifiers_DeleteClassifier + description: Deletes document classifier. + operationId: DocumentClassifiers_DeleteClassifier + parameters: + - name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentClassifiers/{classifierId}:analyze': + post: + summary: DocumentClassifiers_ClassifyDocumentFromStream + description: "Classifies document with document classifier. This operation can be used in two ways:\n1. With a binary document stream in the request body (current endpoint)\n2. With a ClassifyDocumentRequest object containing URL or base64-encoded document (add '_overload=classifyDocument' query parameter)" + operationId: DocumentClassifiers_ClassifyDocumentFromStream + parameters: + - name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + default: '2024-11-30' + type: string + - name: stringIndexType + in: query + description: Method used to compute string offset and length. + schema: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + default: textElements + - name: split + in: query + description: Document splitting mode. + schema: + enum: + - auto + - none + - perPage + type: string + default: none + - name: pages + in: query + description: '1-based page numbers to analyze. Ex. "1-3,5,7-9"' + schema: + pattern: '^(\d+(-\d+)?)(,\s*(\d+(-\d+)?))*$' + type: string + - name: _overload + in: query + description: Optional parameter to specify the operation variant. Use 'classifyDocument' for URL or base64 encoded document input. + schema: + enum: + - classifyDocument + type: string + requestBody: + description: Input content. Can be either a binary document stream or a ClassifyDocumentRequest object depending on the _overload parameter. + content: + application/octet-stream: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + application/pdf: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + image/jpeg: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + image/png: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + image/tiff: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + image/bmp: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + image/heif: + schema: + $ref: '#/components/schemas/Input' + examples: + default: + value: + text/html: + schema: + $ref: '#/components/schemas/Input' + example: + application/vnd.openxmlformats-officedocument.wordprocessingml.document: + schema: + $ref: '#/components/schemas/Input' + example: + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: + schema: + $ref: '#/components/schemas/Input' + example: + application/vnd.openxmlformats-officedocument.presentationml.presentation: + schema: + $ref: '#/components/schemas/Input' + example: + application/json: + schema: + $ref: '#/components/schemas/Input' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeOperation + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentClassifiers/{classifierId}:copyTo': + post: + summary: DocumentClassifiers_CopyClassifierTo + description: 'Copies document classifier to the target resource, region, and classifierId.' + operationId: DocumentClassifiers_CopyClassifierTo + parameters: + - name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Copy to request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ClassifierCopyAuthorization' + example: + targetResourceId: string + targetResourceRegion: string + targetClassifierId: string + targetClassifierLocation: string + accessToken: string + expirationDateTime: string + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentClassifierCopyToOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentClassifiers/{classifierId}/analyzeResults/{resultId}': + get: + summary: DocumentClassifiers_GetClassifyResult + description: Gets the result of document classifier. + operationId: DocumentClassifiers_GetClassifyResult + parameters: + - name: classifierId + in: path + description: Unique document classifier name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeOperation' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentClassifiers:authorizeCopy: + post: + summary: DocumentClassifiers_AuthorizeClassifierCopy + description: "Generates authorization to copy a document classifier to this location with\nspecified classifierId and optional description." + operationId: DocumentClassifiers_AuthorizeClassifierCopy + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Authorize copy request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizeClassifierCopyRequest' + example: + classifierId: string + description: string + tags: { } + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ClassifierCopyAuthorization' + example: + targetResourceId: string + targetResourceRegion: string + targetClassifierId: string + targetClassifierLocation: string + accessToken: string + expirationDateTime: string + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentClassifiers:build: + post: + summary: DocumentClassifiers_BuildClassifier + description: Builds a custom document classifier. + operationId: DocumentClassifiers_BuildClassifier + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Build request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/BuildDocumentClassifierRequest' + example: + classifierId: string + description: string + baseClassifierId: string + docTypes: { } + allowOverwrite: false + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentClassifierBuildOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentModels: + get: + summary: DocumentModels_ListModels + description: List all document models + operationId: DocumentModels_ListModels + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentModelDetails' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}:analyze': + post: + summary: DocumentModels_AnalyzeDocument + description: "Analyzes document with document model. This operation can be used in two ways:\n1. With a binary document stream in the request body (when _overload is not specified)\n2. With an AnalyzeDocumentRequest object containing URL or base64-encoded document (add '_overload=analyzeDocument' query parameter)" + operationId: DocumentModels_AnalyzeDocument + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + default: 'prebuilt-read' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: pages + in: query + description: '1-based page numbers to analyze. Ex. "1-3,5,7-9"' + schema: + pattern: '^(\d+(-\d+)?)(,\s*(\d+(-\d+)?))*$' + type: string + - name: locale + in: query + description: "Locale hint for text recognition and document analysis. Value may contain only\nthe language code (ex. \"en\", \"fr\") or BCP 47 language tag (ex. \"en-US\")." + schema: + type: string + - name: stringIndexType + in: query + description: Method used to compute string offset and length. + schema: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + default: textElements + - name: features + in: query + description: List of optional analysis features. + schema: + type: array + items: + enum: + - ocrHighResolution + - languages + - barcodes + - formulas + - keyValuePairs + - styleFont + - queryFields + type: string + x-ms-enum: + name: DocumentAnalysisFeature + modelAsString: true + values: + - name: ocrHighResolution + value: ocrHighResolution + description: Perform OCR at a higher resolution to handle documents with fine print. + - name: languages + value: languages + description: Enable the detection of the text content language. + - name: barcodes + value: barcodes + description: Enable the detection of barcodes in the document. + - name: formulas + value: formulas + description: Enable the detection of mathematical expressions in the document. + - name: keyValuePairs + value: keyValuePairs + description: Enable the detection of general key value pairs (form fields) in the document. + - name: styleFont + value: styleFont + description: Enable the recognition of various font styles. + - name: queryFields + value: queryFields + description: Enable the extraction of additional fields via the queryFields query parameter. + - name: queryFields + in: query + description: 'List of additional fields to extract. Ex. "NumberOfGuests,StoreNumber"' + schema: + type: array + items: + type: string + - name: outputContentFormat + in: query + description: Format of the analyze result top-level content. + schema: + enum: + - text + - markdown + type: string + default: text + - name: output + in: query + description: Additional outputs to generate during analysis. + schema: + type: array + items: + enum: + - pdf + - figures + type: string + x-ms-enum: + name: AnalyzeOutputOption + modelAsString: true + values: + - name: pdf + value: pdf + description: Generate searchable PDF output. + - name: figures + value: figures + description: Generate cropped images of detected figures. + - name: _overload + in: query + description: Optional parameter to specify the operation variant. Use 'analyzeDocument' for URL or base64 encoded document input. + schema: + enum: + - analyzeDocument + type: string + requestBody: + description: Input content. Can be either a binary document stream or an AnalyzeDocumentRequest object depending on the _overload parameter. + content: + application/json: + schema: + $ref: '#/components/schemas/Input' + example: + urlSource: https://raw.githubusercontent.com/Azure-Samples/cognitive-services-REST-api-samples/master/curl/form-recognizer/rest-api/read.png + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeOperation + schema: + type: string + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}:analyzeBatch': + post: + summary: DocumentModels_AnalyzeBatchDocuments + description: Analyzes batch documents with document model. + operationId: DocumentModels_AnalyzeBatchDocuments + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: pages + in: query + description: '1-based page numbers to analyze. Ex. "1-3,5,7-9"' + schema: + pattern: '^(\d+(-\d+)?)(,\s*(\d+(-\d+)?))*$' + type: string + - name: locale + in: query + description: "Locale hint for text recognition and document analysis. Value may contain only\nthe language code (ex. \"en\", \"fr\") or BCP 47 language tag (ex. \"en-US\")." + schema: + type: string + - name: stringIndexType + in: query + description: Method used to compute string offset and length. + schema: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + default: textElements + - name: features + in: query + description: List of optional analysis features. + schema: + type: array + items: + enum: + - ocrHighResolution + - languages + - barcodes + - formulas + - keyValuePairs + - styleFont + - queryFields + type: string + x-ms-enum: + name: DocumentAnalysisFeature + modelAsString: true + values: + - name: ocrHighResolution + value: ocrHighResolution + description: Perform OCR at a higher resolution to handle documents with fine print. + - name: languages + value: languages + description: Enable the detection of the text content language. + - name: barcodes + value: barcodes + description: Enable the detection of barcodes in the document. + - name: formulas + value: formulas + description: Enable the detection of mathematical expressions in the document. + - name: keyValuePairs + value: keyValuePairs + description: Enable the detection of general key value pairs (form fields) in the document. + - name: styleFont + value: styleFont + description: Enable the recognition of various font styles. + - name: queryFields + value: queryFields + description: Enable the extraction of additional fields via the queryFields query parameter. + - name: queryFields + in: query + description: 'List of additional fields to extract. Ex. "NumberOfGuests,StoreNumber"' + schema: + type: array + items: + type: string + - name: outputContentFormat + in: query + description: Format of the analyze result top-level content. + schema: + enum: + - text + - markdown + type: string + default: text + - name: output + in: query + description: Additional outputs to generate during analysis. + schema: + type: array + items: + enum: + - pdf + - figures + type: string + x-ms-enum: + name: AnalyzeOutputOption + modelAsString: true + values: + - name: pdf + value: pdf + description: Generate searchable PDF output. + - name: figures + value: figures + description: Generate cropped images of detected figures. + requestBody: + description: Analyze batch request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeBatchDocumentsRequest' + example: + azureBlobSource: + containerUrl: string + prefix: string + azureBlobFileListSource: + containerUrl: string + fileList: string + resultContainerUrl: string + resultPrefix: string + overwriteExisting: false + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of AnalyzeBatchOperation + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}': + get: + summary: DocumentModels_GetModel + description: Gets detailed document model information. + operationId: DocumentModels_GetModel + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentModelDetails' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + delete: + summary: DocumentModels_DeleteModel + description: Deletes document model. + operationId: DocumentModels_DeleteModel + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}:copyTo': + post: + summary: DocumentModels_CopyModelTo + description: 'Copies document model to the target resource, region, and modelId.' + operationId: DocumentModels_CopyModelTo + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Copy to request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCopyAuthorization' + example: + targetResourceId: string + targetResourceRegion: string + targetModelId: string + targetModelLocation: string + accessToken: string + expirationDateTime: string + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelCopyToOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}/analyzeBatchResults': + get: + summary: DocumentModels_ListAnalyzeBatchResults + description: List batch document analysis results. + operationId: DocumentModels_ListAnalyzeBatchResults + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/PagedAnalyzeBatchOperation' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}/analyzeBatchResults/{resultId}': + get: + summary: DocumentModels_GetAnalyzeBatchResult + description: Gets the result of batch document analysis. + operationId: DocumentModels_GetAnalyzeBatchResult + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze batch operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeBatchOperation' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + delete: + summary: DocumentModels_DeleteAnalyzeBatchResult + description: Mark the batch document analysis result for deletion. + operationId: DocumentModels_DeleteAnalyzeBatchResult + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze batch operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful.' + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}': + get: + summary: DocumentModels_GetAnalyzeResult + description: Gets the result of document analysis. + operationId: DocumentModels_GetAnalyzeResult + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/AnalyzeOperation' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + delete: + summary: DocumentModels_DeleteAnalyzeResult + description: Mark the result of document analysis for deletion. + operationId: DocumentModels_DeleteAnalyzeResult + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful.' + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}/figures/{figureId}': + get: + summary: DocumentModels_GetAnalyzeResultFigure + description: Gets the generated cropped image of specified figure from document analysis. + operationId: DocumentModels_GetAnalyzeResultFigure + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze operation result ID. + required: true + schema: + type: string + format: uuid + - name: figureId + in: path + description: Figure ID. + required: true + schema: + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + image/png: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ImagePngResp' + examples: + default: + value: + application/json: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ApplicationJ' + examples: + default: + value: + '500': + description: An unexpected error response. + content: + image/png: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/documentModels/{modelId}/analyzeResults/{resultId}/pdf': + get: + summary: DocumentModels_GetAnalyzeResultPdf + description: Gets the generated searchable PDF output from document analysis. + operationId: DocumentModels_GetAnalyzeResultPdf + parameters: + - name: modelId + in: path + description: Unique document model name. + required: true + schema: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + - name: resultId + in: path + description: Analyze operation result ID. + required: true + schema: + type: string + format: uuid + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/pdf: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationPdfResponse' + examples: + default: + value: + application/json: + schema: + $ref: '#/components/schemas/DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationJsonResponse' + examples: + default: + value: + '500': + description: An unexpected error response. + content: + application/pdf: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentModels:authorizeCopy: + post: + summary: DocumentModels_AuthorizeModelCopy + description: "Generates authorization to copy a document model to this location with\nspecified modelId and optional description." + operationId: DocumentModels_AuthorizeModelCopy + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Authorize copy request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/AuthorizeCopyRequest' + example: + modelId: string + description: string + tags: { } + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCopyAuthorization' + example: + targetResourceId: string + targetResourceRegion: string + targetModelId: string + targetModelLocation: string + accessToken: string + expirationDateTime: string + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentModels:build: + post: + summary: DocumentModels_BuildModel + description: Builds a custom document analysis model. + operationId: DocumentModels_BuildModel + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Build request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/BuildDocumentModelRequest' + example: + modelId: string + description: string + buildMode: template + azureBlobSource: + containerUrl: string + prefix: string + azureBlobFileListSource: + containerUrl: string + fileList: string + tags: { } + maxTrainingHours: 0.5 + allowOverwrite: false + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelBuildOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /documentModels:compose: + post: + summary: DocumentModels_ComposeModel + description: Creates a new document model from document types of existing document models. + operationId: DocumentModels_ComposeModel + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + requestBody: + description: Compose request parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/ComposeDocumentModelRequest' + responses: + '202': + description: 'The request has been accepted for processing, but processing has not yet completed.' + headers: + Operation-Location: + description: The location of an instance of DocumentModelComposeOperationDetails + schema: + type: string + format: uri + Retry-After: + description: The Retry-After header can indicate how long the client should wait before polling the operation status. + schema: + type: integer + format: int32 + content: + application/json: { } + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /info: + get: + summary: MiscellaneousOperations_GetResourceDetails + description: Return information about the current resource. + operationId: MiscellaneousOperations_GetResourceDetails + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceResourceDetails' + example: + customDocumentModels: + count: 0 + limit: 0 + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + /operations: + get: + summary: MiscellaneousOperations_ListOperations + description: Lists all operations. + operationId: MiscellaneousOperations_ListOperations + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/PagedDocumentIntelligenceOperationDetails' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' + '/operations/{operationId}': + get: + summary: MiscellaneousOperations_GetOperation + description: "Gets operation info. Different operation types can be retrieved:\n1. General operation: no _overload parameter needed\n2. Document Model Build operation: use _overload=getDocumentModelBuildOperation\n3. Document Model Compose operation: use _overload=getDocumentModelComposeOperation\n4. Document Model Copy To operation: use _overload=getDocumentModelCopyToOperation\n5. Document Classifier Copy To operation: use _overload=getDocumentClassifierCopyToOperation\n6. Document Classifier Build operation: use _overload=getDocumentClassifierBuildOperation" + operationId: MiscellaneousOperations_GetOperation + parameters: + - name: operationId + in: path + description: Operation ID + required: true + schema: + type: string + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + - name: _overload + in: query + description: Optional parameter to specify the operation type to retrieve. + schema: + enum: + - getOperation + - getDocumentModelBuildOperation + - getDocumentModelComposeOperation + - getDocumentModelCopyToOperation + - getDocumentClassifierCopyToOperation + - getDocumentClassifierBuildOperation + type: string + - name: x-ms-client-request-id + in: header + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + responses: + '200': + description: The request has succeeded. + headers: + x-ms-client-request-id: + description: 'An opaque, globally-unique, client-generated string identifier for the request.' + schema: + type: string + format: uuid + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + '500': + description: An unexpected error response. + content: + application/json: + schema: + $ref: '#/components/schemas/DocumentIntelligenceErrorResponse' +components: + schemas: + AddressValue: + type: object + properties: + houseNumber: + type: string + description: House or building number. + poBox: + type: string + description: Post office box number. + road: + type: string + description: Street name. + city: + type: string + description: 'Name of city, town, village, etc.' + state: + type: string + description: First-level administrative division. + postalCode: + type: string + description: Postal code used for mail sorting. + countryRegion: + type: string + description: Country/region. + streetAddress: + type: string + description: 'Street-level address, excluding city, state, countryRegion, and postalCode.' + unit: + type: string + description: Apartment or office number + cityDistrict: + type: string + description: "Districts or boroughs within a city, such as Brooklyn in New York City or City\nof Westminster in London." + stateDistrict: + type: string + description: Second-level administrative division used in certain locales. + suburb: + type: string + description: 'Unofficial neighborhood name, like Chinatown.' + house: + type: string + description: 'Build name, such as World Trade Center.' + level: + type: string + description: 'Floor number, such as 3F.' + description: Address field value. + AnalyzeBatchDocumentsRequest: + required: + - resultContainerUrl + type: object + properties: + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + resultContainerUrl: + type: string + description: Azure Blob Storage container URL where analyze result files will be stored. + format: uri + resultPrefix: + type: string + description: Blob name prefix of result files. + overwriteExisting: + type: boolean + description: Overwrite existing analyze result files? + default: false + description: Batch document analysis parameters. + AnalyzeBatchOperation: + required: + - status + - createdDateTime + - lastUpdatedDateTime + type: object + properties: + resultId: + $ref: '#/components/schemas/Azure.Core.uuid' + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + createdDateTime: + type: string + description: Date and time (UTC) when the operation was submitted. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + percentCompleted: + maximum: 100 + minimum: 0 + type: integer + description: Operation progress (0-100). + format: int32 + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + result: + $ref: '#/components/schemas/AnalyzeBatchResult' + description: Status and result of the analyze batch operation. + AnalyzeBatchOperationDetail: + required: + - status + - sourceUrl + type: object + properties: + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + sourceUrl: + type: string + description: URL of the source document. + format: uri + resultUrl: + type: string + description: URL of the analyze result JSON. + format: uri + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Operation detail for a document in a batch analysis. + AnalyzeBatchResult: + required: + - succeededCount + - failedCount + - skippedCount + type: object + properties: + succeededCount: + type: integer + description: Number of documents that completed with status succeeded. + format: int32 + failedCount: + type: integer + description: Number of documents that completed with status failed. + format: int32 + skippedCount: + type: integer + description: Number of documents that completed with status skipped. + format: int32 + details: + type: array + items: + $ref: '#/components/schemas/AnalyzeBatchOperationDetail' + description: Operation detail for each document in the batch. + x-ms-identifiers: [ ] + description: Batch document analysis result. + AnalyzeDocumentRequest: + type: object + properties: + urlSource: + type: string + description: Document URL to analyze. Either urlSource or base64Source must be specified. + format: uri + base64Source: + type: string + description: "Base64 encoding of the document to analyze. Either urlSource or base64Source\nmust be specified." + format: byte + description: Document analysis parameters. + AnalyzeOperation: + required: + - status + - createdDateTime + - lastUpdatedDateTime + type: object + properties: + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + createdDateTime: + type: string + description: Date and time (UTC) when the analyze operation was submitted. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + analyzeResult: + $ref: '#/components/schemas/AnalyzeResult' + description: Status and result of the analyze operation. + AnalyzeResult: + required: + - apiVersion + - modelId + - stringIndexType + - content + - pages + type: object + properties: + apiVersion: + type: string + description: API version used to produce this result. + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Document model ID used to produce this result. + stringIndexType: + $ref: '#/components/schemas/StringIndexType' + contentFormat: + $ref: '#/components/schemas/DocumentContentFormat' + content: + type: string + description: "Concatenate string representation of all textual and visual elements in reading\norder." + pages: + type: array + items: + $ref: '#/components/schemas/DocumentPage' + description: Analyzed pages. + x-ms-identifiers: [ ] + paragraphs: + type: array + items: + $ref: '#/components/schemas/DocumentParagraph' + description: Extracted paragraphs. + x-ms-identifiers: [ ] + tables: + type: array + items: + $ref: '#/components/schemas/DocumentTable' + description: Extracted tables. + x-ms-identifiers: [ ] + figures: + type: array + items: + $ref: '#/components/schemas/DocumentFigure' + description: Extracted figures. + sections: + type: array + items: + $ref: '#/components/schemas/DocumentSection' + description: Extracted sections. + x-ms-identifiers: [ ] + keyValuePairs: + type: array + items: + $ref: '#/components/schemas/DocumentKeyValuePair' + description: Extracted key-value pairs. + x-ms-identifiers: [ ] + styles: + type: array + items: + $ref: '#/components/schemas/DocumentStyle' + description: Extracted font styles. + x-ms-identifiers: [ ] + languages: + type: array + items: + $ref: '#/components/schemas/DocumentLanguage' + description: Detected languages. + x-ms-identifiers: [ ] + documents: + type: array + items: + $ref: '#/components/schemas/AnalyzedDocument' + description: Extracted documents. + x-ms-identifiers: [ ] + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered. + x-ms-identifiers: [ ] + description: Document analysis result. + AnalyzedDocument: + required: + - docType + - spans + - confidence + type: object + properties: + docType: + maxLength: 64 + minLength: 2 + type: string + description: Document type. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the document. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the document in the reading order concatenated content. + x-ms-identifiers: [ ] + fields: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentField' + description: Dictionary of named field values. + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the document. + format: float + description: An object describing the location and semantic content of a document. + AuthorizeClassifierCopyRequest: + required: + - classifierId + type: object + properties: + classifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + description: + maxLength: 4096 + type: string + description: Document classifier description. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document classifier. + description: Request body to authorize document classifier copy. + AuthorizeCopyRequest: + required: + - modelId + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + description: Request body to authorize document model copy. + Azure.Core.uuid: + type: string + description: Universally Unique Identifier + format: uuid + AzureBlobContentSource: + required: + - containerUrl + type: object + properties: + containerUrl: + type: string + description: Azure Blob Storage container URL. + format: uri + prefix: + type: string + description: Blob name prefix. + description: Azure Blob Storage content. + AzureBlobFileListContentSource: + required: + - containerUrl + - fileList + type: object + properties: + containerUrl: + type: string + description: Azure Blob Storage container URL. + format: uri + fileList: + type: string + description: Path to a JSONL file within the container specifying a subset of documents. + description: File list in Azure Blob Storage. + BoundingRegion: + required: + - pageNumber + - polygon + type: object + properties: + pageNumber: + minimum: 1 + type: integer + description: 1-based page number of page containing the bounding region. + format: int32 + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon on the page, or the entire page if not specified.\nCoordinates specified relative to the top-left of the page. The numbers\nrepresent the x, y values of the polygon vertices, clockwise from the left\n(-180 degrees inclusive) relative to the element orientation." + description: Bounding polygon on a specific page of the input. + BuildDocumentClassifierRequest: + required: + - classifierId + - docTypes + type: object + properties: + classifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + description: + maxLength: 4096 + type: string + description: Document classifier description. + baseClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Base classifierId on top of which to train the classifier. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/ClassifierDocumentTypeDetails' + description: List of document types to classify against. + allowOverwrite: + type: boolean + description: Allow overwriting an existing classifier with the same name. + default: false + description: Request body to build a new custom document classifier. + BuildDocumentModelRequest: + required: + - modelId + - buildMode + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + maxTrainingHours: + type: number + description: Max number of V100-equivalent GPU hours to use for model training. Default=0.5. + format: float + default: 0.5 + allowOverwrite: + type: boolean + description: Allow overwriting an existing model with the same name. + default: false + description: Request body to build a new custom document model. + ClassifierCopyAuthorization: + required: + - targetResourceId + - targetResourceRegion + - targetClassifierId + - targetClassifierLocation + - accessToken + - expirationDateTime + type: object + properties: + targetResourceId: + type: string + description: ID of the target Azure resource where the document classifier should be copied to. + targetResourceRegion: + type: string + description: "Location of the target Azure resource where the document classifier should be copied\nto." + targetClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Identifier of the target document classifier. + targetClassifierLocation: + type: string + description: URL of the copied document classifier in the target account. + format: uri + accessToken: + type: string + description: Token used to authorize the request. + expirationDateTime: + type: string + description: Date/time when the access token expires. + format: date-time + description: "Authorization to copy a document classifier to the specified target resource and\nclassifierId." + ClassifierDocumentTypeDetails: + type: object + properties: + sourceKind: + $ref: '#/components/schemas/ContentSourceKind' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + description: Classifier document type info. + ClassifyDocumentRequest: + type: object + properties: + urlSource: + type: string + description: Document URL to classify. Either urlSource or base64Source must be specified. + format: uri + base64Source: + type: string + description: "Base64 encoding of the document to classify. Either urlSource or base64Source\nmust be specified." + format: byte + description: Document classification parameters. + ComposeDocumentModelRequest: + required: + - modelId + - classifierId + - docTypes + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + description: + maxLength: 4096 + type: string + description: Document model description. + classifierId: + type: string + description: Custom classifier to split and classify the input file. + split: + enum: + - auto + - none + - perPage + type: string + description: File splitting behavior. + default: none + x-ms-enum: + name: SplitMode + modelAsString: true + values: + - name: auto + value: auto + description: Automatically split file into documents. + - name: none + value: none + description: Treat the entire file as a single document. + - name: perPage + value: perPage + description: Treat each page in the file as a separate document. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentTypeDetails' + description: Dictionary mapping supported docTypes to the corresponding document models. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + description: Request body to create a composed document model from component document models. + ContentSourceKind: + enum: + - url + - base64 + - azureBlob + - azureBlobFileList + type: string + description: Type of content source. + x-ms-enum: + name: ContentSourceKind + modelAsString: true + values: + - name: url + value: url + description: Content at a specific URL. + - name: base64 + value: base64 + description: Content represented via Base64 encoding. + - name: azureBlob + value: azureBlob + description: Files in a path within an Azure Blob Storage container. + - name: azureBlobFileList + value: azureBlobFileList + description: A file list specifying individual files in an Azure Blob Storage container. + CurrencyValue: + required: + - amount + type: object + properties: + amount: + type: number + description: Currency amount. + format: double + currencySymbol: + type: string + description: 'Currency symbol label, if any.' + currencyCode: + type: string + description: 'Resolved currency code (ISO 4217), if any.' + description: Currency field value. + CustomDocumentModelsDetails: + required: + - count + - limit + type: object + properties: + count: + type: integer + description: Number of custom document models in the current resource. + format: int32 + limit: + type: integer + description: Maximum number of custom document models supported in the current resource. + format: int32 + description: Details regarding custom document models. + DocumentAnalysisFeature: + enum: + - ocrHighResolution + - languages + - barcodes + - formulas + - keyValuePairs + - styleFont + - queryFields + type: string + description: Document analysis features to enable. + x-ms-enum: + name: DocumentAnalysisFeature + modelAsString: true + values: + - name: ocrHighResolution + value: ocrHighResolution + description: Perform OCR at a higher resolution to handle documents with fine print. + - name: languages + value: languages + description: Enable the detection of the text content language. + - name: barcodes + value: barcodes + description: Enable the detection of barcodes in the document. + - name: formulas + value: formulas + description: Enable the detection of mathematical expressions in the document. + - name: keyValuePairs + value: keyValuePairs + description: Enable the detection of general key value pairs (form fields) in the document. + - name: styleFont + value: styleFont + description: Enable the recognition of various font styles. + - name: queryFields + value: queryFields + description: Enable the extraction of additional fields via the queryFields query parameter. + DocumentBarcode: + required: + - kind + - value + - span + - confidence + type: object + properties: + kind: + $ref: '#/components/schemas/DocumentBarcodeKind' + value: + type: string + description: Barcode value. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the barcode, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the barcode. + format: float + description: A barcode object. + DocumentBarcodeKind: + enum: + - QRCode + - PDF417 + - UPCA + - UPCE + - Code39 + - Code128 + - EAN8 + - EAN13 + - DataBar + - Code93 + - Codabar + - DataBarExpanded + - ITF + - MicroQRCode + - Aztec + - DataMatrix + - MaxiCode + type: string + description: Barcode kind. + x-ms-enum: + name: DocumentBarcodeKind + modelAsString: true + values: + - name: QRCode + value: QRCode + description: 'QR code, as defined in ISO/IEC 18004:2015.' + - name: PDF417 + value: PDF417 + description: 'PDF417, as defined in ISO 15438.' + - name: UPCA + value: UPCA + description: GS1 12-digit Universal Product Code. + - name: UPCE + value: UPCE + description: GS1 6-digit Universal Product Code. + - name: Code39 + value: Code39 + description: 'Code 39 barcode, as defined in ISO/IEC 16388:2007.' + - name: Code128 + value: Code128 + description: 'Code 128 barcode, as defined in ISO/IEC 15417:2007.' + - name: EAN8 + value: EAN8 + description: GS1 8-digit International Article Number (European Article Number). + - name: EAN13 + value: EAN13 + description: GS1 13-digit International Article Number (European Article Number). + - name: DataBar + value: DataBar + description: GS1 DataBar barcode. + - name: Code93 + value: Code93 + description: 'Code 93 barcode, as defined in ANSI/AIM BC5-1995.' + - name: Codabar + value: Codabar + description: 'Codabar barcode, as defined in ANSI/AIM BC3-1995.' + - name: DataBarExpanded + value: DataBarExpanded + description: GS1 DataBar Expanded barcode. + - name: ITF + value: ITF + description: 'Interleaved 2 of 5 barcode, as defined in ANSI/AIM BC2-1995.' + - name: MicroQRCode + value: MicroQRCode + description: 'Micro QR code, as defined in ISO/IEC 23941:2022.' + - name: Aztec + value: Aztec + description: 'Aztec code, as defined in ISO/IEC 24778:2008.' + - name: DataMatrix + value: DataMatrix + description: 'Data matrix code, as defined in ISO/IEC 16022:2006.' + - name: MaxiCode + value: MaxiCode + description: 'MaxiCode, as defined in ISO/IEC 16023:2000.' + DocumentBuildMode: + enum: + - template + - neural + type: string + description: Custom document model build mode. + x-ms-enum: + name: DocumentBuildMode + modelAsString: true + values: + - name: template + value: template + description: Target documents with similar visual templates. + - name: neural + value: neural + description: Support documents with diverse visual templates. + DocumentCaption: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Content of the caption. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the caption. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the caption in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the caption. + description: A caption object describing a table or figure. + DocumentClassifierBuildOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentClassifierBuild + DocumentClassifierCopyToOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentClassifierCopyTo + DocumentClassifierDetails: + required: + - classifierId + - createdDateTime + - apiVersion + - docTypes + type: object + properties: + classifierId: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document classifier name. + x-ms-mutability: + - read + - create + description: + maxLength: 4096 + type: string + description: Document classifier description. + createdDateTime: + type: string + description: Date and time (UTC) when the document classifier was created. + format: date-time + expirationDateTime: + type: string + description: Date and time (UTC) when the document classifier will expire. + format: date-time + modifiedDateTime: + type: string + description: Date and time (UTC) when the document model was last modified. + format: date-time + readOnly: true + apiVersion: + type: string + description: API version used to create this document classifier. + baseClassifierId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Base classifierId on top of which the classifier was trained. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/ClassifierDocumentTypeDetails' + description: List of document types to classify against. + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered while building the classifier. + x-ms-identifiers: [ ] + description: Document classifier info. + DocumentContentFormat: + enum: + - text + - markdown + type: string + description: Format of the content in analyzed result. + x-ms-enum: + name: DocumentContentFormat + modelAsString: true + values: + - name: text + value: text + description: Plain text representation of the document content without any formatting. + - name: markdown + value: markdown + description: "Markdown representation of the document content with section headings, tables,\netc." + DocumentElement: + type: string + description: "An JSON pointer referencing an element of the document, such as paragraph,\ntable, section, etc." + DocumentField: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/DocumentFieldType' + valueString: + type: string + description: String value. + valueDate: + type: string + description: Date value in YYYY-MM-DD format (ISO 8601). + format: date + valueTime: + type: string + description: Time value in hh:mm:ss format (ISO 8601). + format: time + valuePhoneNumber: + type: string + description: Phone number value in E.164 format (ex. +19876543210). + valueNumber: + type: number + description: Floating point value. + format: double + valueInteger: + type: integer + description: Integer value. + format: int64 + valueSelectionMark: + $ref: '#/components/schemas/DocumentSelectionMarkState' + valueSignature: + $ref: '#/components/schemas/DocumentSignatureType' + valueCountryRegion: + type: string + description: 3-letter country code value (ISO 3166-1 alpha-3). + valueArray: + type: array + items: + $ref: '#/components/schemas/DocumentField' + description: Array of field values. + x-ms-identifiers: [ ] + valueObject: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentField' + description: Dictionary of named field values. + valueCurrency: + $ref: '#/components/schemas/CurrencyValue' + valueAddress: + $ref: '#/components/schemas/AddressValue' + valueBoolean: + type: boolean + description: Boolean value. + valueSelectionGroup: + type: array + items: + type: string + description: Selection group value. + content: + type: string + description: Field content. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the field. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the field in the reading order concatenated content. + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the field. + format: float + description: An object representing the content and location of a field value. + DocumentFieldSchema: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/DocumentFieldType' + description: + maxLength: 4096 + type: string + description: Field description. + example: + type: string + description: Example field content. + items: + $ref: '#/components/schemas/DocumentFieldSchema' + properties: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentFieldSchema' + description: Named sub-fields of the object field. + description: Description of the field semantic schema using a JSON Schema style syntax. + DocumentFieldType: + enum: + - string + - date + - time + - phoneNumber + - number + - integer + - selectionMark + - countryRegion + - signature + - array + - object + - currency + - address + - boolean + - selectionGroup + type: string + description: Semantic data type of the field value. + x-ms-enum: + name: DocumentFieldType + modelAsString: true + values: + - name: string + value: string + description: Plain text. + - name: date + value: date + description: 'Date, normalized to ISO 8601 (YYYY-MM-DD) format.' + - name: time + value: time + description: 'Time, normalized to ISO 8601 (hh:mm:ss) format.' + - name: phoneNumber + value: phoneNumber + description: 'Phone number, normalized to E.164 (+{CountryCode}{SubscriberNumber}) format.' + - name: number + value: number + description: 'Floating point number, normalized to double precision floating point.' + - name: integer + value: integer + description: 'Integer number, normalized to 64-bit signed integer.' + - name: selectionMark + value: selectionMark + description: Is field selected? + - name: countryRegion + value: countryRegion + description: 'Country/region, normalized to ISO 3166-1 alpha-3 format (ex. USA).' + - name: signature + value: signature + description: Is signature present? + - name: array + value: array + description: List of subfields of the same type. + - name: object + value: object + description: Named list of subfields of potentially different types. + - name: currency + value: currency + description: Currency amount with optional currency symbol and unit. + - name: address + value: address + description: Parsed address. + - name: boolean + value: boolean + description: 'Boolean value, normalized to true or false.' + - name: selectionGroup + value: selectionGroup + description: Array of selected string values. + DocumentFigure: + required: + - spans + type: object + properties: + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the figure. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the figure in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: 'Child elements of the figure, excluding any caption or footnotes.' + caption: + $ref: '#/components/schemas/DocumentCaption' + footnotes: + type: array + items: + $ref: '#/components/schemas/DocumentFootnote' + description: List of footnotes associated with the figure. + x-ms-identifiers: [ ] + id: + type: string + description: Figure ID. + description: An object representing a figure in the document. + DocumentFontStyle: + enum: + - normal + - italic + type: string + description: Font style. + x-ms-enum: + name: DocumentFontStyle + modelAsString: true + values: + - name: normal + value: normal + description: Characters are represented normally. + - name: italic + value: italic + description: Characters are visually slanted to the right. + DocumentFontWeight: + enum: + - normal + - bold + type: string + description: Font weight. + x-ms-enum: + name: DocumentFontWeight + modelAsString: true + values: + - name: normal + value: normal + description: Characters are represented normally. + - name: bold + value: bold + description: Characters are represented with thicker strokes. + DocumentFootnote: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Content of the footnote. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the footnote. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the footnote in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the footnote. + description: A footnote object describing a table or figure. + DocumentFormula: + required: + - kind + - value + - span + - confidence + type: object + properties: + kind: + $ref: '#/components/schemas/DocumentFormulaKind' + value: + type: string + description: LaTex expression describing the formula. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the formula, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the formula. + format: float + description: A formula object. + DocumentFormulaKind: + enum: + - inline + - display + type: string + description: Formula kind. + x-ms-enum: + name: DocumentFormulaKind + modelAsString: true + values: + - name: inline + value: inline + description: A formula embedded within the content of a paragraph. + - name: display + value: display + description: A formula in display mode that takes up an entire line. + DocumentIntelligenceError: + required: + - code + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + message: + type: string + description: A human-readable representation of the error. + target: + type: string + description: The target of the error. + details: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: An array of details about specific errors that led to this reported error. + x-ms-identifiers: [ ] + innererror: + $ref: '#/components/schemas/DocumentIntelligenceInnerError' + description: The error object. + DocumentIntelligenceErrorResponse: + required: + - error + type: object + properties: + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Error response object. + DocumentIntelligenceInnerError: + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + message: + type: string + description: A human-readable representation of the error. + innererror: + $ref: '#/components/schemas/DocumentIntelligenceInnerError' + description: An object containing more specific information about the error. + DocumentIntelligenceOperationDetails: + required: + - operationId + - status + - createdDateTime + - lastUpdatedDateTime + - kind + - resourceLocation + type: object + properties: + operationId: + type: string + description: Operation ID + x-ms-mutability: + - read + - create + status: + $ref: '#/components/schemas/DocumentIntelligenceOperationStatus' + percentCompleted: + maximum: 100 + minimum: 0 + type: integer + description: Operation progress (0-100). + format: int32 + createdDateTime: + type: string + description: Date and time (UTC) when the operation was created. + format: date-time + lastUpdatedDateTime: + type: string + description: Date and time (UTC) when the status was last updated. + format: date-time + kind: + $ref: '#/components/schemas/OperationKind' + resourceLocation: + type: string + description: URL of the resource targeted by this operation. + format: uri + apiVersion: + type: string + description: API version used to create this operation. + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + error: + $ref: '#/components/schemas/DocumentIntelligenceError' + description: Operation info. + discriminator: + propertyName: kind + DocumentIntelligenceOperationStatus: + enum: + - notStarted + - running + - failed + - succeeded + - canceled + - skipped + type: string + description: Operation status. + x-ms-enum: + name: DocumentIntelligenceOperationStatus + modelAsString: true + values: + - name: notStarted + value: notStarted + description: The operation has not started yet. + - name: running + value: running + description: The operation is in progress. + - name: failed + value: failed + description: The operation has failed. + - name: succeeded + value: succeeded + description: The operation has succeeded. + - name: canceled + value: canceled + description: The operation has been canceled. + - name: skipped + value: skipped + description: The operation has been skipped. + DocumentIntelligenceResourceDetails: + required: + - customDocumentModels + type: object + properties: + customDocumentModels: + $ref: '#/components/schemas/CustomDocumentModelsDetails' + description: General information regarding the current resource. + DocumentIntelligenceWarning: + required: + - code + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of warning codes. + message: + type: string + description: A human-readable representation of the warning. + target: + type: string + description: The target of the error. + description: The error object. + DocumentKeyValueElement: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Concatenated content of the key-value element in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the key-value element. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the key-value element in the reading order concatenated content. + x-ms-identifiers: [ ] + description: An object representing the field key or value in a key-value pair. + DocumentKeyValuePair: + required: + - key + - confidence + type: object + properties: + key: + $ref: '#/components/schemas/DocumentKeyValueElement' + value: + $ref: '#/components/schemas/DocumentKeyValueElement' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the key-value pair. + format: float + description: "An object representing a form field with distinct field label (key) and field\nvalue (may be empty)." + DocumentLanguage: + required: + - locale + - spans + - confidence + type: object + properties: + locale: + type: string + description: "Detected language. Value may an ISO 639-1 language code (ex. \"en\", \"fr\")\nor BCP 47 language tag (ex. \"zh-Hans\")." + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: "Location of the text elements in the concatenated content the language applies\nto." + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly identifying the language. + format: float + description: An object representing the detected language for a given text span. + DocumentLine: + required: + - content + - spans + type: object + properties: + content: + type: string + description: Concatenated content of the contained elements in reading order. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the line, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the line in the reading order concatenated content. + x-ms-identifiers: [ ] + description: "A content line object consisting of an adjacent sequence of content elements,\nsuch as words and selection marks." + DocumentModelBuildOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelBuild + DocumentModelComposeOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelCompose + DocumentModelCopyToOperationDetails: + type: object + allOf: + - $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + properties: + result: + $ref: '#/components/schemas/DocumentModelDetails' + description: Get Operation response object. + x-ms-discriminator-value: documentModelCopyTo + DocumentModelDetails: + required: + - modelId + - createdDateTime + type: object + properties: + modelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Unique document model name. + x-ms-mutability: + - read + - create + description: + maxLength: 4096 + type: string + description: Document model description. + createdDateTime: + type: string + description: Date and time (UTC) when the document model was created. + format: date-time + readOnly: true + expirationDateTime: + type: string + description: Date and time (UTC) when the document model will expire. + format: date-time + readOnly: true + modifiedDateTime: + type: string + description: Date and time (UTC) when the document model was last modified. + format: date-time + readOnly: true + apiVersion: + type: string + description: API version used to create this document model. + readOnly: true + tags: + type: object + additionalProperties: + type: string + description: List of key-value tag attributes associated with the document model. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + azureBlobSource: + $ref: '#/components/schemas/AzureBlobContentSource' + azureBlobFileListSource: + $ref: '#/components/schemas/AzureBlobFileListContentSource' + classifierId: + type: string + description: 'For composed models, the custom classifier to split and classify the input file.' + split: + enum: + - auto + - none + - perPage + type: string + description: 'For composed models, the file splitting behavior.' + default: none + x-ms-enum: + name: SplitMode + modelAsString: true + values: + - name: auto + value: auto + description: Automatically split file into documents. + - name: none + value: none + description: Treat the entire file as a single document. + - name: perPage + value: perPage + description: Treat each page in the file as a separate document. + docTypes: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentTypeDetails' + description: Supported document types. + readOnly: true + warnings: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceWarning' + description: List of warnings encountered while building the model. + readOnly: true + x-ms-identifiers: [ ] + trainingHours: + type: number + description: Number of V100-equivalent GPU hours consumed for model training. + format: float + readOnly: true + description: Document model info. + DocumentPage: + required: + - pageNumber + - spans + type: object + properties: + pageNumber: + minimum: 1 + type: integer + description: 1-based page number in the input document. + format: int32 + angle: + maximum: 180 + type: number + description: "The general orientation of the content in clockwise direction, measured in\ndegrees between (-180, 180]." + format: float + width: + minimum: 0 + type: number + description: 'The width of the image/PDF in pixels/inches, respectively.' + format: float + height: + minimum: 0 + type: number + description: 'The height of the image/PDF in pixels/inches, respectively.' + format: float + unit: + $ref: '#/components/schemas/LengthUnit' + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the page in the reading order concatenated content. + x-ms-identifiers: [ ] + words: + type: array + items: + $ref: '#/components/schemas/DocumentWord' + description: Extracted words from the page. + x-ms-identifiers: [ ] + selectionMarks: + type: array + items: + $ref: '#/components/schemas/DocumentSelectionMark' + description: Extracted selection marks from the page. + x-ms-identifiers: [ ] + lines: + type: array + items: + $ref: '#/components/schemas/DocumentLine' + description: "Extracted lines from the page, potentially containing both textual and visual\nelements." + x-ms-identifiers: [ ] + barcodes: + type: array + items: + $ref: '#/components/schemas/DocumentBarcode' + description: Extracted barcodes from the page. + x-ms-identifiers: [ ] + formulas: + type: array + items: + $ref: '#/components/schemas/DocumentFormula' + description: Extracted formulas from the page. + x-ms-identifiers: [ ] + description: Content and layout elements extracted from a page from the input. + DocumentParagraph: + required: + - content + - spans + type: object + properties: + role: + $ref: '#/components/schemas/ParagraphRole' + content: + type: string + description: Concatenated content of the paragraph in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the paragraph. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the paragraph in the reading order concatenated content. + x-ms-identifiers: [ ] + description: "A paragraph object consisting with contiguous lines generally with common\nalignment and spacing." + DocumentSection: + required: + - spans + type: object + properties: + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the section in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the section. + description: An object representing a section in the document. + DocumentSelectionMark: + required: + - state + - span + - confidence + type: object + properties: + state: + $ref: '#/components/schemas/DocumentSelectionMarkState' + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the selection mark, with coordinates specified relative\nto the top-left of the page. The numbers represent the x, y values of the\npolygon vertices, clockwise from the left (-180 degrees inclusive) relative\nto the element orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the selection mark. + format: float + description: "A selection mark object representing check boxes, radio buttons, and other\nelements indicating a selection." + DocumentSelectionMarkState: + enum: + - selected + - unselected + type: string + description: State of the selection mark. + x-ms-enum: + name: DocumentSelectionMarkState + modelAsString: true + values: + - name: selected + value: selected + description: "The selection mark is selected, often indicated by a check ✓ or cross X inside\nthe selection mark." + - name: unselected + value: unselected + description: The selection mark is not selected. + DocumentSignatureType: + enum: + - signed + - unsigned + type: string + description: Presence of signature. + x-ms-enum: + name: DocumentSignatureType + modelAsString: true + values: + - name: signed + value: signed + description: A signature is detected. + - name: unsigned + value: unsigned + description: No signatures are detected. + DocumentSpan: + required: + - offset + - length + type: object + properties: + offset: + minimum: 0 + type: integer + description: Zero-based index of the content represented by the span. + format: int32 + length: + minimum: 0 + type: integer + description: Number of characters in the content represented by the span. + format: int32 + description: "Contiguous region of the concatenated content property, specified as an offset\nand length." + DocumentStyle: + required: + - spans + - confidence + type: object + properties: + isHandwritten: + type: boolean + description: Is content handwritten? + similarFontFamily: + type: string + description: "Visually most similar font from among the set of supported font families, with\nfallback fonts following CSS convention (ex. 'Arial, sans-serif')." + fontStyle: + $ref: '#/components/schemas/DocumentFontStyle' + fontWeight: + $ref: '#/components/schemas/DocumentFontWeight' + color: + pattern: '^#[0-9a-f]{6}$' + type: string + description: 'Foreground color in #rrggbb hexadecimal format.' + backgroundColor: + pattern: '^#[0-9a-f]{6}$' + type: string + description: 'Background color in #rrggbb hexadecimal format..' + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the text elements in the concatenated content the style applies to. + x-ms-identifiers: [ ] + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly identifying the style. + format: float + description: An object representing observed text styles. + DocumentTable: + required: + - rowCount + - columnCount + - cells + - spans + type: object + properties: + rowCount: + minimum: 1 + type: integer + description: Number of rows in the table. + format: int32 + columnCount: + minimum: 1 + type: integer + description: Number of columns in the table. + format: int32 + cells: + type: array + items: + $ref: '#/components/schemas/DocumentTableCell' + description: Cells contained within the table. + x-ms-identifiers: [ ] + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the table. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the table in the reading order concatenated content. + x-ms-identifiers: [ ] + caption: + $ref: '#/components/schemas/DocumentCaption' + footnotes: + type: array + items: + $ref: '#/components/schemas/DocumentFootnote' + description: List of footnotes associated with the table. + x-ms-identifiers: [ ] + description: A table object consisting table cells arranged in a rectangular layout. + DocumentTableCell: + required: + - rowIndex + - columnIndex + - content + - spans + type: object + properties: + kind: + enum: + - content + - rowHeader + - columnHeader + - stubHead + - description + type: string + description: Table cell kind. + default: content + x-ms-enum: + name: DocumentTableCellKind + modelAsString: true + values: + - name: content + value: content + description: Contains the main content/data. + - name: rowHeader + value: rowHeader + description: Describes the content of the row. + - name: columnHeader + value: columnHeader + description: Describes the content of the column. + - name: stubHead + value: stubHead + description: 'Describes the row headers, usually located at the top left corner of a table.' + - name: description + value: description + description: Describes the content in (parts of) the table. + rowIndex: + type: integer + description: Row index of the cell. + format: int32 + columnIndex: + type: integer + description: Column index of the cell. + format: int32 + rowSpan: + minimum: 1 + type: integer + description: Number of rows spanned by this cell. + format: int32 + default: 1 + columnSpan: + minimum: 1 + type: integer + description: Number of columns spanned by this cell. + format: int32 + default: 1 + content: + type: string + description: Concatenated content of the table cell in reading order. + boundingRegions: + type: array + items: + $ref: '#/components/schemas/BoundingRegion' + description: Bounding regions covering the table cell. + x-ms-identifiers: [ ] + spans: + type: array + items: + $ref: '#/components/schemas/DocumentSpan' + description: Location of the table cell in the reading order concatenated content. + x-ms-identifiers: [ ] + elements: + type: array + items: + $ref: '#/components/schemas/DocumentElement' + description: Child elements of the table cell. + description: An object representing the location and content of a table cell. + DocumentTypeDetails: + type: object + properties: + description: + maxLength: 4096 + type: string + description: Document model description. + buildMode: + $ref: '#/components/schemas/DocumentBuildMode' + fieldSchema: + type: object + additionalProperties: + $ref: '#/components/schemas/DocumentFieldSchema' + description: Description of the document semantic schema using a JSON Schema style syntax. + fieldConfidence: + type: object + additionalProperties: + type: number + format: float + description: Estimated confidence for each field. + modelId: + maxLength: 64 + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Document model to use for analyzing documents with specified type. + confidenceThreshold: + type: number + description: Only perform analysis if docType confidence is above threshold. + format: float + features: + type: array + items: + $ref: '#/components/schemas/DocumentAnalysisFeature' + description: List of optional analysis features. + queryFields: + type: array + items: + type: string + description: 'List of additional fields to extract. Ex. "NumberOfGuests,StoreNumber"' + maxDocumentsToAnalyze: + type: integer + description: Maximum number of documents of specified type to analyze. Default=all. + format: int32 + description: Document type info. + DocumentWord: + required: + - content + - span + - confidence + type: object + properties: + content: + type: string + description: Text content of the word. + polygon: + type: array + items: + type: number + format: float + description: "Bounding polygon of the word, with coordinates specified relative to the\ntop-left of the page. The numbers represent the x, y values of the polygon\nvertices, clockwise from the left (-180 degrees inclusive) relative to the\nelement orientation." + span: + $ref: '#/components/schemas/DocumentSpan' + confidence: + maximum: 1 + minimum: 0 + type: number + description: Confidence of correctly extracting the word. + format: float + description: "A word object consisting of a contiguous sequence of characters. For non-space\ndelimited languages, such as Chinese, Japanese, and Korean, each character is\nrepresented as its own word." + LengthUnit: + enum: + - pixel + - inch + type: string + description: 'The unit used by the width, height, and polygon properties. For images, the unit is "pixel". For PDF, the unit is "inch".' + x-ms-enum: + name: LengthUnit + modelAsString: true + values: + - name: pixel + value: pixel + description: Length unit for image files. + - name: inch + value: inch + description: Length unit for PDF files. + ModelCopyAuthorization: + required: + - targetResourceId + - targetResourceRegion + - targetModelId + - targetModelLocation + - accessToken + - expirationDateTime + type: object + properties: + targetResourceId: + type: string + description: ID of the target Azure resource where the document model should be copied to. + targetResourceRegion: + type: string + description: "Location of the target Azure resource where the document model should be copied\nto." + targetModelId: + pattern: '^[a-zA-Z0-9][a-zA-Z0-9._~-]{1,63}$' + type: string + description: Identifier of the target document model. + targetModelLocation: + type: string + description: URL of the copied document model in the target account. + format: uri + accessToken: + type: string + description: Token used to authorize the request. + expirationDateTime: + type: string + description: Date/time when the access token expires. + format: date-time + description: "Authorization to copy a document model to the specified target resource and\nmodelId." + OperationKind: + enum: + - documentModelBuild + - documentModelCompose + - documentModelCopyTo + - documentClassifierCopyTo + - documentClassifierBuild + type: string + description: Type of operation. + x-ms-enum: + name: OperationKind + modelAsString: true + values: + - name: documentModelBuild + value: documentModelBuild + description: Build a new custom document model. + - name: documentModelCompose + value: documentModelCompose + description: Compose a new custom document model from existing models. + - name: documentModelCopyTo + value: documentModelCopyTo + description: "Copy an existing document model to potentially a different resource, region, or\nsubscription." + - name: documentClassifierCopyTo + value: documentClassifierCopyTo + description: "Copy an existing document classifier to potentially a different resource, region, or\nsubscription." + - name: documentClassifierBuild + value: documentClassifierBuild + description: Build a new custom classifier model. + PagedAnalyzeBatchOperation: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/AnalyzeBatchOperation' + description: The AnalyzeBatchOperation items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of AnalyzeBatchOperation items + PagedDocumentClassifierDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentClassifierDetails' + description: The DocumentClassifierDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentClassifierDetails items + PagedDocumentIntelligenceOperationDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentIntelligenceOperationDetails' + description: The DocumentIntelligenceOperationDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentIntelligenceOperationDetails items + PagedDocumentModelDetails: + required: + - value + type: object + properties: + value: + type: array + items: + $ref: '#/components/schemas/DocumentModelDetails' + description: The DocumentModelDetails items on this page + x-ms-identifiers: [ ] + nextLink: + type: string + description: The link to the next page of items + format: uri + description: Paged collection of DocumentModelDetails items + ParagraphRole: + enum: + - pageHeader + - pageFooter + - pageNumber + - title + - sectionHeading + - footnote + - formulaBlock + type: string + description: Semantic role of the paragraph. + x-ms-enum: + name: ParagraphRole + modelAsString: true + values: + - name: pageHeader + value: pageHeader + description: Text near the top edge of the page. + - name: pageFooter + value: pageFooter + description: Text near the bottom edge of the page. + - name: pageNumber + value: pageNumber + description: Page number. + - name: title + value: title + description: Top-level title describing the entire document. + - name: sectionHeading + value: sectionHeading + description: Sub heading describing a section of the document. + - name: footnote + value: footnote + description: A note usually placed after the main content on a page. + - name: formulaBlock + value: formulaBlock + description: 'A block of formulas, often with shared alignment.' + StringIndexType: + enum: + - textElements + - unicodeCodePoint + - utf16CodeUnit + type: string + description: Method used to compute string offset and length. + x-ms-enum: + name: StringIndexType + modelAsString: true + values: + - name: textElements + value: textElements + description: "User-perceived display character, or grapheme cluster, as defined by Unicode\n8.0.0." + - name: unicodeCodePoint + value: unicodeCodePoint + description: Character unit represented by a single unicode code point. Used by Python 3. + - name: utf16CodeUnit + value: utf16CodeUnit + description: "Character unit represented by a 16-bit Unicode code unit. Used by JavaScript,\nJava, and .NET." + Input: + type: object + DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ImagePngResp: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdFiguresFigureIdGet200ApplicationJ: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationPdfResponse: + type: file + DocumentModelsModelIdAnalyzeResultsResultIdPdfGet200ApplicationJsonResponse: + type: file + securitySchemes: + apiKeyHeader: + type: apiKey + name: Ocp-Apim-Subscription-Key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-api.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-api.bicep new file mode 100644 index 000000000..1a74419d9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-api.bicep @@ -0,0 +1,242 @@ +/** + * @module + * @description This module defines the API resources using Bicep. + * It includes configurations for creating and managing APIs, products, and policies. + * This is version 2 (v2) of the APIM Bicep module. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The suffix to append to the API Management instance name. Defaults to a unique string based on subscription and resource group IDs.') +param resourceSuffix string = uniqueString(subscription().id, resourceGroup().id) + +@description('The name of the API Management instance. Defaults to "apim-".') +param apiManagementName string = 'apim-${resourceSuffix}' + +@description('Id of the APIM Logger') +param apimLoggerId string = '' + +@description('The instrumentation key for Application Insights') +@secure() +param appInsightsInstrumentationKey string = '' + +@description('The resource ID for Application Insights') +param appInsightsId string = '' + +@description('The XML content for the API policy') +param policyXml string + +@description('Configuration array for AI Services') +param aiServicesConfig array = [] + +@description('The name of the Inference API in API Management.') +param inferenceAPIName string = 'inference-api' + +@description('The description of the Inference API in API Management.') +param inferenceAPIDescription string = 'Inferencing API for language models' + +@description('The display name of the Inference API in API Management. .') +param inferenceAPIDisplayName string = 'Inference API' + +@description('The name of the Inference backend pool.') +param inferenceBackendPoolName string = 'inference-backend-pool' + +@description('Allow the use subscription key for the inference API (in case of using JWT auth only this can be set to false)') +param allowSubscriptionKey bool = true + +@description('The inference API type') +@allowed([ + 'AzureOpenAI' + 'AzureAI' + 'OpenAI' + 'OpenAIV1' +]) +param inferenceAPIType string = 'AzureOpenAI' + +@description('The path to the inference API in the APIM service') +param inferenceAPIPath string = 'inference' // Path to the inference API in the APIM service + +@description('Whether to configure the circuit breaker for the inference backend') +param configureCircuitBreaker bool = false + +@description('Azure Monitor diagnostic log settings for the inference API (frontend/backend request/response headers & body bytes, and LLM log settings).') +param azureMonitorLogSettings object = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +@description('Application Insights diagnostic log settings for the inference API (headers to capture and body bytes).') +param appInsightsLogSettings object = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens', 'x-ratelimit-remaining-requests' ] + body: { bytes: 0 } +} + +// ------------------ +// VARIABLES +// ------------------ + +var logSettings = { + headers: appInsightsLogSettings.headers + body: appInsightsLogSettings.body +} + +// to allow future modifications to the policy XML if needed +var updatedPolicyXml = policyXml + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apiManagementName +} + +var endpointPath = (inferenceAPIType == 'AzureOpenAI') ? 'openai' : (inferenceAPIType == 'AzureAI') ? 'inference' : (inferenceAPIType == 'OpenAI') ? 'openai' : (inferenceAPIType == 'OpenAIV1') ? 'models' : '' + +// https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/apis +resource api 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + name: inferenceAPIName + parent: apimService + properties: { + apiType: 'http' + description: inferenceAPIDescription + displayName: inferenceAPIDisplayName + format: 'openapi+json' + path: '${inferenceAPIPath}/${endpointPath}' + protocols: [ + 'https' + ] + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + subscriptionRequired: allowSubscriptionKey + type: 'http' + value: string((inferenceAPIType == 'AzureOpenAI') ? loadJsonContent('./universal-llm-api/AIFoundryOpenAI.json') : (inferenceAPIType == 'AzureAI') ? loadJsonContent('./universal-llm-api/AIFoundryAzureAI.json') : (inferenceAPIType == 'OpenAI') ? loadJsonContent('./universal-llm-api/AIFoundryAzureAI.json') : (inferenceAPIType == 'OpenAIV1') ? loadJsonContent('./universal-llm-api/AIFoundryOpenAIV1.json') : loadJsonContent('./universal-llm-api/PassThrough.json')) + } +} +// https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/apis/policies +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-06-01-preview' = { + name: 'policy' + parent: api + properties: { + format: 'rawxml' + value: updatedPolicyXml + } +} + + +// Reference the backend module +module inferenceBackends './inference-backend.bicep' = { + name: 'inferenceBackends-${inferenceAPIName}' + params: { + resourceSuffix: resourceSuffix + apiManagementName: apiManagementName + aiServicesConfig: aiServicesConfig + inferenceBackendPoolName: inferenceBackendPoolName + configureCircuitBreaker: configureCircuitBreaker + inferenceAPIType: inferenceAPIType + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2024-06-01-preview' = if(length(apimLoggerId) > 0) { + parent: api + name: 'azuremonitor' + properties: { + alwaysLog: 'allErrors' + verbosity: 'verbose' + logClientIp: true + loggerId: apimLoggerId + sampling: { + samplingType: 'fixed' + percentage: json('100') + } + frontend: { + request: { + headers: azureMonitorLogSettings.frontend.request.headers + body: { + bytes: azureMonitorLogSettings.frontend.request.body.bytes + } + } + response: { + headers: azureMonitorLogSettings.frontend.response.headers + body: { + bytes: azureMonitorLogSettings.frontend.response.body.bytes + } + } + } + backend: { + request: { + headers: azureMonitorLogSettings.backend.request.headers + body: { + bytes: azureMonitorLogSettings.backend.request.body.bytes + } + } + response: { + headers: azureMonitorLogSettings.backend.response.headers + body: { + bytes: azureMonitorLogSettings.backend.response.body.bytes + } + } + } + largeLanguageModel: { + logs: azureMonitorLogSettings.largeLanguageModel.logs + requests: { + messages: azureMonitorLogSettings.largeLanguageModel.requests.messages + maxSizeInBytes: azureMonitorLogSettings.largeLanguageModel.requests.maxSizeInBytes + } + responses: { + messages: azureMonitorLogSettings.largeLanguageModel.responses.messages + maxSizeInBytes: azureMonitorLogSettings.largeLanguageModel.responses.maxSizeInBytes + } + } + } +} + +resource apiDiagnosticsAppInsights 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = if (!empty(appInsightsId) && !empty(appInsightsInstrumentationKey)) { + name: 'applicationinsights' + parent: api + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: resourceId(resourceGroup().name, 'Microsoft.ApiManagement/service/loggers', apiManagementName, 'appinsights-logger') + metrics: true + verbosity: 'verbose' + sampling: { + samplingType: 'fixed' + percentage: 100 + } + frontend: { + request: logSettings + response: logSettings + } + backend: { + request: logSettings + response: logSettings + } + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +output apiId string = api.id +output path string = api.properties.path +output backendNames array = inferenceBackends.outputs.backendNames +output backendPoolName string = inferenceBackends.outputs.backendPoolName +output backendPoolId string = inferenceBackends.outputs.backendPoolId diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-backend.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-backend.bicep new file mode 100644 index 000000000..bd4ddec55 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/inference-backend.bicep @@ -0,0 +1,118 @@ +/** + * @module + * @description This module defines the backend resources for the Inference API. + * It includes configurations for creating and managing individual backends and backend pools. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The suffix to append to the API Management instance name. Defaults to a unique string based on subscription and resource group IDs.') +param resourceSuffix string = uniqueString(subscription().id, resourceGroup().id) + +@description('The name of the API Management instance. Defaults to "apim-".') +param apiManagementName string = 'apim-${resourceSuffix}' + +@description('Configuration array for AI Services') +param aiServicesConfig array = [] + +@description('The name of the Inference backend pool.') +param inferenceBackendPoolName string = 'inference-backend-pool' + +@description('Whether to configure the circuit breaker for the inference backend') +param configureCircuitBreaker bool = false + +@description('The inference API type') +@allowed([ + 'AzureOpenAI' + 'AzureAI' + 'OpenAI' + 'OpenAIV1' +]) +param inferenceAPIType string = 'AzureOpenAI' + +// ------------------ +// VARIABLES +// ------------------ + +var endpointPath = (inferenceAPIType == 'AzureOpenAI') ? 'openai' : (inferenceAPIType == 'AzureAI') ? 'models' : '' + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apiManagementName +} + +// https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/backends +resource inferenceBackend 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = [for (config, i) in aiServicesConfig: if(length(aiServicesConfig) > 0) { + name: config.name + parent: apimService + properties: { + description: 'Inference backend' + url: '${config.endpoint}${endpointPath}' + protocol: 'http' + circuitBreaker: (configureCircuitBreaker) ? { + rules: [ + { + failureCondition: { + count: 1 + errorReasons: [ + 'Server errors' + ] + interval: 'PT5M' + statusCodeRanges: [ + { + min: 429 + max: 429 + } + ] + } + name: 'InferenceBreakerRule' + tripDuration: 'PT1M' + acceptRetryAfter: true + } + ] + }: null + credentials: { + #disable-next-line BCP037 + managedIdentity: { + resource: 'https://cognitiveservices.azure.com' + } + } + } +}] + +// https://learn.microsoft.com/azure/templates/microsoft.apimanagement/service/backends +resource backendPoolOpenAI 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = if(length(aiServicesConfig) > 1) { + name: inferenceBackendPoolName + parent: apimService + // BCP035: protocol and url are not needed in the Pool type. This is an incorrect error. + #disable-next-line BCP035 + properties: { + description: 'Load balancer for multiple inference endpoints' + type: 'Pool' + pool: { + services: [for (config, i) in aiServicesConfig: { + id: '/backends/${inferenceBackend[i].name}' + priority: config.?priority + weight: config.?weight + }] + } + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Array of backend names created') +output backendNames array = [for (config, i) in aiServicesConfig: inferenceBackend[i].name] + +@description('The name of the backend pool (if created)') +output backendPoolName string = length(aiServicesConfig) > 1 ? backendPoolOpenAI.name : '' + +@description('The ID of the backend pool (if created)') +output backendPoolId string = length(aiServicesConfig) > 1 ? backendPoolOpenAI.id : '' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/language-api/language-api-2024-11-01.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/language-api/language-api-2024-11-01.json new file mode 100644 index 000000000..508a62be9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/language-api/language-api-2024-11-01.json @@ -0,0 +1,8304 @@ +{ + "swagger": "2.0", + "info": { + "title": "Language Text Analysis", + "version": "2024-11-01", + "description": "The language service API is a suite of natural language processing (NLP) skills built with best-in-class Microsoft machine learning algorithms. The API can be used to analyze unstructured text for tasks such as sentiment analysis, key phrase extraction, language detection and question answering. Further documentation can be found in https://learn.microsoft.com/azure/cognitive-services/language-service/overview.0", + "x-typespec-generated": [ + { + "emitter": "@azure-tools/typespec-autorest" + } + ] + }, + "schemes": [ + "https" + ], + "x-ms-parameterized-host": { + "hostTemplate": "{Endpoint}/language", + "useSchemePrefix": false, + "parameters": [ + { + "name": "Endpoint", + "in": "path", + "description": "Supported Cognitive Services endpoint (e.g., https://.api.cognitiveservices.azure.com).", + "required": true, + "type": "string" + } + ] + }, + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "security": [ + { + "ApiKeyAuth": [] + }, + { + "OAuth2Auth": [ + "https://cognitiveservices.azure.com/.default" + ] + } + ], + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "Ocp-Apim-Subscription-Key", + "in": "header" + }, + "OAuth2Auth": { + "type": "oauth2", + "flow": "accessCode", + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/authorize", + "scopes": { + "https://cognitiveservices.azure.com/.default": "" + }, + "tokenUrl": "https://login.microsoftonline.com/common/oauth2/token" + } + }, + "paths": { + "/:analyze-text": { + "post": { + "operationId": "AnalyzeText", + "description": "Request text analysis over a collection of documents.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "showStats", + "in": "query", + "description": "(Optional) if set to true, response will contain request and document level statistics.", + "required": false, + "type": "boolean" + }, + { + "name": "body", + "in": "body", + "description": "The input documents to analyze.", + "required": true, + "schema": { + "$ref": "#/definitions/AnalyzeTextTask" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "SuccessfulEntityLinkingRequest": { + "$ref": "./examples/SuccessfulEntityLinkingRequest.json" + }, + "SuccessfulEntityRecognitionExclusionRequest": { + "$ref": "./examples/SuccessfulEntityRecognitionExclusionRequest.json" + }, + "SuccessfulEntityRecognitionInclusionRequest": { + "$ref": "./examples/SuccessfulEntityRecognitionInclusionRequest.json" + }, + "SuccessfulEntityRecognitionInferenceOptionsRequest": { + "$ref": "./examples/SuccessfulEntityRecognitionInferenceOptionsRequest.json" + }, + "SuccessfulEntityRecognitionOverlapPolicy": { + "$ref": "./examples/SuccessfulEntityRecognitionOverlapPolicy.json" + }, + "SuccessfulEntityRecognitionRequest": { + "$ref": "./examples/SuccessfulEntityRecognitionRequest.json" + }, + "SuccessfulKeyPhraseExtractionRequest": { + "$ref": "./examples/SuccessfulKeyPhraseExtractionRequest.json" + }, + "SuccessfulLanguageDetectionRequest": { + "$ref": "./examples/SuccessfulLanguageDetectionRequest.json" + }, + "SuccessfulPiiEntityRecognitionRequest": { + "$ref": "./examples/SuccessfulPiiEntityRecognitionRequest.json" + }, + "SuccessfulSentimentAnalysisRequest": { + "$ref": "./examples/SuccessfulSentimentAnalysisRequest.json" + } + } + } + }, + "/analyze-text/jobs": { + "post": { + "operationId": "AnalyzeTextSubmitJob", + "description": "Submit a collection of text documents for analysis. Specify one or more unique tasks to be executed as a long-running operation.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/AnalyzeTextJobsInput" + } + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "SuccessfulAbstractiveSummarizationSummaryLengthTaskSubmit": { + "$ref": "./examples/SuccessfulAbstractiveSummaryLengthSummarizationTaskSubmit.json" + }, + "SuccessfulAbstractiveSummarizationTaskSubmit": { + "$ref": "./examples/SuccessfulAbstractiveSummarizationTaskSubmit.json" + }, + "SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest": { + "$ref": "./examples/SuccessfulAnalyzeTextJobsMultipleTaskSubmitRequest.json" + }, + "SuccessfulHealthcareDocumentTypePostRequest": { + "$ref": "./examples/SuccessfulHealthcareDocumentTypePostRequest.json" + }, + "SuccessfulHealthcarePostRequest": { + "$ref": "./examples/SuccessfulHealthcarePostRequest.json" + } + }, + "x-ms-long-running-operation": true + } + }, + "/analyze-text/jobs/{jobId}": { + "get": { + "operationId": "AnalyzeTextJobStatus", + "summary": "Get analysis status and results", + "description": "Get the status of an analysis job. A job can consist of one or more tasks. After all tasks succeed, the job transitions to the succeeded state and results are available for each task.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "jobId", + "in": "path", + "description": "job ID", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "name": "showStats", + "in": "query", + "description": "(Optional) if set to true, response will contain request and document level statistics.", + "required": false, + "type": "boolean" + }, + { + "name": "top", + "in": "query", + "description": "The maximum number of resources to return from the collection.", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "skip", + "in": "query", + "description": "An offset into the collection of the first resource to be returned.", + "required": false, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "schema": { + "$ref": "#/definitions/AnalyzeTextJobState" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "SuccessfulAbstractiveSummarizationSummaryLengthTaskResult": { + "$ref": "./examples/SuccessfulAbstractiveSummarizationSummaryLengthTaskResult.json" + }, + "SuccessfulAbstractiveSummarizationTaskResult": { + "$ref": "./examples/SuccessfulAbstractiveSummarizationTaskResult.json" + }, + "SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest": { + "$ref": "./examples/SuccessfulAnalyzeTextJobsMultipleTaskStatusRequest.json" + }, + "SuccessfulHealthcareDocumentTypeTaskStatusRequest": { + "$ref": "./examples/SuccessfulHealthcareDocumentTypeTaskStatusRequest.json" + }, + "SuccessfulHealthcareTaskStatusRequest": { + "$ref": "./examples/SuccessfulHealthcareTaskStatusRequest.json" + } + } + } + }, + "/analyze-text/jobs/{jobId}:cancel": { + "post": { + "operationId": "AnalyzeTextCancelJob", + "summary": "Cancel a long-running Text Analysis job.", + "description": "Cancel a long-running Text Analysis job.", + "parameters": [ + { + "$ref": "#/parameters/Azure.Core.Foundations.ApiVersionParameter" + }, + { + "name": "jobId", + "in": "path", + "description": "The job ID to cancel.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "202": { + "description": "The request has been accepted for processing, but processing has not yet completed.", + "headers": { + "Operation-Location": { + "type": "string", + "format": "uri", + "description": "The location for monitoring the operation state." + } + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/ErrorResponse" + }, + "headers": { + "x-ms-error-code": { + "type": "string", + "description": "String error code indicating what went wrong." + } + } + } + }, + "x-ms-examples": { + "SuccessfulAnalyzeTextJobsCancelRequest": { + "$ref": "./examples/SuccessfulAnalyzeTextJobsCancelRequest.json" + } + }, + "x-ms-long-running-operation": true + } + } + }, + "definitions": { + "AbstractiveSummarizationLROResult": { + "type": "object", + "description": "An object representing the results for an Abstractive Summarization task.", + "properties": { + "results": { + "$ref": "#/definitions/AbstractiveSummarizationResult", + "description": "Results of the task." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "AbstractiveSummarizationLROResults" + }, + "AbstractiveSummarizationLROTask": { + "type": "object", + "description": "An object representing the task definition for an Abstractive Summarization task.", + "properties": { + "parameters": { + "$ref": "#/definitions/AbstractiveSummarizationTaskParameters", + "description": "Parameters for the Abstractive Summarization task." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "AbstractiveSummarization" + }, + "AbstractiveSummarizationResult": { + "type": "object", + "description": "An object representing the pre-built Abstractive Summarization results of each document.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/AbstractiveSummaryDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "AbstractiveSummarizationTaskParameters": { + "type": "object", + "description": "Supported parameters for the pre-built Abstractive Summarization task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "sentenceCount": { + "type": "integer", + "format": "int32", + "description": "Controls the approximate number of sentences in the output summaries." + }, + "stringIndexType": { + "type": "string", + "description": "String index type", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + }, + "summaryLength": { + "$ref": "#/definitions/SummaryLengthBucket", + "description": "(NOTE: Recommended to use summaryLength over sentenceCount) Controls the approximate length of the output summaries." + } + } + }, + "AbstractiveSummary": { + "type": "object", + "description": "An object representing a single summary with context for given document.", + "properties": { + "text": { + "type": "string", + "description": "The text of the summary." + }, + "contexts": { + "type": "array", + "description": "The context list of the summary.", + "items": { + "$ref": "#/definitions/SummaryContext" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "text" + ] + }, + "AbstractiveSummaryDocumentResult": { + "type": "object", + "description": "An object representing the pre-built Abstractive Summarization results of each document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "summaries": { + "type": "array", + "description": "A list of abstractive summaries.", + "items": { + "$ref": "#/definitions/AbstractiveSummary" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "summaries" + ] + }, + "AgeMetadata": { + "type": "object", + "description": "Represents the Age entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/AgeUnit", + "description": "Unit of measure for age." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "AgeMetadata" + }, + "AgeUnit": { + "type": "string", + "description": "The Age Unit of measurement", + "enum": [ + "Unspecified", + "Year", + "Month", + "Week", + "Day" + ], + "x-ms-enum": { + "name": "AgeUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified time period" + }, + { + "name": "Year", + "value": "Year", + "description": "Time period of a year" + }, + { + "name": "Month", + "value": "Month", + "description": "Time period of a month" + }, + { + "name": "Week", + "value": "Week", + "description": "Time period of a week" + }, + { + "name": "Day", + "value": "Day", + "description": "Time period of a day" + } + ] + } + }, + "AllowOverlapEntityPolicyType": { + "type": "object", + "description": "Represents the allow overlap policy. Will apply no post processing logic for the entities. Whatever the model predicts is what will be returned to the user. This allows the user to get a full view of every single model's possible values and apply their own custom logic on entity selection", + "allOf": [ + { + "$ref": "#/definitions/BaseEntityOverlapPolicy" + } + ], + "x-ms-discriminator-value": "allowOverlap" + }, + "AnalyzeTextEntityLinkingInput": { + "type": "object", + "description": "Contains the analyze text Entity linking input.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "Contains the analysis input to be handled by the service." + }, + "parameters": { + "$ref": "#/definitions/EntityLinkingTaskParameters", + "description": "Task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "EntityLinking" + }, + "AnalyzeTextEntityRecognitionInput": { + "type": "object", + "description": "The entity recognition analyze text input task request.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "The input to be analyzed." + }, + "parameters": { + "$ref": "#/definitions/EntitiesTaskParameters", + "description": "Task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "EntityRecognition" + }, + "AnalyzeTextJobState": { + "type": "object", + "description": "The object containing the analyze job LRO job state.", + "properties": { + "displayName": { + "type": "string", + "description": "display name" + }, + "createdDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time job created" + }, + "expirationDateTime": { + "type": "string", + "format": "date-time", + "description": "Date and time job expires" + }, + "jobId": { + "$ref": "#/definitions/Azure.Core.uuid", + "description": "job ID", + "readOnly": true + }, + "lastUpdatedDateTime": { + "type": "string", + "format": "date-time", + "description": "last updated date and time" + }, + "status": { + "$ref": "#/definitions/State", + "description": "status" + }, + "errors": { + "type": "array", + "description": "errors", + "items": { + "$ref": "#/definitions/Error" + }, + "x-ms-identifiers": [] + }, + "nextLink": { + "type": "string", + "description": "next link" + }, + "tasks": { + "$ref": "#/definitions/Tasks", + "description": "List of tasks." + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + } + }, + "required": [ + "createdDateTime", + "jobId", + "lastUpdatedDateTime", + "status", + "tasks" + ] + }, + "AnalyzeTextJobsInput": { + "type": "object", + "description": "The input object for the analyze text LRO.", + "properties": { + "displayName": { + "type": "string", + "description": "Name for the task." + }, + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "Contains the input to be analyzed." + }, + "tasks": { + "type": "array", + "description": "List of tasks to be performed as part of the LRO.", + "items": { + "$ref": "#/definitions/AnalyzeTextLROTask" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "analysisInput", + "tasks" + ] + }, + "AnalyzeTextKeyPhraseExtractionInput": { + "type": "object", + "description": "Contains the analyze text KeyPhraseExtraction task input.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "Contains the input documents." + }, + "parameters": { + "$ref": "#/definitions/KeyPhraseTaskParameters", + "description": "Key phrase extraction task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "KeyPhraseExtraction" + }, + "AnalyzeTextLROResult": { + "type": "object", + "description": "Contains the AnalyzeText long running operation result object.", + "properties": { + "lastUpdateDateTime": { + "type": "string", + "format": "date-time", + "description": "The last updated time in UTC for the task." + }, + "status": { + "$ref": "#/definitions/State", + "description": "The status of the task at the mentioned last update time." + }, + "taskName": { + "type": "string", + "description": "task name" + }, + "kind": { + "$ref": "#/definitions/AnalyzeTextLROResultsKind", + "description": "Kind of the task." + } + }, + "discriminator": "kind", + "required": [ + "lastUpdateDateTime", + "status", + "kind" + ] + }, + "AnalyzeTextLROResultsKind": { + "type": "string", + "description": "The kind of the response object returned by the analyze-text long running task.", + "enum": [ + "SentimentAnalysisLROResults", + "EntityRecognitionLROResults", + "PiiEntityRecognitionLROResults", + "KeyPhraseExtractionLROResults", + "EntityLinkingLROResults", + "HealthcareLROResults", + "CustomEntityRecognitionLROResults", + "CustomSingleLabelClassificationLROResults", + "CustomMultiLabelClassificationLROResults", + "ExtractiveSummarizationLROResults", + "AbstractiveSummarizationLROResults" + ], + "x-ms-enum": { + "name": "AnalyzeTextLROResultsKind", + "modelAsString": true, + "values": [ + { + "name": "SentimentAnalysisLROResults", + "value": "SentimentAnalysisLROResults", + "description": "Sentiment analysis LRO results" + }, + { + "name": "EntityRecognitionLROResults", + "value": "EntityRecognitionLROResults", + "description": "Entity recognition LRO results" + }, + { + "name": "PiiEntityRecognitionLROResults", + "value": "PiiEntityRecognitionLROResults", + "description": "PII entity recognition LRO results" + }, + { + "name": "KeyPhraseExtractionLROResults", + "value": "KeyPhraseExtractionLROResults", + "description": "Key phrase extraction LRO results" + }, + { + "name": "EntityLinkingLROResults", + "value": "EntityLinkingLROResults", + "description": "Entity linking LRO results" + }, + { + "name": "HealthcareLROResults", + "value": "HealthcareLROResults", + "description": "Healthcare LRO results" + }, + { + "name": "CustomEntityRecognitionLROResults", + "value": "CustomEntityRecognitionLROResults", + "description": "Custom entity recognition LRO results" + }, + { + "name": "CustomSingleLabelClassificationLROResults", + "value": "CustomSingleLabelClassificationLROResults", + "description": "Custom single label classification LRO results" + }, + { + "name": "CustomMultiLabelClassificationLROResults", + "value": "CustomMultiLabelClassificationLROResults", + "description": "Custom multi label classification LRO results" + }, + { + "name": "ExtractiveSummarizationLROResults", + "value": "ExtractiveSummarizationLROResults", + "description": "Extractive summarization LRO results" + }, + { + "name": "AbstractiveSummarizationLROResults", + "value": "AbstractiveSummarizationLROResults", + "description": "Abstractive summarization LRO results" + } + ] + } + }, + "AnalyzeTextLROTask": { + "type": "object", + "description": "The long running task to be performed by the service on the input documents.", + "properties": { + "taskName": { + "type": "string", + "description": "task name" + }, + "kind": { + "$ref": "#/definitions/AnalyzeTextLROTaskKind", + "description": "The kind of task to perform." + } + }, + "discriminator": "kind", + "required": [ + "kind" + ] + }, + "AnalyzeTextLROTaskKind": { + "type": "string", + "description": "The kind of the long running analyze text tasks supported.", + "enum": [ + "SentimentAnalysis", + "EntityRecognition", + "PiiEntityRecognition", + "KeyPhraseExtraction", + "EntityLinking", + "Healthcare", + "CustomEntityRecognition", + "CustomSingleLabelClassification", + "CustomMultiLabelClassification", + "ExtractiveSummarization", + "AbstractiveSummarization" + ], + "x-ms-enum": { + "name": "AnalyzeTextLROTaskKind", + "modelAsString": true, + "values": [ + { + "name": "SentimentAnalysis", + "value": "SentimentAnalysis", + "description": "Sentiment analysis task" + }, + { + "name": "EntityRecognition", + "value": "EntityRecognition", + "description": "Entity recognition task" + }, + { + "name": "PiiEntityRecognition", + "value": "PiiEntityRecognition", + "description": "PII entity recognition task" + }, + { + "name": "KeyPhraseExtraction", + "value": "KeyPhraseExtraction", + "description": "Key phrase extraction task" + }, + { + "name": "EntityLinking", + "value": "EntityLinking", + "description": "Entity linking task" + }, + { + "name": "Healthcare", + "value": "Healthcare", + "description": "Healthcare task" + }, + { + "name": "CustomEntityRecognition", + "value": "CustomEntityRecognition", + "description": "Custom entity recognition task" + }, + { + "name": "CustomSingleLabelClassification", + "value": "CustomSingleLabelClassification", + "description": "Custom single label classification task" + }, + { + "name": "CustomMultiLabelClassification", + "value": "CustomMultiLabelClassification", + "description": "Custom multi label classification task" + }, + { + "name": "ExtractiveSummarization", + "value": "ExtractiveSummarization", + "description": "Extractive summarization task" + }, + { + "name": "AbstractiveSummarization", + "value": "AbstractiveSummarization", + "description": "Abstractive summarization task" + } + ] + } + }, + "AnalyzeTextLanguageDetectionInput": { + "type": "object", + "description": "Contains the language detection document analysis task input.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/LanguageDetectionAnalysisInput", + "description": "Documents to be analyzed." + }, + "parameters": { + "$ref": "#/definitions/LanguageDetectionTaskParameters", + "description": "task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "LanguageDetection" + }, + "AnalyzeTextPiiEntitiesRecognitionInput": { + "type": "object", + "description": "Contains the analyze text PIIEntityRecognition task input.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "Contains the input documents." + }, + "parameters": { + "$ref": "#/definitions/PiiTaskParameters", + "description": "Pii task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "PiiEntityRecognition" + }, + "AnalyzeTextSentimentAnalysisInput": { + "type": "object", + "description": "Contains the analyze text SentimentAnalysis task input.", + "properties": { + "analysisInput": { + "$ref": "#/definitions/MultiLanguageAnalysisInput", + "description": "Contains the input documents." + }, + "parameters": { + "$ref": "#/definitions/SentimentAnalysisTaskParameters", + "description": "Sentiment Analysis task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTask" + } + ], + "x-ms-discriminator-value": "SentimentAnalysis" + }, + "AnalyzeTextTask": { + "type": "object", + "description": "Collection of documents to analyze and a single task to execute.", + "properties": { + "kind": { + "$ref": "#/definitions/AnalyzeTextTaskKind", + "description": "The kind of task to perform." + } + }, + "discriminator": "kind", + "required": [ + "kind" + ] + }, + "AnalyzeTextTaskKind": { + "type": "string", + "description": "The kind of the analyze-text tasks supported.", + "enum": [ + "SentimentAnalysis", + "EntityRecognition", + "PiiEntityRecognition", + "KeyPhraseExtraction", + "LanguageDetection", + "EntityLinking" + ], + "x-ms-enum": { + "name": "AnalyzeTextTaskKind", + "modelAsString": true, + "values": [ + { + "name": "SentimentAnalysis", + "value": "SentimentAnalysis", + "description": "Sentiment analysis task" + }, + { + "name": "EntityRecognition", + "value": "EntityRecognition", + "description": "Entity recognition task" + }, + { + "name": "PiiEntityRecognition", + "value": "PiiEntityRecognition", + "description": "PII entity recognition task" + }, + { + "name": "KeyPhraseExtraction", + "value": "KeyPhraseExtraction", + "description": "Key phrase extraction task" + }, + { + "name": "LanguageDetection", + "value": "LanguageDetection", + "description": "Language detection task" + }, + { + "name": "EntityLinking", + "value": "EntityLinking", + "description": "Entity linking task" + } + ] + } + }, + "AnalyzeTextTaskResult": { + "type": "object", + "description": "The result object for the analyze task.", + "properties": { + "kind": { + "$ref": "#/definitions/AnalyzeTextTaskResultsKind", + "description": "The kind of task result." + } + }, + "discriminator": "kind", + "required": [ + "kind" + ] + }, + "AnalyzeTextTaskResultsKind": { + "type": "string", + "description": "The kind of the response object returned by the analyze-text task.", + "enum": [ + "SentimentAnalysisResults", + "EntityRecognitionResults", + "PiiEntityRecognitionResults", + "KeyPhraseExtractionResults", + "LanguageDetectionResults", + "EntityLinkingResults" + ], + "x-ms-enum": { + "name": "AnalyzeTextTaskResultsKind", + "modelAsString": true, + "values": [ + { + "name": "SentimentAnalysisResults", + "value": "SentimentAnalysisResults", + "description": "Sentiment analysis results" + }, + { + "name": "EntityRecognitionResults", + "value": "EntityRecognitionResults", + "description": "Entity recognition results" + }, + { + "name": "PiiEntityRecognitionResults", + "value": "PiiEntityRecognitionResults", + "description": "PII entity recognition results" + }, + { + "name": "KeyPhraseExtractionResults", + "value": "KeyPhraseExtractionResults", + "description": "Key phrase extraction results" + }, + { + "name": "LanguageDetectionResults", + "value": "LanguageDetectionResults", + "description": "Language detection results" + }, + { + "name": "EntityLinkingResults", + "value": "EntityLinkingResults", + "description": "Entity linking results" + } + ] + } + }, + "AreaMetadata": { + "type": "object", + "description": "Represents the Area entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/AreaUnit", + "description": "Unit of measure for area." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "AreaMetadata" + }, + "AreaUnit": { + "type": "string", + "description": "The area unit of measurement.", + "enum": [ + "Unspecified", + "SquareKilometer", + "SquareHectometer", + "SquareDecameter", + "SquareDecimeter", + "SquareMeter", + "SquareCentimeter", + "SquareMillimeter", + "SquareInch", + "SquareFoot", + "SquareMile", + "SquareYard", + "Acre" + ], + "x-ms-enum": { + "name": "AreaUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified area unit" + }, + { + "name": "SquareKilometer", + "value": "SquareKilometer", + "description": "Area unit in square kilometers" + }, + { + "name": "SquareHectometer", + "value": "SquareHectometer", + "description": "Area unit in square hectometers" + }, + { + "name": "SquareDecameter", + "value": "SquareDecameter", + "description": "Area unit in square decameters" + }, + { + "name": "SquareDecimeter", + "value": "SquareDecimeter", + "description": "Area unit in square decimeters" + }, + { + "name": "SquareMeter", + "value": "SquareMeter", + "description": "Area unit in square meters" + }, + { + "name": "SquareCentimeter", + "value": "SquareCentimeter", + "description": "Area unit in square centimeters" + }, + { + "name": "SquareMillimeter", + "value": "SquareMillimeter", + "description": "Area unit in square millimeters" + }, + { + "name": "SquareInch", + "value": "SquareInch", + "description": "Area unit in square inches" + }, + { + "name": "SquareFoot", + "value": "SquareFoot", + "description": "Area unit in square feet" + }, + { + "name": "SquareMile", + "value": "SquareMile", + "description": "Area unit in square miles" + }, + { + "name": "SquareYard", + "value": "SquareYard", + "description": "Area unit in square yards" + }, + { + "name": "Acre", + "value": "Acre", + "description": "Area unit in acres" + } + ] + } + }, + "Association": { + "type": "string", + "description": "Describes if the entity is the subject of the text or if it describes someone else.", + "enum": [ + "subject", + "other" + ], + "x-ms-enum": { + "name": "Association", + "modelAsString": false, + "values": [ + { + "name": "subject", + "value": "subject", + "description": "Subject association" + }, + { + "name": "other", + "value": "other", + "description": "Other association" + } + ] + } + }, + "Azure.Core.uuid": { + "type": "string", + "format": "uuid", + "description": "Universally Unique Identifier" + }, + "BaseEntityOverlapPolicy": { + "type": "object", + "description": "The abstract base class for entity OverlapPolicy.", + "properties": { + "policyKind": { + "type": "string", + "description": "The entity OverlapPolicy object kind.", + "default": "matchLongest", + "enum": [ + "matchLongest", + "allowOverlap" + ], + "x-ms-enum": { + "name": "policyKind", + "modelAsString": true, + "values": [ + { + "name": "matchLongest", + "value": "matchLongest", + "description": "Represents MatchLongestEntityPolicyType" + }, + { + "name": "allowOverlap", + "value": "allowOverlap", + "description": "Represents AllowOverlapEntityPolicyType" + } + ] + } + } + }, + "discriminator": "policyKind", + "required": [ + "policyKind" + ] + }, + "BaseMetadata": { + "type": "object", + "description": "The abstract base class for entity Metadata.", + "properties": { + "metadataKind": { + "$ref": "#/definitions/MetadataKind", + "description": "The entity Metadata object kind." + } + }, + "discriminator": "metadataKind", + "required": [ + "metadataKind" + ] + }, + "Certainty": { + "type": "string", + "description": "Describes the entities certainty and polarity.", + "enum": [ + "positive", + "positivePossible", + "neutralPossible", + "negativePossible", + "negative" + ], + "x-ms-enum": { + "name": "Certainty", + "modelAsString": false, + "values": [ + { + "name": "positive", + "value": "positive", + "description": "Positive certainty" + }, + { + "name": "positivePossible", + "value": "positivePossible", + "description": "Possibly positive certainty" + }, + { + "name": "neutralPossible", + "value": "neutralPossible", + "description": "Possibly neutral certainty" + }, + { + "name": "negativePossible", + "value": "negativePossible", + "description": "Possibly negative certainty" + }, + { + "name": "negative", + "value": "negative", + "description": "Negative certainty" + } + ] + } + }, + "ClassificationDocumentResult": { + "type": "object", + "description": "Contains the classification doc result for the task.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "class": { + "type": "array", + "description": "Contains the classification doc results for all docs.", + "items": { + "$ref": "#/definitions/ClassificationResult" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "class" + ] + }, + "ClassificationResult": { + "type": "object", + "description": "Contains the classification result.", + "properties": { + "category": { + "type": "string", + "description": "Classification type." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Confidence score between 0 and 1 of the recognized class." + } + }, + "required": [ + "category", + "confidenceScore" + ] + }, + "Conditionality": { + "type": "string", + "description": "Describes any conditionality on the entity.", + "enum": [ + "hypothetical", + "conditional" + ], + "x-ms-enum": { + "name": "Conditionality", + "modelAsString": false, + "values": [ + { + "name": "hypothetical", + "value": "hypothetical", + "description": "Hypothetical conditionality" + }, + { + "name": "conditional", + "value": "conditional", + "description": "Conditional conditionality" + } + ] + } + }, + "CurrencyMetadata": { + "type": "object", + "description": "Represents the Currency ) entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "type": "string", + "description": "Currency unit." + }, + "iso4217": { + "type": "string", + "description": "The alphabetic code based on another ISO standard, ISO 3166, which lists the codes for country names. The first two letters of the ISO 4217 three-letter code are the same as the code for the country name, and, where possible, the third letter corresponds to the first letter of the currency name." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "CurrencyMetadata" + }, + "CustomEntitiesLROTask": { + "type": "object", + "description": "Contains the custom text LRO task.", + "properties": { + "parameters": { + "$ref": "#/definitions/CustomEntitiesTaskParameters", + "description": "task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "CustomEntityRecognition" + }, + "CustomEntitiesResult": { + "type": "object", + "description": "Contains the list of detected custom entities result for the documents.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "projectName": { + "type": "string", + "description": "This field indicates the project name for the model." + }, + "deploymentName": { + "type": "string", + "description": "This field indicates the deployment name for the model." + }, + "documents": { + "type": "array", + "description": "Enumeration of the document results.", + "items": { + "$ref": "#/definitions/EntitiesDocumentResult" + } + } + }, + "required": [ + "errors", + "projectName", + "deploymentName", + "documents" + ] + }, + "CustomEntitiesTaskParameters": { + "type": "object", + "description": "Supported parameters for a Custom Entities task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "projectName": { + "type": "string", + "description": "This field indicates the project name for the model." + }, + "deploymentName": { + "type": "string", + "description": "This field indicates the deployment name for the model." + }, + "stringIndexType": { + "type": "string", + "description": "Optional parameter to provide the string index type used to interpret string offsets. Defaults to TextElements (Graphemes).", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + } + }, + "required": [ + "projectName", + "deploymentName" + ] + }, + "CustomEntityRecognitionLROResult": { + "type": "object", + "description": "Contains the custom entity recognition job result.", + "properties": { + "results": { + "$ref": "#/definitions/CustomEntitiesResult", + "description": "List of results." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "CustomEntityRecognitionLROResults" + }, + "CustomLabelClassificationResult": { + "type": "object", + "description": "Contains the custom label classification results.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "projectName": { + "type": "string", + "description": "This field indicates the project name for the model." + }, + "deploymentName": { + "type": "string", + "description": "This field indicates the deployment name for the model." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/ClassificationDocumentResult" + } + } + }, + "required": [ + "errors", + "projectName", + "deploymentName", + "documents" + ] + }, + "CustomMultiLabelClassificationLROResult": { + "type": "object", + "description": "Contains the custom multi label classification job result.", + "properties": { + "results": { + "$ref": "#/definitions/CustomLabelClassificationResult", + "description": "List of results." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "CustomMultiLabelClassificationLROResults" + }, + "CustomMultiLabelClassificationLROTask": { + "type": "object", + "description": "Use custom models to classify text into multi label taxonomy", + "properties": { + "parameters": { + "$ref": "#/definitions/CustomMultiLabelClassificationTaskParameters", + "description": "Task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "CustomMultiLabelClassification" + }, + "CustomMultiLabelClassificationTaskParameters": { + "type": "object", + "description": "Supported parameters for a Custom Multi Classification task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "projectName": { + "type": "string", + "description": "This field indicates the project name for the model." + }, + "deploymentName": { + "type": "string", + "description": "This field indicates the deployment name for the model." + } + }, + "required": [ + "projectName", + "deploymentName" + ] + }, + "CustomSingleLabelClassificationLROResult": { + "type": "object", + "description": "Contains the custom single label classification job result.", + "properties": { + "results": { + "$ref": "#/definitions/CustomLabelClassificationResult", + "description": "List of results." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "CustomSingleLabelClassificationLROResults" + }, + "CustomSingleLabelClassificationLROTask": { + "type": "object", + "description": "Use custom models to classify text into single label taxonomy", + "properties": { + "parameters": { + "$ref": "#/definitions/CustomSingleLabelClassificationTaskParameters", + "description": "Task parameters" + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "CustomSingleLabelClassification" + }, + "CustomSingleLabelClassificationTaskParameters": { + "type": "object", + "description": "Supported parameters for a Custom Single Classification task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "projectName": { + "type": "string", + "description": "This field indicates the project name for the model." + }, + "deploymentName": { + "type": "string", + "description": "This field indicates the deployment name for the model." + } + }, + "required": [ + "projectName", + "deploymentName" + ] + }, + "DateMetadata": { + "type": "object", + "description": "A Metadata for date entity instances.", + "properties": { + "dateValues": { + "type": "array", + "description": "List of date values.", + "items": { + "$ref": "#/definitions/DateValue" + }, + "x-ms-identifiers": [] + } + }, + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "DateMetadata" + }, + "DateTimeMetadata": { + "type": "object", + "description": "A Metadata for datetime entity instances.", + "properties": { + "dateValues": { + "type": "array", + "description": "List of date values.", + "items": { + "$ref": "#/definitions/DateValue" + }, + "x-ms-identifiers": [] + } + }, + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "DateTimeMetadata" + }, + "DateValue": { + "type": "object", + "description": "Represents the date value.", + "properties": { + "timex": { + "type": "string", + "description": "An extended ISO 8601 date/time representation as described in (https://github.com/Microsoft/Recognizers-Text/blob/master/Patterns/English/English-DateTime.yaml)" + }, + "value": { + "type": "string", + "description": "The actual time that the extracted text denote." + }, + "modifier": { + "$ref": "#/definitions/TemporalModifier", + "description": "Modifier for datetime to indicate point of reference like before, after etc." + } + }, + "required": [ + "timex", + "value" + ] + }, + "DetectedLanguage": { + "type": "object", + "description": "Contains the details of the detected language for the text.", + "properties": { + "name": { + "type": "string", + "description": "Long name of a detected language (e.g. English, French)." + }, + "iso6391Name": { + "type": "string", + "description": "A two letter representation of the detected language according to the ISO 639-1 standard (e.g. en, fr)." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "A confidence score between 0 and 1. Scores close to 1 indicate 100% certainty that the identified language is true." + }, + "scriptName": { + "$ref": "#/definitions/ScriptKind", + "description": "Identifies the script name of the input document according to the ISO 15924 standard." + }, + "scriptIso15924Code": { + "$ref": "#/definitions/ScriptCode", + "description": "Identifies the script code of the input document according to the ISO 15924 standard." + } + }, + "required": [ + "name", + "iso6391Name", + "confidenceScore" + ] + }, + "DocumentError": { + "type": "object", + "description": "Contains details of errors encountered during a job execution.", + "properties": { + "id": { + "type": "string", + "description": "The ID of the input document." + }, + "error": { + "$ref": "#/definitions/Error", + "description": "Error encountered." + } + }, + "required": [ + "id", + "error" + ] + }, + "DocumentSentimentValue": { + "type": "string", + "description": "Predicted sentiment for document (Negative, Neutral, Positive, or Mixed).", + "enum": [ + "positive", + "neutral", + "negative", + "mixed" + ], + "x-ms-enum": { + "name": "DocumentSentimentValue", + "modelAsString": false, + "values": [ + { + "name": "positive", + "value": "positive", + "description": "Positive statement" + }, + { + "name": "neutral", + "value": "neutral", + "description": "Neutral statement" + }, + { + "name": "negative", + "value": "negative", + "description": "Negative statement" + }, + { + "name": "mixed", + "value": "mixed", + "description": "Mixed statement" + } + ] + } + }, + "DocumentStatistics": { + "type": "object", + "description": "if showStats=true was specified in the request this field will contain information about the document payload.", + "properties": { + "charactersCount": { + "type": "integer", + "format": "int32", + "description": "Number of text elements recognized in the document." + }, + "transactionsCount": { + "type": "integer", + "format": "int32", + "description": "Number of transactions for the document." + } + }, + "required": [ + "charactersCount", + "transactionsCount" + ] + }, + "DocumentWarning": { + "type": "object", + "description": "Contains the warnings object with warnings encountered for the processed document.", + "properties": { + "code": { + "$ref": "#/definitions/WarningCodeValue", + "description": "Warning code." + }, + "message": { + "type": "string", + "description": "Warning message." + }, + "targetRef": { + "type": "string", + "description": "A JSON pointer reference indicating the target object." + } + }, + "required": [ + "code", + "message" + ] + }, + "EntitiesDocumentResult": { + "type": "object", + "description": "Contains the entity recognition task result for the document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "entities": { + "type": "array", + "description": "Recognized entities in the document.", + "items": { + "$ref": "#/definitions/Entity" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "entities" + ] + }, + "EntitiesDocumentResultWithMetadata": { + "type": "object", + "description": "Entity documents result with metadata.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "entities": { + "type": "array", + "description": "Recognized entities in the document.", + "items": { + "$ref": "#/definitions/EntityWithMetadata" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "entities" + ] + }, + "EntitiesLROTask": { + "type": "object", + "description": "An object representing the task definition for an Entities Recognition task.", + "properties": { + "parameters": { + "$ref": "#/definitions/EntitiesTaskParameters", + "description": "Task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "EntityRecognition" + }, + "EntitiesResult": { + "type": "object", + "description": "Contains the entity recognition task result.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/EntitiesDocumentResultWithMetadata" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "EntitiesTaskParameters": { + "type": "object", + "description": "Supported parameters for an Entity Recognition task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "stringIndexType": { + "type": "string", + "description": "(Optional) parameter to provide the string index type used to interpret string offsets. Defaults to TextElements (Graphemes).", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + }, + "inclusionList": { + "type": "array", + "description": "(Optional) request parameter that limits the output to the requested entity types included in this list. We will apply inclusionList before exclusionList", + "items": { + "$ref": "#/definitions/EntityCategory" + } + }, + "exclusionList": { + "type": "array", + "description": "(Optional) request parameter that filters out any entities that are included the excludeList. When a user specifies an excludeList, they cannot get a prediction returned with an entity in that list. We will apply inclusionList before exclusionList", + "items": { + "$ref": "#/definitions/EntityCategory" + } + }, + "overlapPolicy": { + "$ref": "#/definitions/BaseEntityOverlapPolicy", + "description": "(Optional) describes the type of overlap policy to apply to the ner output." + }, + "inferenceOptions": { + "$ref": "#/definitions/EntityInferenceOptions", + "description": "(Optional) request parameter that allows the user to provide settings for running the inference." + } + } + }, + "EntitiesTaskResult": { + "type": "object", + "description": "Contains the entity task", + "properties": { + "results": { + "$ref": "#/definitions/EntitiesResult", + "description": "Results for entity recognition." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "EntityRecognitionResults" + }, + "Entity": { + "type": "object", + "description": "Defines the detected entity object containing the entity category and entity text detected, etc.", + "properties": { + "text": { + "type": "string", + "description": "Entity text as appears in the request." + }, + "category": { + "type": "string", + "description": "Entity type." + }, + "subcategory": { + "type": "string", + "description": "(Optional) Entity sub type." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "Start position for the entity text. Use of different 'stringIndexType' values can affect the offset returned." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Confidence score between 0 and 1 of the extracted entity." + } + }, + "required": [ + "text", + "category", + "offset", + "length", + "confidenceScore" + ] + }, + "EntityCategory": { + "type": "string", + "description": "Contains all the entity categories detected by entity recognition.", + "enum": [ + "Address", + "Numeric", + "Age", + "Currency", + "Number", + "NumberRange", + "Percentage", + "Ordinal", + "Temperature", + "Dimension", + "Length", + "Weight", + "Height", + "Speed", + "Area", + "Volume", + "Information", + "Temporal", + "Date", + "Time", + "DateTime", + "DateRange", + "TimeRange", + "DateTimeRange", + "Duration", + "SetTemporal", + "Event", + "SportsEvent", + "CulturalEvent", + "NaturalEvent", + "Location", + "GPE", + "City", + "State", + "CountryRegion", + "Continent", + "Structural", + "Airport", + "Geological", + "Organization", + "OrganizationMedical", + "OrganizationStockExchange", + "OrganizationSports", + "Person", + "PersonType", + "Email", + "URL", + "IP", + "PhoneNumber", + "Product", + "ComputingProduct", + "Skill" + ], + "x-ms-enum": { + "name": "EntityCategory", + "modelAsString": true, + "values": [ + { + "name": "Address", + "value": "Address", + "description": "Specific street-level mentions of locations: house/building numbers, streets, avenues, highways, intersections referenced by name." + }, + { + "name": "Numeric", + "value": "Numeric", + "description": "Numeric values, including digits and number words." + }, + { + "name": "Age", + "value": "Age", + "description": "Age-related values." + }, + { + "name": "Currency", + "value": "Currency", + "description": "Currency-related values." + }, + { + "name": "Number", + "value": "Number", + "description": "Numbers without a unit" + }, + { + "name": "NumberRange", + "value": "NumberRange", + "description": "Range of Numbers" + }, + { + "name": "Percentage", + "value": "Percentage", + "description": "Percentage-related values." + }, + { + "name": "Ordinal", + "value": "Ordinal", + "description": "Ordinal numbers." + }, + { + "name": "Temperature", + "value": "Temperature", + "description": "Temperature-related values." + }, + { + "name": "Dimension", + "value": "Dimension", + "description": "Dimension of measurements" + }, + { + "name": "Length", + "value": "Length", + "description": "Length of an object." + }, + { + "name": "Weight", + "value": "Weight", + "description": "Weight of an object." + }, + { + "name": "Height", + "value": "Height", + "description": "Height of an object." + }, + { + "name": "Speed", + "value": "Speed", + "description": "Speed of an object." + }, + { + "name": "Area", + "value": "Area", + "description": "Area of an object." + }, + { + "name": "Volume", + "value": "Volume", + "description": "Volume of an object." + }, + { + "name": "Information", + "value": "Information", + "description": "Unit of measure for digital information." + }, + { + "name": "Temporal", + "value": "Temporal", + "description": "Items relating to time." + }, + { + "name": "Date", + "value": "Date", + "description": "Calendar dates." + }, + { + "name": "Time", + "value": "Time", + "description": "Times of day." + }, + { + "name": "DateTime", + "value": "DateTime", + "description": "Calendar dates with time." + }, + { + "name": "DateRange", + "value": "DateRange", + "description": "Range of dates." + }, + { + "name": "TimeRange", + "value": "TimeRange", + "description": "Range of times." + }, + { + "name": "DateTimeRange", + "value": "DateTimeRange", + "description": "Range of date and time." + }, + { + "name": "Duration", + "value": "Duration", + "description": "Duration of time." + }, + { + "name": "SetTemporal", + "value": "SetTemporal", + "description": "Set of time-related values." + }, + { + "name": "Event", + "value": "Event", + "description": "Social, sports, business, political, educational, natural, historical, criminal, violent, legal, military events with a timed period." + }, + { + "name": "SportsEvent", + "value": "SportsEvent", + "description": "Sports event-related values." + }, + { + "name": "CulturalEvent", + "value": "CulturalEvent", + "description": "Cultural event-related values." + }, + { + "name": "NaturalEvent", + "value": "NaturalEvent", + "description": "Natural event-related values." + }, + { + "name": "Location", + "value": "Location", + "description": "Particular point or place in physical space." + }, + { + "name": "GPE", + "value": "GPE", + "description": "Cities, countries/regions, states." + }, + { + "name": "City", + "value": "City", + "description": "City-related values." + }, + { + "name": "State", + "value": "State", + "description": "State-related values." + }, + { + "name": "CountryRegion", + "value": "CountryRegion", + "description": "Country or region-related values." + }, + { + "name": "Continent", + "value": "Continent", + "description": "Continent-related values." + }, + { + "name": "Structural", + "value": "Structural", + "description": "Manmade structures." + }, + { + "name": "Airport", + "value": "Airport", + "description": "Airports." + }, + { + "name": "Geological", + "value": "Geological", + "description": "Geographic and natural features such as rivers, oceans, and deserts." + }, + { + "name": "Organization", + "value": "Organization", + "description": "Corporations, agencies, and other groups of people defined by some established organizational structure. These labels can include companies, political parties/movements, musical bands, sport clubs, government bodies, and public organizations. Nationalities or religions are not ORGANIZATION." + }, + { + "name": "OrganizationMedical", + "value": "OrganizationMedical", + "description": "Medical companies and groups." + }, + { + "name": "OrganizationStockExchange", + "value": "OrganizationStockExchange", + "description": "Stock exchange groups." + }, + { + "name": "OrganizationSports", + "value": "OrganizationSports", + "description": "Sports-related organizations." + }, + { + "name": "Person", + "value": "Person", + "description": "First, last, and middle names, names of fictional characters, and aliases. Titles, such as 'Mr.' or 'President', are not considered part of the named entity." + }, + { + "name": "PersonType", + "value": "PersonType", + "description": "Human roles classified by a group membership." + }, + { + "name": "Email", + "value": "Email", + "description": "Email addresses." + }, + { + "name": "URL", + "value": "URL", + "description": "URLs to websites." + }, + { + "name": "IP", + "value": "IP", + "description": "network IP addresses." + }, + { + "name": "PhoneNumber", + "value": "PhoneNumber", + "description": "Phone numbers (US and EU phone numbers only)." + }, + { + "name": "Product", + "value": "Product", + "description": "Single or group of commercial, consumable objects, electronics, vehicles, food groups." + }, + { + "name": "ComputingProduct", + "value": "ComputingProduct", + "description": "Computing products." + }, + { + "name": "Skill", + "value": "Skill", + "description": "A capability, skill, or expertise." + } + ] + } + }, + "EntityInferenceOptions": { + "type": "object", + "description": "The class that houses the inference options allowed for named entity recognition.", + "properties": { + "excludeNormalizedValues": { + "type": "boolean", + "description": "Option to include/exclude the detected entity values to be normalized and included in the metadata. The numeric and temporal entity types support value normalization.", + "default": false + } + } + }, + "EntityLinkingLROResult": { + "type": "object", + "description": "Contains the analyze text Entity linking task LRO result.", + "properties": { + "results": { + "$ref": "#/definitions/EntityLinkingResult", + "description": "Entity linking result." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "EntityLinkingLROResults" + }, + "EntityLinkingLROTask": { + "type": "object", + "description": "Contains the analyze text Entity linking LRO task.", + "properties": { + "parameters": { + "$ref": "#/definitions/EntityLinkingTaskParameters", + "description": "Task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "EntityLinking" + }, + "EntityLinkingResult": { + "type": "object", + "description": "Entity linking result.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/LinkedEntitiesDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "EntityLinkingTaskParameters": { + "type": "object", + "description": "Supported parameters for an Entity Linking task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "stringIndexType": { + "type": "string", + "description": "Optional parameter to provide the string index type used to interpret string offsets. Defaults to TextElements (Graphemes).", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + } + } + }, + "EntityLinkingTaskResult": { + "type": "object", + "description": "Contains the analyze text Entity linking task result.", + "properties": { + "results": { + "$ref": "#/definitions/EntityLinkingResult", + "description": "Entity linking result." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "EntityLinkingResults" + }, + "EntityRecognitionLROResult": { + "type": "object", + "description": "Contains the entity recognition job task result.", + "properties": { + "results": { + "$ref": "#/definitions/EntitiesResult", + "description": "Results for the task." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "EntityRecognitionLROResults" + }, + "EntityTag": { + "type": "object", + "description": "Entity tag object which contains the name of the tags abd any associated confidence score. Entity Tags are used to express some similarities/affinity between entities.", + "properties": { + "name": { + "type": "string", + "description": "Name of the tag. Entity Tag names will be unique globally." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Detection score between 0 and 1 of the extracted entity." + } + }, + "required": [ + "name" + ] + }, + "EntityWithMetadata": { + "type": "object", + "description": "Entity object with tags and metadata.", + "properties": { + "text": { + "type": "string", + "description": "Entity text as appears in the request." + }, + "category": { + "type": "string", + "description": "Entity type." + }, + "subcategory": { + "type": "string", + "description": "(Optional) Entity sub type." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "Start position for the entity text. Use of different 'stringIndexType' values can affect the offset returned." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Confidence score between 0 and 1 of the extracted entity." + }, + "type": { + "type": "string", + "description": "An entity type is the lowest (or finest) granularity at which the entity has been detected. The type maps to the specific metadata attributes associated with the entity detected." + }, + "tags": { + "type": "array", + "description": "List of entity tags. Tags are to express some similarities/affinity between entities.", + "items": { + "$ref": "#/definitions/EntityTag" + }, + "x-ms-identifiers": [] + }, + "metadata": { + "$ref": "#/definitions/BaseMetadata", + "description": "The entity metadata object." + } + }, + "required": [ + "text", + "category", + "offset", + "length", + "confidenceScore" + ] + }, + "Error": { + "type": "object", + "description": "The error response object returned when the service encounters some errors during processing the request.", + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "description": "An array of details about specific errors that led to this reported error.", + "items": { + "$ref": "#/definitions/Error" + }, + "x-ms-identifiers": [] + }, + "innererror": { + "$ref": "#/definitions/InnerErrorModel", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "ErrorCode": { + "type": "string", + "description": "Human-readable error code.", + "enum": [ + "InvalidRequest", + "InvalidArgument", + "Unauthorized", + "Forbidden", + "NotFound", + "ProjectNotFound", + "OperationNotFound", + "AzureCognitiveSearchNotFound", + "AzureCognitiveSearchIndexNotFound", + "TooManyRequests", + "AzureCognitiveSearchThrottling", + "AzureCognitiveSearchIndexLimitReached", + "InternalServerError", + "ServiceUnavailable", + "Timeout", + "QuotaExceeded", + "Conflict", + "Warning" + ], + "x-ms-enum": { + "name": "ErrorCode", + "modelAsString": true, + "values": [ + { + "name": "InvalidRequest", + "value": "InvalidRequest", + "description": "Invalid request error" + }, + { + "name": "InvalidArgument", + "value": "InvalidArgument", + "description": "Invalid argument error" + }, + { + "name": "Unauthorized", + "value": "Unauthorized", + "description": "Unauthorized access error" + }, + { + "name": "Forbidden", + "value": "Forbidden", + "description": "Forbidden access error" + }, + { + "name": "NotFound", + "value": "NotFound", + "description": "Not found error" + }, + { + "name": "ProjectNotFound", + "value": "ProjectNotFound", + "description": "Project not found error" + }, + { + "name": "OperationNotFound", + "value": "OperationNotFound", + "description": "Operation not found error" + }, + { + "name": "AzureCognitiveSearchNotFound", + "value": "AzureCognitiveSearchNotFound", + "description": "Azure Cognitive Search not found error" + }, + { + "name": "AzureCognitiveSearchIndexNotFound", + "value": "AzureCognitiveSearchIndexNotFound", + "description": "Azure Cognitive Search index not found error" + }, + { + "name": "TooManyRequests", + "value": "TooManyRequests", + "description": "Too many requests error" + }, + { + "name": "AzureCognitiveSearchThrottling", + "value": "AzureCognitiveSearchThrottling", + "description": "Azure Cognitive Search throttling error" + }, + { + "name": "AzureCognitiveSearchIndexLimitReached", + "value": "AzureCognitiveSearchIndexLimitReached", + "description": "Azure Cognitive Search index limit reached error" + }, + { + "name": "InternalServerError", + "value": "InternalServerError", + "description": "Internal server error" + }, + { + "name": "ServiceUnavailable", + "value": "ServiceUnavailable", + "description": "Service unavailable error" + }, + { + "name": "Timeout", + "value": "Timeout", + "description": "Timeout error" + }, + { + "name": "QuotaExceeded", + "value": "QuotaExceeded", + "description": "Quota exceeded error" + }, + { + "name": "Conflict", + "value": "Conflict", + "description": "Conflict error" + }, + { + "name": "Warning", + "value": "Warning", + "description": "Warning error" + } + ] + } + }, + "ErrorResponse": { + "type": "object", + "description": "Error response.", + "properties": { + "error": { + "$ref": "#/definitions/Error", + "description": "The error object." + } + }, + "required": [ + "error" + ] + }, + "ExtractedSummaryDocumentResult": { + "type": "object", + "description": "A ranked list of sentences representing the extracted summary.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "sentences": { + "type": "array", + "description": "Specifies the the extracted sentences from the input document.", + "items": { + "$ref": "#/definitions/ExtractedSummarySentence" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "sentences" + ] + }, + "ExtractedSummarySentence": { + "type": "object", + "description": "Represents an extracted sentences from the input document.", + "properties": { + "text": { + "type": "string", + "description": "The extracted sentence text." + }, + "rankScore": { + "type": "number", + "format": "double", + "description": "A double value representing the relevance of the sentence within the summary. Higher values indicate higher importance." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "The sentence offset from the start of the document, based on the value of the parameter StringIndexType." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "The length of the sentence." + } + }, + "required": [ + "text", + "rankScore", + "offset", + "length" + ] + }, + "ExtractiveSummarizationLROResult": { + "type": "object", + "description": "An object representing the results for an Extractive Summarization task.", + "properties": { + "results": { + "$ref": "#/definitions/ExtractiveSummarizationResult", + "description": "Results of the task." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "ExtractiveSummarizationLROResults" + }, + "ExtractiveSummarizationLROTask": { + "type": "object", + "description": "An object representing the task definition for an Extractive Summarization task.", + "properties": { + "parameters": { + "$ref": "#/definitions/ExtractiveSummarizationTaskParameters", + "description": "Parameters for the Extractive Summarization task." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "ExtractiveSummarization" + }, + "ExtractiveSummarizationResult": { + "type": "object", + "description": "An object representing the pre-built Extractive Summarization results of each document.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/ExtractedSummaryDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "ExtractiveSummarizationTaskParameters": { + "type": "object", + "description": "Supported parameters for an Extractive Summarization task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "sentenceCount": { + "type": "integer", + "format": "int64", + "description": "Specifies the number of sentences in the extracted summary.", + "default": 3 + }, + "sortBy": { + "type": "string", + "description": "Specifies how to sort the extracted summaries.", + "default": "Offset", + "enum": [ + "Offset", + "Rank" + ], + "x-ms-enum": { + "name": "ExtractiveSummarizationSortingCriteria", + "modelAsString": true, + "values": [ + { + "name": "Offset", + "value": "Offset", + "description": "Indicates that results should be sorted in order of appearance in the text." + }, + { + "name": "Rank", + "value": "Rank", + "description": "Indicates that results should be sorted in order of importance (i.e. rank score) according to the model." + } + ] + } + }, + "stringIndexType": { + "type": "string", + "description": "Specifies the method used to interpret string offsets.", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + } + } + }, + "FhirBundle": { + "type": "object", + "description": "JSON bundle containing a FHIR compatible object for consumption in other Healthcare tools. For additional information see https://www.hl7.org/fhir/overview.html.", + "additionalProperties": {} + }, + "HealthcareAssertion": { + "type": "object", + "description": "Assertion of the entity.", + "properties": { + "conditionality": { + "$ref": "#/definitions/Conditionality", + "description": "Describes any conditionality on the entity." + }, + "certainty": { + "$ref": "#/definitions/Certainty", + "description": "Describes the entities certainty and polarity." + }, + "association": { + "$ref": "#/definitions/Association", + "description": "Describes if the entity is the subject of the text or if it describes someone else." + }, + "temporality": { + "$ref": "#/definitions/Temporality", + "description": "Describes temporal information regarding the entity." + } + } + }, + "HealthcareEntitiesDocumentResult": { + "type": "object", + "description": "Result object for the processed Healthcare document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "entities": { + "type": "array", + "description": "Healthcare entities.", + "items": { + "$ref": "#/definitions/HealthcareEntity" + }, + "x-ms-identifiers": [] + }, + "relations": { + "type": "array", + "description": "Healthcare entity relations.", + "items": { + "$ref": "#/definitions/HealthcareRelation" + }, + "x-ms-identifiers": [] + }, + "fhirBundle": { + "$ref": "#/definitions/FhirBundle", + "description": "JSON bundle containing a FHIR compatible object for consumption in other Healthcare tools. For additional information see https://www.hl7.org/fhir/overview.html." + } + }, + "required": [ + "id", + "warnings", + "entities", + "relations" + ] + }, + "HealthcareEntity": { + "type": "object", + "description": "Healthcare entity extracted from the document", + "properties": { + "text": { + "type": "string", + "description": "Entity text as appears in the request." + }, + "category": { + "$ref": "#/definitions/healthcareEntityCategory", + "description": "Healthcare Entity Category." + }, + "subcategory": { + "type": "string", + "description": "(Optional) Entity sub type." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "Start position for the entity text. Use of different 'stringIndexType' values can affect the offset returned." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "Length for the entity text. Use of different 'stringIndexType' values can affect the length returned." + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Confidence score between 0 and 1 of the extracted entity." + }, + "assertion": { + "$ref": "#/definitions/HealthcareAssertion", + "description": "Assertion of the entity." + }, + "name": { + "type": "string", + "description": "Preferred name for the entity. Example: 'histologically' would have a 'name' of 'histologic'." + }, + "links": { + "type": "array", + "description": "Entity references in known data sources.", + "items": { + "$ref": "#/definitions/HealthcareEntityLink" + } + } + }, + "required": [ + "text", + "category", + "offset", + "length", + "confidenceScore" + ] + }, + "HealthcareEntityLink": { + "type": "object", + "description": "Reference to an entity in known data sources.", + "properties": { + "dataSource": { + "type": "string", + "description": "Entity Catalog. Examples include: UMLS, CHV, MSH, etc." + }, + "id": { + "type": "string", + "description": "Entity id in the given source catalog." + } + }, + "required": [ + "dataSource", + "id" + ] + }, + "HealthcareLROResult": { + "type": "object", + "description": "Healthcare Analyze Text long tunning operation result object.", + "properties": { + "results": { + "$ref": "#/definitions/HealthcareResult", + "description": "Results of the task." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "HealthcareLROResults" + }, + "HealthcareLROTask": { + "type": "object", + "description": "The long running task to be performed by the service on the Healthcare input documents.", + "properties": { + "parameters": { + "$ref": "#/definitions/HealthcareTaskParameters", + "description": "Parameters for the Healthcare task." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "Healthcare" + }, + "HealthcareRelation": { + "type": "object", + "description": "Every relation is an entity graph of a certain relationType, where all entities are connected and have specific roles within the relation context.", + "properties": { + "relationType": { + "$ref": "#/definitions/relationType", + "description": "Type of relation. Examples include: `DosageOfMedication` or 'FrequencyOfMedication', etc." + }, + "entities": { + "type": "array", + "description": "The entities in the relation.", + "items": { + "$ref": "#/definitions/HealthcareRelationEntity" + }, + "x-ms-identifiers": [] + }, + "confidenceScore": { + "type": "number", + "format": "double", + "description": "Confidence score between 0 and 1 of the extracted relation." + } + }, + "required": [ + "relationType", + "entities" + ] + }, + "HealthcareRelationEntity": { + "type": "object", + "description": "Entity in the relation.", + "properties": { + "ref": { + "type": "string", + "description": "Reference link object, using a JSON pointer RFC 6901 (URI Fragment Identifier Representation), pointing to the entity ." + }, + "role": { + "type": "string", + "description": "Role of entity in the relationship. For example: 'CD20-positive diffuse large B-cell lymphoma' has the following entities with their roles in parenthesis: CD20 (GeneOrProtein), Positive (Expression), diffuse large B-cell lymphoma (Diagnosis)." + } + }, + "required": [ + "ref", + "role" + ] + }, + "HealthcareResult": { + "type": "object", + "description": "Result object for the processed Healthcare task.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "List of result objects for the processed Healthcare documents.", + "items": { + "$ref": "#/definitions/HealthcareEntitiesDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "HealthcareTaskParameters": { + "type": "object", + "description": "Supported parameters for a Healthcare task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "stringIndexType": { + "type": "string", + "description": "Specifies the method used to interpret string offsets.", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + }, + "fhirVersion": { + "$ref": "#/definitions/fhirVersion", + "description": "The FHIR Spec version that the result will use to format the fhirBundle. For additional information see https://www.hl7.org/fhir/overview.html." + }, + "documentType": { + "$ref": "#/definitions/healthcareDocumentType", + "description": "Document type that can be provided as input for Fhir Documents. Expect to have fhirVersion provided when used. Behavior of using None enum is the same as not using the documentType parameter." + } + } + }, + "InformationMetadata": { + "type": "object", + "description": "Represents the Information (data) entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/InformationUnit", + "description": "Unit of measure for information." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "InformationMetadata" + }, + "InformationUnit": { + "type": "string", + "description": "The information (data) Unit of measurement.", + "enum": [ + "Unspecified", + "Bit", + "Kilobit", + "Megabit", + "Gigabit", + "Terabit", + "Petabit", + "Byte", + "Kilobyte", + "Megabyte", + "Gigabyte", + "Terabyte", + "Petabyte" + ], + "x-ms-enum": { + "name": "InformationUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified data size unit" + }, + { + "name": "Bit", + "value": "Bit", + "description": "Data size unit in bits" + }, + { + "name": "Kilobit", + "value": "Kilobit", + "description": "Data size unit in kilobits" + }, + { + "name": "Megabit", + "value": "Megabit", + "description": "Data size unit in megabits" + }, + { + "name": "Gigabit", + "value": "Gigabit", + "description": "Data size unit in gigabits" + }, + { + "name": "Terabit", + "value": "Terabit", + "description": "Data size unit in terabits" + }, + { + "name": "Petabit", + "value": "Petabit", + "description": "Data size unit in petabits" + }, + { + "name": "Byte", + "value": "Byte", + "description": "Data size unit in bytes" + }, + { + "name": "Kilobyte", + "value": "Kilobyte", + "description": "Data size unit in kilobytes" + }, + { + "name": "Megabyte", + "value": "Megabyte", + "description": "Data size unit in megabytes" + }, + { + "name": "Gigabyte", + "value": "Gigabyte", + "description": "Data size unit in gigabytes" + }, + { + "name": "Terabyte", + "value": "Terabyte", + "description": "Data size unit in terabytes" + }, + { + "name": "Petabyte", + "value": "Petabyte", + "description": "Data size unit in petabytes" + } + ] + } + }, + "InnerErrorCode": { + "type": "string", + "description": "Human-readable error code.", + "enum": [ + "InvalidRequest", + "InvalidParameterValue", + "KnowledgeBaseNotFound", + "AzureCognitiveSearchNotFound", + "AzureCognitiveSearchThrottling", + "ExtractionFailure", + "InvalidRequestBodyFormat", + "EmptyRequest", + "MissingInputDocuments", + "InvalidDocument", + "ModelVersionIncorrect", + "InvalidDocumentBatch", + "UnsupportedLanguageCode", + "InvalidCountryHint" + ], + "x-ms-enum": { + "name": "InnerErrorCode", + "modelAsString": true, + "values": [ + { + "name": "InvalidRequest", + "value": "InvalidRequest", + "description": "Invalid request error" + }, + { + "name": "InvalidParameterValue", + "value": "InvalidParameterValue", + "description": "Invalid parameter value error" + }, + { + "name": "KnowledgeBaseNotFound", + "value": "KnowledgeBaseNotFound", + "description": "Knowledge base not found error" + }, + { + "name": "AzureCognitiveSearchNotFound", + "value": "AzureCognitiveSearchNotFound", + "description": "Azure Cognitive Search not found error" + }, + { + "name": "AzureCognitiveSearchThrottling", + "value": "AzureCognitiveSearchThrottling", + "description": "Azure Cognitive Search throttling error" + }, + { + "name": "ExtractionFailure", + "value": "ExtractionFailure", + "description": "Extraction failure error" + }, + { + "name": "InvalidRequestBodyFormat", + "value": "InvalidRequestBodyFormat", + "description": "Invalid request body format error" + }, + { + "name": "EmptyRequest", + "value": "EmptyRequest", + "description": "Empty request error" + }, + { + "name": "MissingInputDocuments", + "value": "MissingInputDocuments", + "description": "Missing input documents error" + }, + { + "name": "InvalidDocument", + "value": "InvalidDocument", + "description": "Invalid document error" + }, + { + "name": "ModelVersionIncorrect", + "value": "ModelVersionIncorrect", + "description": "Model version incorrect error" + }, + { + "name": "InvalidDocumentBatch", + "value": "InvalidDocumentBatch", + "description": "Invalid document batch error" + }, + { + "name": "UnsupportedLanguageCode", + "value": "UnsupportedLanguageCode", + "description": "Unsupported language code error" + }, + { + "name": "InvalidCountryHint", + "value": "InvalidCountryHint", + "description": "Invalid country hint error" + } + ] + } + }, + "InnerErrorModel": { + "type": "object", + "description": "An object containing more specific information about the error. As per Microsoft One API guidelines - https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses.", + "properties": { + "code": { + "$ref": "#/definitions/InnerErrorCode", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "Error message." + }, + "details": { + "type": "object", + "description": "Error details.", + "additionalProperties": { + "type": "string" + } + }, + "target": { + "type": "string", + "description": "Error target." + }, + "innererror": { + "$ref": "#/definitions/InnerErrorModel", + "description": "An object containing more specific information than the current object about the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "KeyPhraseExtractionLROResult": { + "type": "object", + "description": "Contains the analyze text KeyPhraseExtraction LRO task.", + "properties": { + "results": { + "$ref": "#/definitions/KeyPhraseResult", + "description": "The list of Key phrase extraction results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "KeyPhraseExtractionLROResults" + }, + "KeyPhraseLROTask": { + "type": "object", + "description": "An object representing the task definition for a Key Phrase Extraction task.", + "properties": { + "parameters": { + "$ref": "#/definitions/KeyPhraseTaskParameters", + "description": "Key phrase extraction task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "KeyPhraseExtraction" + }, + "KeyPhraseResult": { + "type": "object", + "description": "Contains the KeyPhraseResult.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/KeyPhrasesDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "KeyPhraseTaskParameters": { + "type": "object", + "description": "Supported parameters for a Key Phrase Extraction task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + } + } + }, + "KeyPhraseTaskResult": { + "type": "object", + "description": "Contains the analyze text KeyPhraseExtraction task result.", + "properties": { + "results": { + "$ref": "#/definitions/KeyPhraseResult", + "description": "The list of Key phrase extraction results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "KeyPhraseExtractionResults" + }, + "KeyPhrasesDocumentResult": { + "type": "object", + "description": "Contains the Key phrase extraction results for a document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "keyPhrases": { + "type": "array", + "description": "A list of representative words or phrases. The number of key phrases returned is proportional to the number of words in the input document.", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "warnings", + "keyPhrases" + ] + }, + "LanguageDetectionAnalysisInput": { + "type": "object", + "description": "Contains the language detection document analysis input.", + "properties": { + "documents": { + "type": "array", + "description": "List of documents to be analyzed.", + "items": { + "$ref": "#/definitions/LanguageInput" + } + } + } + }, + "LanguageDetectionDocumentResult": { + "type": "object", + "description": "Contains the language detection for a document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "detectedLanguage": { + "$ref": "#/definitions/DetectedLanguage", + "description": "Detected Language." + } + }, + "required": [ + "id", + "warnings", + "detectedLanguage" + ] + }, + "LanguageDetectionResult": { + "type": "object", + "description": "Contains the language detection result for the request.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Enumeration of language detection results for each input document.", + "items": { + "$ref": "#/definitions/LanguageDetectionDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "LanguageDetectionTaskParameters": { + "type": "object", + "description": "Supported parameters for a Language Detection task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + } + } + }, + "LanguageDetectionTaskResult": { + "type": "object", + "description": "Contains the language detection task result for the request.", + "properties": { + "results": { + "$ref": "#/definitions/LanguageDetectionResult", + "description": "Contains the language detection results." + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "LanguageDetectionResults" + }, + "LanguageInput": { + "type": "object", + "description": "Contains the language detection input.", + "properties": { + "id": { + "type": "string", + "description": "A unique, non-empty document identifier." + }, + "text": { + "type": "string", + "description": "The input text to process." + }, + "countryHint": { + "type": "string", + "description": "The country hint to help with language detection of the text." + } + }, + "required": [ + "id", + "text" + ] + }, + "LengthMetadata": { + "type": "object", + "description": "Represents the Length entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/LengthUnit", + "description": "Unit of measure for length." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "LengthMetadata" + }, + "LengthUnit": { + "type": "string", + "description": "The length unit of measurement.", + "enum": [ + "Unspecified", + "Kilometer", + "Hectometer", + "Decameter", + "Meter", + "Decimeter", + "Centimeter", + "Millimeter", + "Micrometer", + "Nanometer", + "Picometer", + "Mile", + "Yard", + "Inch", + "Foot", + "LightYear", + "Point" + ], + "x-ms-enum": { + "name": "LengthUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified length unit." + }, + { + "name": "Kilometer", + "value": "Kilometer", + "description": "Length unit in kilometers." + }, + { + "name": "Hectometer", + "value": "Hectometer", + "description": "Length unit in hectometers." + }, + { + "name": "Decameter", + "value": "Decameter", + "description": "Length unit in decameters." + }, + { + "name": "Meter", + "value": "Meter", + "description": "Length unit in meters." + }, + { + "name": "Decimeter", + "value": "Decimeter", + "description": "Length unit in decimeters." + }, + { + "name": "Centimeter", + "value": "Centimeter", + "description": "Length unit in centimeters." + }, + { + "name": "Millimeter", + "value": "Millimeter", + "description": "Length unit in millimeters." + }, + { + "name": "Micrometer", + "value": "Micrometer", + "description": "Length unit in micrometers." + }, + { + "name": "Nanometer", + "value": "Nanometer", + "description": "Length unit in nanometers." + }, + { + "name": "Picometer", + "value": "Picometer", + "description": "Length unit in picometers." + }, + { + "name": "Mile", + "value": "Mile", + "description": "Length unit in miles." + }, + { + "name": "Yard", + "value": "Yard", + "description": "Length unit in yards." + }, + { + "name": "Inch", + "value": "Inch", + "description": "Length unit in inches." + }, + { + "name": "Foot", + "value": "Foot", + "description": "Length unit in feet." + }, + { + "name": "LightYear", + "value": "LightYear", + "description": "Length unit in light years." + }, + { + "name": "Point", + "value": "Point", + "description": "Length unit in points." + } + ] + } + }, + "LinkedEntitiesDocumentResult": { + "type": "object", + "description": "Entity linking document result.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "entities": { + "type": "array", + "description": "Recognized well known entities in the document.", + "items": { + "$ref": "#/definitions/LinkedEntity" + } + } + }, + "required": [ + "id", + "warnings", + "entities" + ] + }, + "LinkedEntity": { + "type": "object", + "description": "The LinkedEntity object containing the detected entity with the associated sources/links.", + "properties": { + "name": { + "type": "string", + "description": "Entity Linking formal name." + }, + "matches": { + "type": "array", + "description": "List of instances this entity appears in the text.", + "items": { + "$ref": "#/definitions/Match" + }, + "x-ms-identifiers": [] + }, + "language": { + "type": "string", + "description": "Language used in the data source." + }, + "id": { + "type": "string", + "description": "Unique identifier of the recognized entity from the data source." + }, + "url": { + "type": "string", + "description": "URL for the entity's page from the data source." + }, + "dataSource": { + "type": "string", + "description": "Data source used to extract entity linking, such as Wiki/Bing etc." + }, + "bingId": { + "type": "string", + "description": "Bing Entity Search API unique identifier of the recognized entity." + } + }, + "required": [ + "name", + "matches", + "language", + "url", + "dataSource" + ] + }, + "Match": { + "type": "object", + "description": "The Match object containing the detected entity text with the offset and the length.", + "properties": { + "confidenceScore": { + "type": "number", + "format": "double", + "description": "If a well known item is recognized, a decimal number denoting the confidence level between 0 and 1 will be returned." + }, + "text": { + "type": "string", + "description": "Entity text as appears in the request." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "Start position for the entity match text." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "Length for the entity match text." + } + }, + "required": [ + "confidenceScore", + "text", + "offset", + "length" + ] + }, + "MatchLongestEntityPolicyType": { + "type": "object", + "description": "Represents the Match longest overlap policy. No overlapping entities as far as it is possible. 1. If there are overlapping entities, the longest one will be returned. 2. If the set of characters predicted for 2 or more entities are exactly the same, select the entity that has the higher confidence score.3. If the entity scores are identical, return all entities that are still present after applying the previous rules. 3. If there is partial overlap (as in Hello Text Analytics) follow the above steps starting from 1.", + "allOf": [ + { + "$ref": "#/definitions/BaseEntityOverlapPolicy" + } + ], + "x-ms-discriminator-value": "matchLongest" + }, + "MetadataKind": { + "type": "string", + "description": "The entity Metadata object kind.", + "enum": [ + "DateMetadata", + "DateTimeMetadata", + "TimeMetadata", + "TemporalSetMetadata", + "NumberMetadata", + "OrdinalMetadata", + "SpeedMetadata", + "WeightMetadata", + "LengthMetadata", + "VolumeMetadata", + "AreaMetadata", + "AgeMetadata", + "InformationMetadata", + "TemperatureMetadata", + "CurrencyMetadata", + "NumericRangeMetadata", + "TemporalSpanMetadata" + ], + "x-ms-enum": { + "name": "MetadataKind", + "modelAsString": true, + "values": [ + { + "name": "DateMetadata", + "value": "DateMetadata", + "description": "Metadata for date-related values." + }, + { + "name": "DateTimeMetadata", + "value": "DateTimeMetadata", + "description": "Metadata for date and time-related values." + }, + { + "name": "TimeMetadata", + "value": "TimeMetadata", + "description": "Metadata for time-related values." + }, + { + "name": "TemporalSetMetadata", + "value": "TemporalSetMetadata", + "description": "Metadata for set of time-related values." + }, + { + "name": "NumberMetadata", + "value": "NumberMetadata", + "description": "Metadata for numeric values." + }, + { + "name": "OrdinalMetadata", + "value": "OrdinalMetadata", + "description": "Metadata for ordinal numbers." + }, + { + "name": "SpeedMetadata", + "value": "SpeedMetadata", + "description": "Metadata for speed-related values." + }, + { + "name": "WeightMetadata", + "value": "WeightMetadata", + "description": "Metadata for weight-related values." + }, + { + "name": "LengthMetadata", + "value": "LengthMetadata", + "description": "Metadata for length-related values." + }, + { + "name": "VolumeMetadata", + "value": "VolumeMetadata", + "description": "Metadata for volume-related values." + }, + { + "name": "AreaMetadata", + "value": "AreaMetadata", + "description": "Metadata for area-related values." + }, + { + "name": "AgeMetadata", + "value": "AgeMetadata", + "description": "Metadata for age-related values." + }, + { + "name": "InformationMetadata", + "value": "InformationMetadata", + "description": "Metadata for information-related values." + }, + { + "name": "TemperatureMetadata", + "value": "TemperatureMetadata", + "description": "Metadata for temperature-related values." + }, + { + "name": "CurrencyMetadata", + "value": "CurrencyMetadata", + "description": "Metadata for currency-related values." + }, + { + "name": "NumericRangeMetadata", + "value": "NumericRangeMetadata", + "description": "Metadata for numeric range values." + }, + { + "name": "TemporalSpanMetadata", + "value": "TemporalSpanMetadata", + "description": "Metadata for temporal span values." + } + ] + } + }, + "MultiLanguageAnalysisInput": { + "type": "object", + "description": "Collection of input documents to be analyzed by the service.", + "properties": { + "documents": { + "type": "array", + "description": "The input documents to be analyzed.", + "items": { + "$ref": "#/definitions/MultiLanguageInput" + } + } + } + }, + "MultiLanguageInput": { + "type": "object", + "description": "Contains an input document to be analyzed by the service.", + "properties": { + "id": { + "type": "string", + "description": "A unique, non-empty document identifier." + }, + "text": { + "type": "string", + "description": "The input text to process." + }, + "language": { + "type": "string", + "description": "(Optional) This is the 2 letter ISO 639-1 representation of a language. For example, use \\\"en\\\" for English; \\\"es\\\" for Spanish etc. If not set, use \\\"en\\\" for English as default. (Following only applies to 2023-04-15-preview and above) For Auto Language Detection, use \\\"auto\\\". If not set, use \\\"en\\\" for English as default." + } + }, + "required": [ + "id", + "text" + ] + }, + "NumberKind": { + "type": "string", + "description": "The type of the extracted number entity.", + "enum": [ + "Integer", + "Decimal", + "Power", + "Fraction", + "Percent", + "Unspecified" + ], + "x-ms-enum": { + "name": "NumberKind", + "modelAsString": true, + "values": [ + { + "name": "Integer", + "value": "Integer", + "description": "Integer number" + }, + { + "name": "Decimal", + "value": "Decimal", + "description": "Decimal number" + }, + { + "name": "Power", + "value": "Power", + "description": "Power number" + }, + { + "name": "Fraction", + "value": "Fraction", + "description": "Fraction number" + }, + { + "name": "Percent", + "value": "Percent", + "description": "Percent number" + }, + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified number kind" + } + ] + } + }, + "NumberMetadata": { + "type": "object", + "description": "A metadata for numeric entity instances.", + "properties": { + "numberKind": { + "$ref": "#/definitions/NumberKind", + "description": "Kind of the number type." + }, + "value": { + "type": "number", + "format": "double", + "description": "A numeric representation of what the extracted text denotes." + } + }, + "required": [ + "numberKind", + "value" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "NumberMetadata" + }, + "NumericRangeMetadata": { + "type": "object", + "description": "represents the Metadata of numeric intervals.", + "properties": { + "rangeKind": { + "$ref": "#/definitions/RangeKind", + "description": "Kind of numeric ranges supported - like Number, Speed, etc." + }, + "minimum": { + "type": "number", + "format": "double", + "description": "The beginning value of the interval." + }, + "maximum": { + "type": "number", + "format": "double", + "description": "The ending value of the interval." + }, + "rangeInclusivity": { + "$ref": "#/definitions/RangeInclusivity", + "description": "The inclusiveness of this range." + } + }, + "required": [ + "rangeKind", + "minimum", + "maximum" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "NumericRangeMetadata" + }, + "OrdinalMetadata": { + "type": "object", + "description": "A metadata for numeric entity instances.", + "properties": { + "offset": { + "type": "string", + "description": "The offset with respect to the reference (e.g., offset = -1 indicates the second to last)" + }, + "relativeTo": { + "$ref": "#/definitions/RelativeTo", + "description": "The reference point that the ordinal number denotes." + }, + "value": { + "type": "string", + "description": "A simple arithmetic expression that the ordinal denotes." + } + }, + "required": [ + "offset", + "relativeTo", + "value" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "OrdinalMetadata" + }, + "PiiCategory": { + "type": "string", + "description": "(Optional) describes the PII categories to return", + "enum": [ + "ABARoutingNumber", + "ARNationalIdentityNumber", + "AUBankAccountNumber", + "AUDriversLicenseNumber", + "AUMedicalAccountNumber", + "AUPassportNumber", + "AUTaxFileNumber", + "AUBusinessNumber", + "AUCompanyNumber", + "ATIdentityCard", + "ATTaxIdentificationNumber", + "ATValueAddedTaxNumber", + "AzureDocumentDBAuthKey", + "AzureIAASDatabaseConnectionAndSQLString", + "AzureIoTConnectionString", + "AzurePublishSettingPassword", + "AzureRedisCacheString", + "AzureSAS", + "AzureServiceBusString", + "AzureStorageAccountKey", + "AzureStorageAccountGeneric", + "BENationalNumber", + "BENationalNumberV2", + "BEValueAddedTaxNumber", + "BRCPFNumber", + "BRLegalEntityNumber", + "BRNationalIDRG", + "BGUniformCivilNumber", + "CABankAccountNumber", + "CADriversLicenseNumber", + "CAHealthServiceNumber", + "CAPassportNumber", + "CAPersonalHealthIdentification", + "CASocialInsuranceNumber", + "CLIdentityCardNumber", + "CNResidentIdentityCardNumber", + "CreditCardNumber", + "HRIdentityCardNumber", + "HRNationalIDNumber", + "HRPersonalIdentificationNumber", + "HRPersonalIdentificationOIBNumberV2", + "CYIdentityCard", + "CYTaxIdentificationNumber", + "CZPersonalIdentityNumber", + "CZPersonalIdentityV2", + "DKPersonalIdentificationNumber", + "DKPersonalIdentificationV2", + "DrugEnforcementAgencyNumber", + "EEPersonalIdentificationCode", + "EUDebitCardNumber", + "EUDriversLicenseNumber", + "EUGPSCoordinates", + "EUNationalIdentificationNumber", + "EUPassportNumber", + "EUSocialSecurityNumber", + "EUTaxIdentificationNumber", + "FIEuropeanHealthNumber", + "FINationalID", + "FINationalIDV2", + "FIPassportNumber", + "FRDriversLicenseNumber", + "FRHealthInsuranceNumber", + "FRNationalID", + "FRPassportNumber", + "FRSocialSecurityNumber", + "FRTaxIdentificationNumber", + "FRValueAddedTaxNumber", + "DEDriversLicenseNumber", + "DEPassportNumber", + "DEIdentityCardNumber", + "DETaxIdentificationNumber", + "DEValueAddedNumber", + "GRNationalIDCard", + "GRNationalIDV2", + "GRTaxIdentificationNumber", + "HKIdentityCardNumber", + "HUValueAddedNumber", + "HUPersonalIdentificationNumber", + "HUTaxIdentificationNumber", + "INPermanentAccount", + "INUniqueIdentificationNumber", + "IDIdentityCardNumber", + "InternationalBankingAccountNumber", + "IEPersonalPublicServiceNumber", + "IEPersonalPublicServiceNumberV2", + "ILBankAccountNumber", + "ILNationalID", + "ITDriversLicenseNumber", + "ITFiscalCode", + "ITValueAddedTaxNumber", + "JPBankAccountNumber", + "JPDriversLicenseNumber", + "JPPassportNumber", + "JPResidentRegistrationNumber", + "JPSocialInsuranceNumber", + "JPMyNumberCorporate", + "JPMyNumberPersonal", + "JPResidenceCardNumber", + "LVPersonalCode", + "LTPersonalCode", + "LUNationalIdentificationNumberNatural", + "LUNationalIdentificationNumberNonNatural", + "MYIdentityCardNumber", + "MTIdentityCardNumber", + "MTTaxIDNumber", + "NLCitizensServiceNumber", + "NLCitizensServiceNumberV2", + "NLTaxIdentificationNumber", + "NLValueAddedTaxNumber", + "NZBankAccountNumber", + "NZDriversLicenseNumber", + "NZInlandRevenueNumber", + "NZMinistryOfHealthNumber", + "NZSocialWelfareNumber", + "NOIdentityNumber", + "PHUnifiedMultiPurposeIDNumber", + "PLIdentityCard", + "PLNationalID", + "PLNationalIDV2", + "PLPassportNumber", + "PLTaxIdentificationNumber", + "PLREGONNumber", + "PTCitizenCardNumber", + "PTCitizenCardNumberV2", + "PTTaxIdentificationNumber", + "ROPersonalNumericalCode", + "RUPassportNumberDomestic", + "RUPassportNumberInternational", + "SANationalID", + "SGNationalRegistrationIdentityCardNumber", + "SKPersonalNumber", + "SITaxIdentificationNumber", + "SIUniqueMasterCitizenNumber", + "ZAIdentificationNumber", + "KRResidentRegistrationNumber", + "ESDNI", + "ESSocialSecurityNumber", + "ESTaxIdentificationNumber", + "SQLServerConnectionString", + "SENationalID", + "SENationalIDV2", + "SEPassportNumber", + "SETaxIdentificationNumber", + "SWIFTCode", + "CHSocialSecurityNumber", + "TWNationalID", + "TWPassportNumber", + "TWResidentCertificate", + "THPopulationIdentificationCode", + "TRNationalIdentificationNumber", + "UKDriversLicenseNumber", + "UKElectoralRollNumber", + "UKNationalHealthNumber", + "UKNationalInsuranceNumber", + "UKUniqueTaxpayerNumber", + "USUKPassportNumber", + "USBankAccountNumber", + "USDriversLicenseNumber", + "USIndividualTaxpayerIdentification", + "USSocialSecurityNumber", + "UAPassportNumberDomestic", + "UAPassportNumberInternational", + "Organization", + "Email", + "URL", + "Age", + "PhoneNumber", + "IPAddress", + "Date", + "Person", + "Address", + "All", + "Default" + ], + "x-ms-enum": { + "name": "PiiCategory", + "modelAsString": true, + "values": [ + { + "name": "ABARoutingNumber", + "value": "ABARoutingNumber", + "description": "ABA Routing number" + }, + { + "name": "ARNationalIdentityNumber", + "value": "ARNationalIdentityNumber", + "description": "AR National Identity Number" + }, + { + "name": "AUBankAccountNumber", + "value": "AUBankAccountNumber", + "description": "AT Bank Account Number" + }, + { + "name": "AUDriversLicenseNumber", + "value": "AUDriversLicenseNumber", + "description": "AU Driver's License Number" + }, + { + "name": "AUMedicalAccountNumber", + "value": "AUMedicalAccountNumber", + "description": "AU Medical Account Number" + }, + { + "name": "AUPassportNumber", + "value": "AUPassportNumber", + "description": "AU Passport Number" + }, + { + "name": "AUTaxFileNumber", + "value": "AUTaxFileNumber", + "description": "AU Tax File Number" + }, + { + "name": "AUBusinessNumber", + "value": "AUBusinessNumber", + "description": "AU Business Number" + }, + { + "name": "AUCompanyNumber", + "value": "AUCompanyNumber", + "description": "AU Company Number" + }, + { + "name": "ATIdentityCard", + "value": "ATIdentityCard", + "description": "AT Identity Card" + }, + { + "name": "ATTaxIdentificationNumber", + "value": "ATTaxIdentificationNumber", + "description": "AT Tax Identification Number" + }, + { + "name": "ATValueAddedTaxNumber", + "value": "ATValueAddedTaxNumber", + "description": "AT Value Added Tax Number" + }, + { + "name": "AzureDocumentDBAuthKey", + "value": "AzureDocumentDBAuthKey", + "description": "Azure Document DB Auth Key" + }, + { + "name": "AzureIAASDatabaseConnectionAndSQLString", + "value": "AzureIAASDatabaseConnectionAndSQLString", + "description": "Azure IAAS Database Connection And SQL String" + }, + { + "name": "AzureIoTConnectionString", + "value": "AzureIoTConnectionString", + "description": "Azure IoT Connection String" + }, + { + "name": "AzurePublishSettingPassword", + "value": "AzurePublishSettingPassword", + "description": "Azure Publish Setting Password" + }, + { + "name": "AzureRedisCacheString", + "value": "AzureRedisCacheString", + "description": "Azure Redis Cache String" + }, + { + "name": "AzureSAS", + "value": "AzureSAS", + "description": "Azure SAS" + }, + { + "name": "AzureServiceBusString", + "value": "AzureServiceBusString", + "description": "Azure Service Bus String" + }, + { + "name": "AzureStorageAccountKey", + "value": "AzureStorageAccountKey", + "description": "Azure Storage Account Key" + }, + { + "name": "AzureStorageAccountGeneric", + "value": "AzureStorageAccountGeneric", + "description": "Azure Storage Account Generic" + }, + { + "name": "BENationalNumber", + "value": "BENationalNumber", + "description": "BE National Number" + }, + { + "name": "BENationalNumberV2", + "value": "BENationalNumberV2", + "description": "BE National Number V2" + }, + { + "name": "BEValueAddedTaxNumber", + "value": "BEValueAddedTaxNumber", + "description": "BE Value Added Tax Number" + }, + { + "name": "BRCPFNumber", + "value": "BRCPFNumber", + "description": "BR CPF Number" + }, + { + "name": "BRLegalEntityNumber", + "value": "BRLegalEntityNumber", + "description": "BR Legal Entity Number" + }, + { + "name": "BRNationalIDRG", + "value": "BRNationalIDRG", + "description": "BR National ID RG" + }, + { + "name": "BGUniformCivilNumber", + "value": "BGUniformCivilNumber", + "description": "BG Uniform Civil Number" + }, + { + "name": "CABankAccountNumber", + "value": "CABankAccountNumber", + "description": "CA Bank Account Number" + }, + { + "name": "CADriversLicenseNumber", + "value": "CADriversLicenseNumber", + "description": "CA Driver's License Number" + }, + { + "name": "CAHealthServiceNumber", + "value": "CAHealthServiceNumber", + "description": "CA Health Service Number" + }, + { + "name": "CAPassportNumber", + "value": "CAPassportNumber", + "description": "CA Passport Number" + }, + { + "name": "CAPersonalHealthIdentification", + "value": "CAPersonalHealthIdentification", + "description": "CA Personal Health Identification" + }, + { + "name": "CASocialInsuranceNumber", + "value": "CASocialInsuranceNumber", + "description": "CA Social Insurance Number" + }, + { + "name": "CLIdentityCardNumber", + "value": "CLIdentityCardNumber", + "description": "CL Identity Card Number" + }, + { + "name": "CNResidentIdentityCardNumber", + "value": "CNResidentIdentityCardNumber", + "description": "CN Resident Identity Card Number" + }, + { + "name": "CreditCardNumber", + "value": "CreditCardNumber", + "description": "Credit Card Number" + }, + { + "name": "HRIdentityCardNumber", + "value": "HRIdentityCardNumber", + "description": "HR Identity Card Number" + }, + { + "name": "HRNationalIDNumber", + "value": "HRNationalIDNumber", + "description": "HR National ID Number" + }, + { + "name": "HRPersonalIdentificationNumber", + "value": "HRPersonalIdentificationNumber", + "description": "HR Personal Identification Number" + }, + { + "name": "HRPersonalIdentificationOIBNumberV2", + "value": "HRPersonalIdentificationOIBNumberV2", + "description": "HR Personal Identification OIB Number V2" + }, + { + "name": "CYIdentityCard", + "value": "CYIdentityCard", + "description": "CY Identity Card" + }, + { + "name": "CYTaxIdentificationNumber", + "value": "CYTaxIdentificationNumber", + "description": "CY Tax Identification Number" + }, + { + "name": "CZPersonalIdentityNumber", + "value": "CZPersonalIdentityNumber", + "description": "CZ Personal Identity Number" + }, + { + "name": "CZPersonalIdentityV2", + "value": "CZPersonalIdentityV2", + "description": "CZ Personal Identity V2" + }, + { + "name": "DKPersonalIdentificationNumber", + "value": "DKPersonalIdentificationNumber", + "description": "DK Personal Identification Number" + }, + { + "name": "DKPersonalIdentificationV2", + "value": "DKPersonalIdentificationV2", + "description": "DK Personal Identification V2" + }, + { + "name": "DrugEnforcementAgencyNumber", + "value": "DrugEnforcementAgencyNumber", + "description": "Drug Enforcement Agency Number" + }, + { + "name": "EEPersonalIdentificationCode", + "value": "EEPersonalIdentificationCode", + "description": "EE Personal Identification Code" + }, + { + "name": "EUDebitCardNumber", + "value": "EUDebitCardNumber", + "description": "EU Debit Card Number" + }, + { + "name": "EUDriversLicenseNumber", + "value": "EUDriversLicenseNumber", + "description": "EU Driver's License Number" + }, + { + "name": "EUGPSCoordinates", + "value": "EUGPSCoordinates", + "description": "EU GPS Coordinates" + }, + { + "name": "EUNationalIdentificationNumber", + "value": "EUNationalIdentificationNumber", + "description": "EU National Identification Number" + }, + { + "name": "EUPassportNumber", + "value": "EUPassportNumber", + "description": "EU Passport Number" + }, + { + "name": "EUSocialSecurityNumber", + "value": "EUSocialSecurityNumber", + "description": "EU Social Security Number" + }, + { + "name": "EUTaxIdentificationNumber", + "value": "EUTaxIdentificationNumber", + "description": "EU Tax Identification Number" + }, + { + "name": "FIEuropeanHealthNumber", + "value": "FIEuropeanHealthNumber", + "description": "FI European Health Number" + }, + { + "name": "FINationalID", + "value": "FINationalID", + "description": "FI National ID" + }, + { + "name": "FINationalIDV2", + "value": "FINationalIDV2", + "description": "FI National ID V2" + }, + { + "name": "FIPassportNumber", + "value": "FIPassportNumber", + "description": "FI Passport Number" + }, + { + "name": "FRDriversLicenseNumber", + "value": "FRDriversLicenseNumber", + "description": "FR Driver's License Number" + }, + { + "name": "FRHealthInsuranceNumber", + "value": "FRHealthInsuranceNumber", + "description": "FR Health Insurance Number" + }, + { + "name": "FRNationalID", + "value": "FRNationalID", + "description": "FR National ID" + }, + { + "name": "FRPassportNumber", + "value": "FRPassportNumber", + "description": "FR Passport Number" + }, + { + "name": "FRSocialSecurityNumber", + "value": "FRSocialSecurityNumber", + "description": "FR Social Security Number" + }, + { + "name": "FRTaxIdentificationNumber", + "value": "FRTaxIdentificationNumber", + "description": "FR Tax Identification Number" + }, + { + "name": "FRValueAddedTaxNumber", + "value": "FRValueAddedTaxNumber", + "description": "FR Value Added Tax Number" + }, + { + "name": "DEDriversLicenseNumber", + "value": "DEDriversLicenseNumber", + "description": "DE Driver's License Number" + }, + { + "name": "DEPassportNumber", + "value": "DEPassportNumber", + "description": "DE Passport Number" + }, + { + "name": "DEIdentityCardNumber", + "value": "DEIdentityCardNumber", + "description": "DE Identity Card Number" + }, + { + "name": "DETaxIdentificationNumber", + "value": "DETaxIdentificationNumber", + "description": "DE Tax Identification Number" + }, + { + "name": "DEValueAddedNumber", + "value": "DEValueAddedNumber", + "description": "DE Value Added Number" + }, + { + "name": "GRNationalIDCard", + "value": "GRNationalIDCard", + "description": "GR National ID Card" + }, + { + "name": "GRNationalIDV2", + "value": "GRNationalIDV2", + "description": "GR National ID V2" + }, + { + "name": "GRTaxIdentificationNumber", + "value": "GRTaxIdentificationNumber", + "description": "GR Tax Identification Number" + }, + { + "name": "HKIdentityCardNumber", + "value": "HKIdentityCardNumber", + "description": "HK Identity Card Number" + }, + { + "name": "HUValueAddedNumber", + "value": "HUValueAddedNumber", + "description": "HU Value Added Number" + }, + { + "name": "HUPersonalIdentificationNumber", + "value": "HUPersonalIdentificationNumber", + "description": "HU Personal Identification Number" + }, + { + "name": "HUTaxIdentificationNumber", + "value": "HUTaxIdentificationNumber", + "description": "HU Tax Identification Number" + }, + { + "name": "INPermanentAccount", + "value": "INPermanentAccount", + "description": "IN Permanent Account" + }, + { + "name": "INUniqueIdentificationNumber", + "value": "INUniqueIdentificationNumber", + "description": "IN Unique Identification Number" + }, + { + "name": "IDIdentityCardNumber", + "value": "IDIdentityCardNumber", + "description": "ID Identity Card Number" + }, + { + "name": "InternationalBankingAccountNumber", + "value": "InternationalBankingAccountNumber", + "description": "International Banking Account Number" + }, + { + "name": "IEPersonalPublicServiceNumber", + "value": "IEPersonalPublicServiceNumber", + "description": "IE Personal Public Service Number" + }, + { + "name": "IEPersonalPublicServiceNumberV2", + "value": "IEPersonalPublicServiceNumberV2", + "description": "IE Personal Public Service Number V2" + }, + { + "name": "ILBankAccountNumber", + "value": "ILBankAccountNumber", + "description": "IL Bank Account Number" + }, + { + "name": "ILNationalID", + "value": "ILNationalID", + "description": "IL National ID" + }, + { + "name": "ITDriversLicenseNumber", + "value": "ITDriversLicenseNumber", + "description": "IT Driver's License Number" + }, + { + "name": "ITFiscalCode", + "value": "ITFiscalCode", + "description": "IT Fiscal Code" + }, + { + "name": "ITValueAddedTaxNumber", + "value": "ITValueAddedTaxNumber", + "description": "IT Value Added Tax Number" + }, + { + "name": "JPBankAccountNumber", + "value": "JPBankAccountNumber", + "description": "JP Bank Account Number" + }, + { + "name": "JPDriversLicenseNumber", + "value": "JPDriversLicenseNumber", + "description": "JP Driver's License Number" + }, + { + "name": "JPPassportNumber", + "value": "JPPassportNumber", + "description": "JP Passport Number" + }, + { + "name": "JPResidentRegistrationNumber", + "value": "JPResidentRegistrationNumber", + "description": "JP Resident Registration Number" + }, + { + "name": "JPSocialInsuranceNumber", + "value": "JPSocialInsuranceNumber", + "description": "JP Social Insurance Number" + }, + { + "name": "JPMyNumberCorporate", + "value": "JPMyNumberCorporate", + "description": "JP My Number Corporate" + }, + { + "name": "JPMyNumberPersonal", + "value": "JPMyNumberPersonal", + "description": "JP My Number Personal" + }, + { + "name": "JPResidenceCardNumber", + "value": "JPResidenceCardNumber", + "description": "JP Residence Card Number" + }, + { + "name": "LVPersonalCode", + "value": "LVPersonalCode", + "description": "LV Personal Code" + }, + { + "name": "LTPersonalCode", + "value": "LTPersonalCode", + "description": "LT Personal Code" + }, + { + "name": "LUNationalIdentificationNumberNatural", + "value": "LUNationalIdentificationNumberNatural", + "description": "LU National Identification Number Natural" + }, + { + "name": "LUNationalIdentificationNumberNonNatural", + "value": "LUNationalIdentificationNumberNonNatural", + "description": "LU National Identification Number Non Natural" + }, + { + "name": "MYIdentityCardNumber", + "value": "MYIdentityCardNumber", + "description": "MY Identity Card Number" + }, + { + "name": "MTIdentityCardNumber", + "value": "MTIdentityCardNumber", + "description": "MT Identity Card Number" + }, + { + "name": "MTTaxIDNumber", + "value": "MTTaxIDNumber", + "description": "MT Tax ID Number" + }, + { + "name": "NLCitizensServiceNumber", + "value": "NLCitizensServiceNumber", + "description": "NL Citizens Service Number" + }, + { + "name": "NLCitizensServiceNumberV2", + "value": "NLCitizensServiceNumberV2", + "description": "NL Citizens Service Number V2" + }, + { + "name": "NLTaxIdentificationNumber", + "value": "NLTaxIdentificationNumber", + "description": "NL Tax Identification Number" + }, + { + "name": "NLValueAddedTaxNumber", + "value": "NLValueAddedTaxNumber", + "description": "NL Value Added Tax Number" + }, + { + "name": "NZBankAccountNumber", + "value": "NZBankAccountNumber", + "description": "NZ Bank Account Number" + }, + { + "name": "NZDriversLicenseNumber", + "value": "NZDriversLicenseNumber", + "description": "NZ Driver's License Number" + }, + { + "name": "NZInlandRevenueNumber", + "value": "NZInlandRevenueNumber", + "description": "NZ Inland Revenue Number" + }, + { + "name": "NZMinistryOfHealthNumber", + "value": "NZMinistryOfHealthNumber", + "description": "NZ Ministry Of Health Number" + }, + { + "name": "NZSocialWelfareNumber", + "value": "NZSocialWelfareNumber", + "description": "NZ Social Welfare Number" + }, + { + "name": "NOIdentityNumber", + "value": "NOIdentityNumber", + "description": "NO Identity Number" + }, + { + "name": "PHUnifiedMultiPurposeIDNumber", + "value": "PHUnifiedMultiPurposeIDNumber", + "description": "PH Unified Multi Purpose ID Number" + }, + { + "name": "PLIdentityCard", + "value": "PLIdentityCard", + "description": "PL Identity Card" + }, + { + "name": "PLNationalID", + "value": "PLNationalID", + "description": "PL National ID" + }, + { + "name": "PLNationalIDV2", + "value": "PLNationalIDV2", + "description": "PL National ID V2" + }, + { + "name": "PLPassportNumber", + "value": "PLPassportNumber", + "description": "PL Passport Number" + }, + { + "name": "PLTaxIdentificationNumber", + "value": "PLTaxIdentificationNumber", + "description": "PL Tax Identification Number" + }, + { + "name": "PLREGONNumber", + "value": "PLREGONNumber", + "description": "PL REGON Number" + }, + { + "name": "PTCitizenCardNumber", + "value": "PTCitizenCardNumber", + "description": "PT Citizen Card Number" + }, + { + "name": "PTCitizenCardNumberV2", + "value": "PTCitizenCardNumberV2", + "description": "PT Citizen Card Number V2" + }, + { + "name": "PTTaxIdentificationNumber", + "value": "PTTaxIdentificationNumber", + "description": "PT Tax Identification Number" + }, + { + "name": "ROPersonalNumericalCode", + "value": "ROPersonalNumericalCode", + "description": "RO Personal Numerical Code" + }, + { + "name": "RUPassportNumberDomestic", + "value": "RUPassportNumberDomestic", + "description": "RU Passport Number Domestic" + }, + { + "name": "RUPassportNumberInternational", + "value": "RUPassportNumberInternational", + "description": "RU Passport Number International" + }, + { + "name": "SANationalID", + "value": "SANationalID", + "description": "SA National ID" + }, + { + "name": "SGNationalRegistrationIdentityCardNumber", + "value": "SGNationalRegistrationIdentityCardNumber", + "description": "SG National Registration Identity Card Number" + }, + { + "name": "SKPersonalNumber", + "value": "SKPersonalNumber", + "description": "SK Personal Number" + }, + { + "name": "SITaxIdentificationNumber", + "value": "SITaxIdentificationNumber", + "description": "SI Tax Identification Number" + }, + { + "name": "SIUniqueMasterCitizenNumber", + "value": "SIUniqueMasterCitizenNumber", + "description": "SI Unique Master Citizen Number" + }, + { + "name": "ZAIdentificationNumber", + "value": "ZAIdentificationNumber", + "description": "ZA Identification Number" + }, + { + "name": "KRResidentRegistrationNumber", + "value": "KRResidentRegistrationNumber", + "description": "KR Resident Registration Number" + }, + { + "name": "ESDNI", + "value": "ESDNI", + "description": "ES DNI" + }, + { + "name": "ESSocialSecurityNumber", + "value": "ESSocialSecurityNumber", + "description": "ES Social Security Number" + }, + { + "name": "ESTaxIdentificationNumber", + "value": "ESTaxIdentificationNumber", + "description": "ES Tax Identification Number" + }, + { + "name": "SQLServerConnectionString", + "value": "SQLServerConnectionString", + "description": "SQL Server Connection String" + }, + { + "name": "SENationalID", + "value": "SENationalID", + "description": "SE National ID" + }, + { + "name": "SENationalIDV2", + "value": "SENationalIDV2", + "description": "SE National ID V2" + }, + { + "name": "SEPassportNumber", + "value": "SEPassportNumber", + "description": "SE Passport Number" + }, + { + "name": "SETaxIdentificationNumber", + "value": "SETaxIdentificationNumber", + "description": "SE Tax Identification Number" + }, + { + "name": "SWIFTCode", + "value": "SWIFTCode", + "description": "SWIFT Code" + }, + { + "name": "CHSocialSecurityNumber", + "value": "CHSocialSecurityNumber", + "description": "CH Social Security Number" + }, + { + "name": "TWNationalID", + "value": "TWNationalID", + "description": "TW National ID" + }, + { + "name": "TWPassportNumber", + "value": "TWPassportNumber", + "description": "TW Passport Number" + }, + { + "name": "TWResidentCertificate", + "value": "TWResidentCertificate", + "description": "TW Resident Certificate" + }, + { + "name": "THPopulationIdentificationCode", + "value": "THPopulationIdentificationCode", + "description": "TH Population Identification Code" + }, + { + "name": "TRNationalIdentificationNumber", + "value": "TRNationalIdentificationNumber", + "description": "TR National Identification Number" + }, + { + "name": "UKDriversLicenseNumber", + "value": "UKDriversLicenseNumber", + "description": "UK Driver's License Number" + }, + { + "name": "UKElectoralRollNumber", + "value": "UKElectoralRollNumber", + "description": "UK Electoral Roll Number" + }, + { + "name": "UKNationalHealthNumber", + "value": "UKNationalHealthNumber", + "description": "UK National Health Number" + }, + { + "name": "UKNationalInsuranceNumber", + "value": "UKNationalInsuranceNumber", + "description": "UK National Insurance Number" + }, + { + "name": "UKUniqueTaxpayerNumber", + "value": "UKUniqueTaxpayerNumber", + "description": "UK Unique Taxpayer Number" + }, + { + "name": "USUKPassportNumber", + "value": "USUKPassportNumber", + "description": "US UK Passport Number" + }, + { + "name": "USBankAccountNumber", + "value": "USBankAccountNumber", + "description": "US Bank Account Number" + }, + { + "name": "USDriversLicenseNumber", + "value": "USDriversLicenseNumber", + "description": "US Driver's License Number" + }, + { + "name": "USIndividualTaxpayerIdentification", + "value": "USIndividualTaxpayerIdentification", + "description": "US Individual Taxpayer Identification" + }, + { + "name": "USSocialSecurityNumber", + "value": "USSocialSecurityNumber", + "description": "US Social Security Number" + }, + { + "name": "UAPassportNumberDomestic", + "value": "UAPassportNumberDomestic", + "description": "UA Passport Number Domestic" + }, + { + "name": "UAPassportNumberInternational", + "value": "UAPassportNumberInternational", + "description": "UA Passport Number International" + }, + { + "name": "Organization", + "value": "Organization", + "description": "Organization" + }, + { + "name": "Email", + "value": "Email", + "description": "Email" + }, + { + "name": "URL", + "value": "URL", + "description": "URL" + }, + { + "name": "Age", + "value": "Age", + "description": "Age" + }, + { + "name": "PhoneNumber", + "value": "PhoneNumber", + "description": "Phone Number" + }, + { + "name": "IPAddress", + "value": "IPAddress", + "description": "IP Address" + }, + { + "name": "Date", + "value": "Date", + "description": "Date" + }, + { + "name": "Person", + "value": "Person", + "description": "Person" + }, + { + "name": "Address", + "value": "Address", + "description": "Address" + }, + { + "name": "All", + "value": "All", + "description": "All PII categories." + }, + { + "name": "Default", + "value": "Default", + "description": "Default PII categories for the language." + } + ] + } + }, + "PiiEntitiesDocumentResult": { + "type": "object", + "description": "Contains the PII results.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "redactedText": { + "type": "string", + "description": "Returns redacted text." + }, + "entities": { + "type": "array", + "description": "Recognized entities in the document.", + "items": { + "$ref": "#/definitions/Entity" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "redactedText", + "entities" + ] + }, + "PiiEntityRecognitionLROResult": { + "type": "object", + "description": "Contains the PII LRO results.", + "properties": { + "results": { + "$ref": "#/definitions/PiiResult", + "description": "The list of pii results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "PiiEntityRecognitionLROResults" + }, + "PiiLROTask": { + "type": "object", + "description": "Contains the analyze text PIIEntityRecognition LRO task.", + "properties": { + "parameters": { + "$ref": "#/definitions/PiiTaskParameters", + "description": "Pii task parameters." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "PiiEntityRecognition" + }, + "PiiResult": { + "type": "object", + "description": "Contains the PiiResult.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "Response by document", + "items": { + "$ref": "#/definitions/PiiEntitiesDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "PiiTaskParameters": { + "type": "object", + "description": "Supported parameters for a PII Entities Recognition task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": true + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "domain": { + "type": "string", + "description": "Domain for PII task", + "default": "none", + "enum": [ + "phi", + "none" + ], + "x-ms-enum": { + "name": "PiiDomain", + "modelAsString": true, + "values": [ + { + "name": "phi", + "value": "phi", + "description": "Indicates that entities in the Personal Health Information domain should be redacted." + }, + { + "name": "none", + "value": "none", + "description": "Indicates that no domain is specified." + } + ] + } + }, + "piiCategories": { + "type": "array", + "description": "Enumeration of PII categories to be returned in the response.", + "items": { + "$ref": "#/definitions/PiiCategory" + } + }, + "stringIndexType": { + "type": "string", + "description": "StringIndexType to be used for analysis.", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + } + } + }, + "PiiTaskResult": { + "type": "object", + "description": "Contains the analyze text PIIEntityRecognition LRO task.", + "properties": { + "results": { + "$ref": "#/definitions/PiiResult", + "description": "The list of pii results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "PiiEntityRecognitionResults" + }, + "RangeInclusivity": { + "type": "string", + "description": "The range inclusiveness of this property property.", + "enum": [ + "NoneInclusive", + "LeftInclusive", + "RightInclusive", + "LeftRightInclusive" + ], + "x-ms-enum": { + "name": "RangeInclusivity", + "modelAsString": true, + "values": [ + { + "name": "NoneInclusive", + "value": "NoneInclusive", + "description": "No inclusivity" + }, + { + "name": "LeftInclusive", + "value": "LeftInclusive", + "description": "Left side inclusive" + }, + { + "name": "RightInclusive", + "value": "RightInclusive", + "description": "Right side inclusive" + }, + { + "name": "LeftRightInclusive", + "value": "LeftRightInclusive", + "description": "Both sides inclusive" + } + ] + } + }, + "RangeKind": { + "type": "string", + "description": "The kind of the number range entity.", + "enum": [ + "Number", + "Speed", + "Weight", + "Length", + "Volume", + "Area", + "Age", + "Information", + "Temperature", + "Currency" + ], + "x-ms-enum": { + "name": "RangeKind", + "modelAsString": true, + "values": [ + { + "name": "Number", + "value": "Number", + "description": "Number range" + }, + { + "name": "Speed", + "value": "Speed", + "description": "Speed range" + }, + { + "name": "Weight", + "value": "Weight", + "description": "Weight range" + }, + { + "name": "Length", + "value": "Length", + "description": "Length range" + }, + { + "name": "Volume", + "value": "Volume", + "description": "Volume range" + }, + { + "name": "Area", + "value": "Area", + "description": "Area range" + }, + { + "name": "Age", + "value": "Age", + "description": "Age range" + }, + { + "name": "Information", + "value": "Information", + "description": "Information range" + }, + { + "name": "Temperature", + "value": "Temperature", + "description": "Temperature range" + }, + { + "name": "Currency", + "value": "Currency", + "description": "Currency range" + } + ] + } + }, + "RelativeTo": { + "type": "string", + "description": "The reference point that the ordinal number denotes.", + "enum": [ + "Current", + "End", + "Start" + ], + "x-ms-enum": { + "name": "RelativeTo", + "modelAsString": true, + "values": [ + { + "name": "Current", + "value": "Current", + "description": "Current state or position" + }, + { + "name": "End", + "value": "End", + "description": "End state or position" + }, + { + "name": "Start", + "value": "Start", + "description": "Start state or position" + } + ] + } + }, + "RequestStatistics": { + "type": "object", + "description": "if showStats=true was specified in the request this field will contain information about the request payload.", + "properties": { + "documentsCount": { + "type": "integer", + "format": "int32", + "description": "Number of documents submitted in the request." + }, + "validDocumentsCount": { + "type": "integer", + "format": "int32", + "description": "Number of valid documents. This excludes empty, over-size limit or non-supported languages documents." + }, + "erroneousDocumentsCount": { + "type": "integer", + "format": "int32", + "description": "Number of invalid documents. This includes empty, over-size limit or non-supported languages documents." + }, + "transactionsCount": { + "type": "integer", + "format": "int64", + "description": "Number of transactions for the request." + } + }, + "required": [ + "documentsCount", + "validDocumentsCount", + "erroneousDocumentsCount", + "transactionsCount" + ] + }, + "ScriptCode": { + "type": "string", + "description": "Identifies the script of the input document. Maps to the ISO 15924 standard script code.", + "enum": [ + "Arab", + "Armn", + "Beng", + "Cans", + "Cyrl", + "Deva", + "Ethi", + "Geor", + "Grek", + "Gujr", + "Guru", + "Hang", + "Hani", + "Hans", + "Hant", + "Hebr", + "Jpan", + "Khmr", + "Knda", + "Laoo", + "Latn", + "Mlym", + "Mong", + "Mtei", + "Mymr", + "Olck", + "Orya", + "Sinh", + "Shrd", + "Taml", + "Telu", + "Thaa", + "Thai", + "Tibt" + ], + "x-ms-enum": { + "name": "ScriptCode", + "modelAsString": true, + "values": [ + { + "name": "Arab", + "value": "Arab", + "description": "Script code for the Arabic script." + }, + { + "name": "Armn", + "value": "Armn", + "description": "Script code for the Armenian script." + }, + { + "name": "Beng", + "value": "Beng", + "description": "Script code for the Bangla script." + }, + { + "name": "Cans", + "value": "Cans", + "description": "Script code for the UnifiedCanadianAboriginalSyllabics script." + }, + { + "name": "Cyrl", + "value": "Cyrl", + "description": "Script code for the Cyrillic script." + }, + { + "name": "Deva", + "value": "Deva", + "description": "Script code for the Devanagari script." + }, + { + "name": "Ethi", + "value": "Ethi", + "description": "Script code for the Ethiopic script." + }, + { + "name": "Geor", + "value": "Geor", + "description": "Script code for the Georgian script." + }, + { + "name": "Grek", + "value": "Grek", + "description": "Script code for the Greek script." + }, + { + "name": "Gujr", + "value": "Gujr", + "description": "Script code for the Gujarati script." + }, + { + "name": "Guru", + "value": "Guru", + "description": "Script code for the Gurmukhi script." + }, + { + "name": "Hang", + "value": "Hang", + "description": "Script code for the Hangul script." + }, + { + "name": "Hani", + "value": "Hani", + "description": "Script code for the HanLiteral script." + }, + { + "name": "Hans", + "value": "Hans", + "description": "Script code for the HanSimplified script." + }, + { + "name": "Hant", + "value": "Hant", + "description": "Script code for the HanTraditional script." + }, + { + "name": "Hebr", + "value": "Hebr", + "description": "Script code for the Hebrew script." + }, + { + "name": "Jpan", + "value": "Jpan", + "description": "Script code for the Japanese script." + }, + { + "name": "Khmr", + "value": "Khmr", + "description": "Script code for the Khmer script." + }, + { + "name": "Knda", + "value": "Knda", + "description": "Script code for the Kannada script." + }, + { + "name": "Laoo", + "value": "Laoo", + "description": "Script code for the Lao script." + }, + { + "name": "Latn", + "value": "Latn", + "description": "Script code for the Latin script." + }, + { + "name": "Mlym", + "value": "Mlym", + "description": "Script code for the Malayalam script." + }, + { + "name": "Mong", + "value": "Mong", + "description": "Script code for the Mongolian script." + }, + { + "name": "Mtei", + "value": "Mtei", + "description": "Script code for the Meitei script." + }, + { + "name": "Mymr", + "value": "Mymr", + "description": "Script code for the Myanmar script." + }, + { + "name": "Olck", + "value": "Olck", + "description": "Script code for the Santali script." + }, + { + "name": "Orya", + "value": "Orya", + "description": "Script code for the Odia script." + }, + { + "name": "Sinh", + "value": "Sinh", + "description": "Script code for the Sinhala script." + }, + { + "name": "Shrd", + "value": "Shrd", + "description": "Script code for the Sharada script." + }, + { + "name": "Taml", + "value": "Taml", + "description": "Script code for the Tamil script." + }, + { + "name": "Telu", + "value": "Telu", + "description": "Script code for the Telugu script." + }, + { + "name": "Thaa", + "value": "Thaa", + "description": "Script code for the Thaana script." + }, + { + "name": "Thai", + "value": "Thai", + "description": "Script code for the Thai script." + }, + { + "name": "Tibt", + "value": "Tibt", + "description": "Script code for the Tibetan script." + } + ] + } + }, + "ScriptKind": { + "type": "string", + "description": "Identifies the script of the input document. Maps to the ISO 15924 standard formal name.", + "enum": [ + "Arabic", + "Armenian", + "Bangla", + "UnifiedCanadianAboriginalSyllabics", + "Cyrillic", + "Devanagari", + "Ethiopic", + "Georgian", + "Greek", + "Gujarati", + "Gurmukhi", + "Hangul", + "HanLiteral", + "HanSimplified", + "HanTraditional", + "Hebrew", + "Japanese", + "Khmer", + "Kannada", + "Lao", + "Latin", + "Malayalam", + "Meitei", + "Mongolian", + "Myanmar", + "Odia", + "Santali", + "Sharada", + "Sinhala", + "Tamil", + "Telugu", + "Thaana", + "Thai", + "Tibetan" + ], + "x-ms-enum": { + "name": "ScriptKind", + "modelAsString": true, + "values": [ + { + "name": "Arabic", + "value": "Arabic", + "description": "Script name for the Arabic script." + }, + { + "name": "Armenian", + "value": "Armenian", + "description": "Script name for the Armenian script." + }, + { + "name": "Bangla", + "value": "Bangla", + "description": "Script name for the Bangla script." + }, + { + "name": "UnifiedCanadianAboriginalSyllabics", + "value": "UnifiedCanadianAboriginalSyllabics", + "description": "Script name for the UnifiedCanadianAboriginalSyllabics script." + }, + { + "name": "Cyrillic", + "value": "Cyrillic", + "description": "Script name for the Cyrillic script." + }, + { + "name": "Devanagari", + "value": "Devanagari", + "description": "Script name for the Devanagari script." + }, + { + "name": "Ethiopic", + "value": "Ethiopic", + "description": "Script name for the Ethiopic script." + }, + { + "name": "Georgian", + "value": "Georgian", + "description": "Script name for the Georgian script." + }, + { + "name": "Greek", + "value": "Greek", + "description": "Script name for the Greek script." + }, + { + "name": "Gujarati", + "value": "Gujarati", + "description": "Script name for the Gujarati script." + }, + { + "name": "Gurmukhi", + "value": "Gurmukhi", + "description": "Script name for the Gurmukhi script." + }, + { + "name": "Hangul", + "value": "Hangul", + "description": "Script name for the Hangul script." + }, + { + "name": "HanLiteral", + "value": "HanLiteral", + "description": "Script name for the HanLiteral script." + }, + { + "name": "HanSimplified", + "value": "HanSimplified", + "description": "Script name for the HanSimplified script." + }, + { + "name": "HanTraditional", + "value": "HanTraditional", + "description": "Script name for the HanTraditional script." + }, + { + "name": "Hebrew", + "value": "Hebrew", + "description": "Script name for the Hebrew script." + }, + { + "name": "Japanese", + "value": "Japanese", + "description": "Script name for the Japanese script." + }, + { + "name": "Khmer", + "value": "Khmer", + "description": "Script name for the Khmer script." + }, + { + "name": "Kannada", + "value": "Kannada", + "description": "Script name for the Kannada script." + }, + { + "name": "Lao", + "value": "Lao", + "description": "Script name for the Lao script." + }, + { + "name": "Latin", + "value": "Latin", + "description": "Script name for the Latin script." + }, + { + "name": "Malayalam", + "value": "Malayalam", + "description": "Script name for the Malayalam script." + }, + { + "name": "Meitei", + "value": "Meitei", + "description": "Script name for the Meitei script." + }, + { + "name": "Mongolian", + "value": "Mongolian", + "description": "Script name for the Mongolian script." + }, + { + "name": "Myanmar", + "value": "Myanmar", + "description": "Script name for the Myanmar script." + }, + { + "name": "Odia", + "value": "Odia", + "description": "Script name for the Odia script." + }, + { + "name": "Santali", + "value": "Santali", + "description": "Script name for the Santali script." + }, + { + "name": "Sharada", + "value": "Sharada", + "description": "Script name for the Sharada script." + }, + { + "name": "Sinhala", + "value": "Sinhala", + "description": "Script name for the Sinhala script." + }, + { + "name": "Tamil", + "value": "Tamil", + "description": "Script name for the Tamil script." + }, + { + "name": "Telugu", + "value": "Telugu", + "description": "Script name for the Telugu script." + }, + { + "name": "Thaana", + "value": "Thaana", + "description": "Script name for the Thaana script." + }, + { + "name": "Thai", + "value": "Thai", + "description": "Script name for the Thai script." + }, + { + "name": "Tibetan", + "value": "Tibetan", + "description": "Script name for the Tibetan script." + } + ] + } + }, + "SentenceAssessment": { + "type": "object", + "description": "Represents a sentence assessment and the assessments or target objects related to it.", + "properties": { + "sentiment": { + "$ref": "#/definitions/TokenSentimentValue", + "description": "The sentiment of the sentence." + }, + "confidenceScores": { + "$ref": "#/definitions/TargetConfidenceScoreLabel", + "description": "Represents the confidence scores across all sentiment classes: positive and negative." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "The target offset from the start of the sentence." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "The length of the target." + }, + "text": { + "type": "string", + "description": "The target text detected." + }, + "isNegated": { + "type": "boolean", + "description": "The indicator representing if the assessment is negated." + } + }, + "required": [ + "sentiment", + "confidenceScores", + "offset", + "length", + "text", + "isNegated" + ] + }, + "SentenceSentiment": { + "type": "object", + "description": "A document's sentence sentiment.", + "properties": { + "text": { + "type": "string", + "description": "The sentence text." + }, + "sentiment": { + "$ref": "#/definitions/SentenceSentimentValue", + "description": "The predicted Sentiment for the sentence." + }, + "confidenceScores": { + "$ref": "#/definitions/SentimentConfidenceScores", + "description": "The sentiment confidence score between 0 and 1 for the sentence for all classes." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "The target offset from the start of the sentence." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "The length of the target." + }, + "targets": { + "type": "array", + "description": "The array of sentence targets for the sentence.", + "items": { + "$ref": "#/definitions/SentenceTarget" + }, + "x-ms-identifiers": [] + }, + "assessments": { + "type": "array", + "description": "The array of assessments for the sentence.", + "items": { + "$ref": "#/definitions/SentenceAssessment" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "text", + "sentiment", + "confidenceScores", + "offset", + "length" + ] + }, + "SentenceSentimentValue": { + "type": "string", + "description": "The predicted Sentiment for the sentence.", + "enum": [ + "positive", + "neutral", + "negative" + ], + "x-ms-enum": { + "name": "SentenceSentimentValue", + "modelAsString": false, + "values": [ + { + "name": "positive", + "value": "positive", + "description": "Positive sentiment" + }, + { + "name": "neutral", + "value": "neutral", + "description": "Neutral sentiment" + }, + { + "name": "negative", + "value": "negative", + "description": "Negative sentiment" + } + ] + } + }, + "SentenceTarget": { + "type": "object", + "description": "Represents a sentence target and the assessments or target objects related to it.", + "properties": { + "sentiment": { + "$ref": "#/definitions/TokenSentimentValue", + "description": "The sentiment of the sentence." + }, + "confidenceScores": { + "$ref": "#/definitions/TargetConfidenceScoreLabel", + "description": "Represents the confidence scores across all sentiment classes: positive and negative." + }, + "offset": { + "type": "integer", + "format": "int32", + "description": "The target offset from the start of the sentence." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "The length of the target." + }, + "text": { + "type": "string", + "description": "The target text detected." + }, + "relations": { + "type": "array", + "description": "The array of either assessment or target objects which is related to the target.", + "items": { + "$ref": "#/definitions/TargetRelation" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "sentiment", + "confidenceScores", + "offset", + "length", + "text", + "relations" + ] + }, + "SentimentAnalysisLROTask": { + "type": "object", + "description": "An object representing the task definition for a Sentiment Analysis task.", + "properties": { + "parameters": { + "$ref": "#/definitions/SentimentAnalysisTaskParameters", + "description": "Parameters for the Sentiment Analysis task." + } + }, + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROTask" + } + ], + "x-ms-discriminator-value": "SentimentAnalysis" + }, + "SentimentAnalysisTaskParameters": { + "type": "object", + "description": "Supported parameters for a Sentiment Analysis task.", + "properties": { + "loggingOptOut": { + "type": "boolean", + "description": "logging opt out", + "default": false + }, + "modelVersion": { + "type": "string", + "description": "model version", + "default": "latest" + }, + "opinionMining": { + "type": "boolean", + "description": "Whether to use opinion mining in the request or not.", + "default": false + }, + "stringIndexType": { + "type": "string", + "description": "Specifies the method used to interpret string offsets.", + "default": "TextElements_v8", + "enum": [ + "TextElements_v8", + "UnicodeCodePoint", + "Utf16CodeUnit" + ], + "x-ms-enum": { + "name": "StringIndexType", + "modelAsString": true, + "values": [ + { + "name": "TextElements_v8", + "value": "TextElements_v8", + "description": "Returned offset and length values will correspond to TextElements (Graphemes and Grapheme clusters) confirming to the Unicode 8.0.0 standard. Use this option if your application is written in .Net Framework or .Net Core and you will be using StringInfo." + }, + { + "name": "UnicodeCodePoint", + "value": "UnicodeCodePoint", + "description": "Returned offset and length values will correspond to Unicode code points. Use this option if your application is written in a language that support Unicode, for example Python." + }, + { + "name": "Utf16CodeUnit", + "value": "Utf16CodeUnit", + "description": "Returned offset and length values will correspond to UTF-16 code units. Use this option if your application is written in a language that support Unicode, for example Java, JavaScript." + } + ] + } + } + } + }, + "SentimentConfidenceScores": { + "type": "object", + "description": "Represents the confidence scores between 0 and 1 across all sentiment classes: positive, neutral, negative.", + "properties": { + "positive": { + "type": "number", + "format": "double", + "description": "Confidence score for positive sentiment" + }, + "neutral": { + "type": "number", + "format": "double", + "description": "Confidence score for neutral sentiment" + }, + "negative": { + "type": "number", + "format": "double", + "description": "Confidence score for negative sentiment" + } + }, + "required": [ + "positive", + "neutral", + "negative" + ] + }, + "SentimentDocumentResult": { + "type": "object", + "description": "An object representing the pre-built Sentiment Analysis results of each document.", + "properties": { + "id": { + "type": "string", + "description": "Unique, non-empty document identifier." + }, + "warnings": { + "type": "array", + "description": "Warnings encountered while processing document.", + "items": { + "$ref": "#/definitions/DocumentWarning" + }, + "x-ms-identifiers": [] + }, + "statistics": { + "$ref": "#/definitions/DocumentStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the document payload." + }, + "sentiment": { + "$ref": "#/definitions/DocumentSentimentValue", + "description": "Predicted sentiment for document (Negative, Neutral, Positive, or Mixed)." + }, + "confidenceScores": { + "$ref": "#/definitions/SentimentConfidenceScores", + "description": "The sentiment confidence score between 0 and 1 for the sentence for all classes." + }, + "sentences": { + "type": "array", + "description": "The document's sentences sentiment.", + "items": { + "$ref": "#/definitions/SentenceSentiment" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "id", + "warnings", + "sentiment", + "confidenceScores", + "sentences" + ] + }, + "SentimentLROResult": { + "type": "object", + "description": "Contains the Sentiment Analysis LRO results.", + "properties": { + "results": { + "$ref": "#/definitions/SentimentResponse", + "description": "The sentiment analysis results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextLROResult" + } + ], + "x-ms-discriminator-value": "SentimentAnalysisLROResults" + }, + "SentimentResponse": { + "type": "object", + "description": "Sentiment analysis results for the input documents.", + "properties": { + "errors": { + "type": "array", + "description": "Errors by document id.", + "items": { + "$ref": "#/definitions/DocumentError" + } + }, + "statistics": { + "$ref": "#/definitions/RequestStatistics", + "description": "if showStats=true was specified in the request this field will contain information about the request payload." + }, + "modelVersion": { + "type": "string", + "description": "This field indicates which model is used for scoring." + }, + "documents": { + "type": "array", + "description": "The sentiment analysis results for each document in the input.", + "items": { + "$ref": "#/definitions/SentimentDocumentResult" + } + } + }, + "required": [ + "errors", + "modelVersion", + "documents" + ] + }, + "SentimentTaskResult": { + "type": "object", + "description": "Contains the analyze text SentimentAnalysis LRO task result.", + "properties": { + "results": { + "$ref": "#/definitions/SentimentResponse", + "description": "The sentiment analysis results" + } + }, + "required": [ + "results" + ], + "allOf": [ + { + "$ref": "#/definitions/AnalyzeTextTaskResult" + } + ], + "x-ms-discriminator-value": "SentimentAnalysisResults" + }, + "SpeedMetadata": { + "type": "object", + "description": "Represents the Speed entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/SpeedUnit", + "description": "Unit of measure for speed." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "SpeedMetadata" + }, + "SpeedUnit": { + "type": "string", + "description": "The speed Unit of measurement", + "enum": [ + "Unspecified", + "MetersPerSecond", + "KilometersPerHour", + "KilometersPerMinute", + "KilometersPerSecond", + "MilesPerHour", + "Knots", + "FeetPerSecond", + "FeetPerMinute", + "YardsPerMinute", + "YardsPerSecond", + "MetersPerMillisecond", + "CentimetersPerMillisecond", + "KilometersPerMillisecond" + ], + "x-ms-enum": { + "name": "SpeedUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified speed unit." + }, + { + "name": "MetersPerSecond", + "value": "MetersPerSecond", + "description": "Speed unit in meters per second." + }, + { + "name": "KilometersPerHour", + "value": "KilometersPerHour", + "description": "Speed unit in kilometers per hour." + }, + { + "name": "KilometersPerMinute", + "value": "KilometersPerMinute", + "description": "Speed unit in kilometers per minute." + }, + { + "name": "KilometersPerSecond", + "value": "KilometersPerSecond", + "description": "Speed unit in kilometers per second." + }, + { + "name": "MilesPerHour", + "value": "MilesPerHour", + "description": "Speed unit in miles per hour." + }, + { + "name": "Knots", + "value": "Knots", + "description": "Speed unit in knots." + }, + { + "name": "FeetPerSecond", + "value": "FeetPerSecond", + "description": "Speed unit in feet per second." + }, + { + "name": "FeetPerMinute", + "value": "FeetPerMinute", + "description": "Speed unit in feet per minute." + }, + { + "name": "YardsPerMinute", + "value": "YardsPerMinute", + "description": "Speed unit in yards per minute." + }, + { + "name": "YardsPerSecond", + "value": "YardsPerSecond", + "description": "Speed unit in yards per second." + }, + { + "name": "MetersPerMillisecond", + "value": "MetersPerMillisecond", + "description": "Speed unit in meters per millisecond." + }, + { + "name": "CentimetersPerMillisecond", + "value": "CentimetersPerMillisecond", + "description": "Speed unit in centimeters per millisecond." + }, + { + "name": "KilometersPerMillisecond", + "value": "KilometersPerMillisecond", + "description": "Speed unit in Kilometers per millisecond." + } + ] + } + }, + "State": { + "type": "string", + "description": "The status of the task at the mentioned last update time.", + "enum": [ + "notStarted", + "running", + "succeeded", + "partiallyCompleted", + "failed", + "cancelled", + "cancelling" + ], + "x-ms-enum": { + "name": "State", + "modelAsString": true, + "values": [ + { + "name": "notStarted", + "value": "notStarted", + "description": "Not started status" + }, + { + "name": "running", + "value": "running", + "description": "Running status" + }, + { + "name": "succeeded", + "value": "succeeded", + "description": "Succeeded status" + }, + { + "name": "partiallyCompleted", + "value": "partiallyCompleted", + "description": "Partially completed status" + }, + { + "name": "failed", + "value": "failed", + "description": "Failed status" + }, + { + "name": "cancelled", + "value": "cancelled", + "description": "Cancelled status" + }, + { + "name": "cancelling", + "value": "cancelling", + "description": "Cancelling status" + } + ] + } + }, + "SummaryContext": { + "type": "object", + "description": "The context of the summary.", + "properties": { + "offset": { + "type": "integer", + "format": "int32", + "description": "Start position for the context. Use of different 'stringIndexType' values can affect the offset returned." + }, + "length": { + "type": "integer", + "format": "int32", + "description": "The length of the context. Use of different 'stringIndexType' values can affect the length returned." + } + }, + "required": [ + "offset", + "length" + ] + }, + "SummaryLengthBucket": { + "type": "string", + "description": "Enum that defines the length of the output summaries.", + "enum": [ + "short", + "medium", + "long" + ], + "x-ms-enum": { + "name": "SummaryLengthBucket", + "modelAsString": true, + "values": [ + { + "name": "short", + "value": "short", + "description": "Instructs model to generate shorter length summaries." + }, + { + "name": "medium", + "value": "medium", + "description": "Instructs model to generate medium length summaries." + }, + { + "name": "long", + "value": "long", + "description": "Instructs model to generate longer length summaries." + } + ] + } + }, + "TargetConfidenceScoreLabel": { + "type": "object", + "description": "Represents the confidence scores across all sentiment classes: positive and negative.", + "properties": { + "positive": { + "type": "number", + "format": "double", + "description": "Confidence score for positive sentiment" + }, + "negative": { + "type": "number", + "format": "double", + "description": "Confidence score for negative sentiment" + } + }, + "required": [ + "positive", + "negative" + ] + }, + "TargetRelation": { + "type": "object", + "description": "Represents the relation between assessments and/or targets.", + "properties": { + "ref": { + "type": "string", + "description": "The JSON pointer indicating the linked object." + }, + "relationType": { + "$ref": "#/definitions/TargetRelationType", + "description": "The type related to the target." + } + }, + "required": [ + "ref", + "relationType" + ] + }, + "TargetRelationType": { + "type": "string", + "description": "The type related to the target.", + "enum": [ + "assessment", + "target" + ], + "x-ms-enum": { + "name": "TargetRelationType", + "modelAsString": false, + "values": [ + { + "name": "assessment", + "value": "assessment", + "description": "Assessment relation." + }, + { + "name": "target", + "value": "target", + "description": "Target relation." + } + ] + } + }, + "Tasks": { + "type": "object", + "description": "Container for the tasks status for the LRO job.", + "properties": { + "completed": { + "type": "integer", + "format": "int32", + "description": "Count of completed tasks." + }, + "failed": { + "type": "integer", + "format": "int32", + "description": "Count of failed tasks." + }, + "inProgress": { + "type": "integer", + "format": "int32", + "description": "Count of inprogress tasks." + }, + "total": { + "type": "integer", + "format": "int32", + "description": "Count of total tasks." + }, + "items": { + "type": "array", + "description": "Enumerable of Analyze text job results.", + "items": { + "$ref": "#/definitions/AnalyzeTextLROResult" + }, + "x-ms-identifiers": [] + } + }, + "required": [ + "completed", + "failed", + "inProgress", + "total" + ] + }, + "TemperatureMetadata": { + "type": "object", + "description": "Represents the Information entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/TemperatureUnit", + "description": "Unit of measure for temperature." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "TemperatureMetadata" + }, + "TemperatureUnit": { + "type": "string", + "description": "The temperature Unit of measurement.", + "enum": [ + "Unspecified", + "Fahrenheit", + "Kelvin", + "Rankine", + "Celsius" + ], + "x-ms-enum": { + "name": "TemperatureUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified temperature unit" + }, + { + "name": "Fahrenheit", + "value": "Fahrenheit", + "description": "Temperature unit in Fahrenheit" + }, + { + "name": "Kelvin", + "value": "Kelvin", + "description": "Temperature unit in Kelvin" + }, + { + "name": "Rankine", + "value": "Rankine", + "description": "Temperature unit in Rankine" + }, + { + "name": "Celsius", + "value": "Celsius", + "description": "Temperature unit in Celsius" + } + ] + } + }, + "TemporalModifier": { + "type": "string", + "description": "An optional modifier of a date/time instance.", + "enum": [ + "AfterApprox", + "Before", + "BeforeStart", + "Approx", + "ReferenceUndefined", + "SinceEnd", + "AfterMid", + "Start", + "After", + "BeforeEnd", + "Until", + "End", + "Less", + "Since", + "AfterStart", + "BeforeApprox", + "Mid", + "More" + ], + "x-ms-enum": { + "name": "TemporalModifier", + "modelAsString": true, + "values": [ + { + "name": "AfterApprox", + "value": "AfterApprox", + "description": "After an approximate time" + }, + { + "name": "Before", + "value": "Before", + "description": "Before a specific time" + }, + { + "name": "BeforeStart", + "value": "BeforeStart", + "description": "Before the start of a time period" + }, + { + "name": "Approx", + "value": "Approx", + "description": "Approximately at a specific time" + }, + { + "name": "ReferenceUndefined", + "value": "ReferenceUndefined", + "description": "Reference to an undefined time" + }, + { + "name": "SinceEnd", + "value": "SinceEnd", + "description": "Since the end of a time period" + }, + { + "name": "AfterMid", + "value": "AfterMid", + "description": "After the middle of a time period" + }, + { + "name": "Start", + "value": "Start", + "description": "At the start of a time period" + }, + { + "name": "After", + "value": "After", + "description": "After a specific time" + }, + { + "name": "BeforeEnd", + "value": "BeforeEnd", + "description": "Before the end of a time period" + }, + { + "name": "Until", + "value": "Until", + "description": "Until a specific time" + }, + { + "name": "End", + "value": "End", + "description": "At the end of a time period" + }, + { + "name": "Less", + "value": "Less", + "description": "Less than a specific time" + }, + { + "name": "Since", + "value": "Since", + "description": "Since a specific time" + }, + { + "name": "AfterStart", + "value": "AfterStart", + "description": "After the start of a time period" + }, + { + "name": "BeforeApprox", + "value": "BeforeApprox", + "description": "Before an approximate time" + }, + { + "name": "Mid", + "value": "Mid", + "description": "In the middle of a time period" + }, + { + "name": "More", + "value": "More", + "description": "More than a specific time" + } + ] + } + }, + "TemporalSetMetadata": { + "type": "object", + "description": "A Metadata for temporal set entity instances.", + "properties": { + "dateValues": { + "type": "array", + "description": "List of date values.", + "items": { + "$ref": "#/definitions/DateValue" + }, + "x-ms-identifiers": [] + } + }, + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "TemporalSetMetadata" + }, + "TemporalSpanMetadata": { + "type": "object", + "description": "represents the Metadata of a date and/or time span.", + "properties": { + "spanValues": { + "type": "array", + "description": "List of temporal spans detected.", + "items": { + "$ref": "#/definitions/TemporalSpanValues" + }, + "x-ms-identifiers": [] + } + }, + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "TemporalSpanMetadata" + }, + "TemporalSpanValues": { + "type": "object", + "description": "Temporal span object.", + "properties": { + "begin": { + "type": "string", + "description": "Start value for the span." + }, + "end": { + "type": "string", + "description": "End value for the span." + }, + "duration": { + "type": "string", + "description": "An optional duration value formatted based on the ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601#Durations)" + }, + "modifier": { + "$ref": "#/definitions/TemporalModifier", + "description": "Modifier for datetime to indicate point of reference like before, after etc." + }, + "timex": { + "type": "string", + "description": "An optional triplet containing the beginning, the end, and the duration all stated as ISO 8601 formatted strings." + } + } + }, + "Temporality": { + "type": "string", + "description": "Describes temporal information regarding the entity.", + "enum": [ + "current", + "past", + "future" + ], + "x-ms-enum": { + "name": "Temporality", + "modelAsString": false, + "values": [ + { + "name": "current", + "value": "current", + "description": "Current temporality" + }, + { + "name": "past", + "value": "past", + "description": "Past temporality" + }, + { + "name": "future", + "value": "future", + "description": "Future temporality" + } + ] + } + }, + "TimeMetadata": { + "type": "object", + "description": "A Metadata for time entity instances.", + "properties": { + "dateValues": { + "type": "array", + "description": "List of date values.", + "items": { + "$ref": "#/definitions/DateValue" + }, + "x-ms-identifiers": [] + } + }, + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "TimeMetadata" + }, + "TokenSentimentValue": { + "type": "string", + "description": "The predicted Sentiment for the sentence.", + "enum": [ + "positive", + "mixed", + "negative" + ], + "x-ms-enum": { + "name": "TokenSentimentValue", + "modelAsString": false, + "values": [ + { + "name": "positive", + "value": "positive", + "description": "Positive sentiment" + }, + { + "name": "mixed", + "value": "mixed", + "description": "Mixed sentiment" + }, + { + "name": "negative", + "value": "negative", + "description": "Negative sentiment" + } + ] + } + }, + "VolumeMetadata": { + "type": "object", + "description": "Represents the Volume entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/VolumeUnit", + "description": "Unit of measure for volume." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "VolumeMetadata" + }, + "VolumeUnit": { + "type": "string", + "description": "The Volume Unit of measurement", + "enum": [ + "Unspecified", + "CubicMeter", + "CubicCentimeter", + "CubicMillimeter", + "Hectoliter", + "Decaliter", + "Liter", + "Centiliter", + "Milliliter", + "CubicYard", + "CubicInch", + "CubicFoot", + "CubicMile", + "FluidOunce", + "Teaspoon", + "Tablespoon", + "Pint", + "Quart", + "Cup", + "Gill", + "Pinch", + "FluidDram", + "Barrel", + "Minim", + "Cord", + "Peck", + "Bushel", + "Hogshead" + ], + "x-ms-enum": { + "name": "VolumeUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified volume unit." + }, + { + "name": "CubicMeter", + "value": "CubicMeter", + "description": "Volume unit in cubic meters." + }, + { + "name": "CubicCentimeter", + "value": "CubicCentimeter", + "description": "Volume unit in cubic centimeters." + }, + { + "name": "CubicMillimeter", + "value": "CubicMillimeter", + "description": "Volume unit in cubic millimeters." + }, + { + "name": "Hectoliter", + "value": "Hectoliter", + "description": "Volume unit in hectoliters." + }, + { + "name": "Decaliter", + "value": "Decaliter", + "description": "Volume unit in decaliters." + }, + { + "name": "Liter", + "value": "Liter", + "description": "Volume unit in liters." + }, + { + "name": "Centiliter", + "value": "Centiliter", + "description": "Volume unit in centiliters." + }, + { + "name": "Milliliter", + "value": "Milliliter", + "description": "Volume unit in milliliters." + }, + { + "name": "CubicYard", + "value": "CubicYard", + "description": "Volume unit in cubic yards." + }, + { + "name": "CubicInch", + "value": "CubicInch", + "description": "Volume unit in cubic inches." + }, + { + "name": "CubicFoot", + "value": "CubicFoot", + "description": "Volume unit in cubic feet." + }, + { + "name": "CubicMile", + "value": "CubicMile", + "description": "Volume unit in cubic miles." + }, + { + "name": "FluidOunce", + "value": "FluidOunce", + "description": "Volume unit in fluid ounces." + }, + { + "name": "Teaspoon", + "value": "Teaspoon", + "description": "Volume unit in teaspoons." + }, + { + "name": "Tablespoon", + "value": "Tablespoon", + "description": "Volume unit in tablespoons." + }, + { + "name": "Pint", + "value": "Pint", + "description": "Volume unit in pints." + }, + { + "name": "Quart", + "value": "Quart", + "description": "Volume unit in quarts." + }, + { + "name": "Cup", + "value": "Cup", + "description": "Volume unit in cups." + }, + { + "name": "Gill", + "value": "Gill", + "description": "Volume unit in gills." + }, + { + "name": "Pinch", + "value": "Pinch", + "description": "Volume unit in pinches." + }, + { + "name": "FluidDram", + "value": "FluidDram", + "description": "Volume unit in fluid drams." + }, + { + "name": "Barrel", + "value": "Barrel", + "description": "Volume unit in barrels." + }, + { + "name": "Minim", + "value": "Minim", + "description": "Volume unit in minims." + }, + { + "name": "Cord", + "value": "Cord", + "description": "Volume unit in cords." + }, + { + "name": "Peck", + "value": "Peck", + "description": "Volume unit in pecks." + }, + { + "name": "Bushel", + "value": "Bushel", + "description": "Volume unit in bushels." + }, + { + "name": "Hogshead", + "value": "Hogshead", + "description": "Volume unit in hogsheads." + } + ] + } + }, + "WarningCodeValue": { + "type": "string", + "description": "Defines the list of the warning codes.", + "enum": [ + "LongWordsInDocument", + "DocumentTruncated" + ], + "x-ms-enum": { + "name": "WarningCodeValue", + "modelAsString": true, + "values": [ + { + "name": "LongWordsInDocument", + "value": "LongWordsInDocument", + "description": "Long words in document warning" + }, + { + "name": "DocumentTruncated", + "value": "DocumentTruncated", + "description": "Document truncated warning" + } + ] + } + }, + "WeightMetadata": { + "type": "object", + "description": "Represents the Weight ) entity Metadata model.", + "properties": { + "value": { + "type": "number", + "format": "double", + "description": "The numeric value that the extracted text denotes." + }, + "unit": { + "$ref": "#/definitions/WeightUnit", + "description": "Unit of measure for weight." + } + }, + "required": [ + "value", + "unit" + ], + "allOf": [ + { + "$ref": "#/definitions/BaseMetadata" + } + ], + "x-ms-discriminator-value": "WeightMetadata" + }, + "WeightUnit": { + "type": "string", + "description": "The weight Unit of measurement.", + "enum": [ + "Unspecified", + "Kilogram", + "Gram", + "Milligram", + "Gallon", + "MetricTon", + "Ton", + "Pound", + "Ounce", + "Grain", + "PennyWeight", + "LongTonBritish", + "ShortTonUS", + "ShortHundredWeightUS", + "Stone", + "Dram" + ], + "x-ms-enum": { + "name": "WeightUnit", + "modelAsString": true, + "values": [ + { + "name": "Unspecified", + "value": "Unspecified", + "description": "Unspecified weight unit" + }, + { + "name": "Kilogram", + "value": "Kilogram", + "description": "Weight unit in kilograms" + }, + { + "name": "Gram", + "value": "Gram", + "description": "Weight unit in grams" + }, + { + "name": "Milligram", + "value": "Milligram", + "description": "Weight unit in milligrams" + }, + { + "name": "Gallon", + "value": "Gallon", + "description": "Volume unit in gallons" + }, + { + "name": "MetricTon", + "value": "MetricTon", + "description": "Weight unit in metric tons" + }, + { + "name": "Ton", + "value": "Ton", + "description": "Weight unit in tons" + }, + { + "name": "Pound", + "value": "Pound", + "description": "Weight unit in pounds" + }, + { + "name": "Ounce", + "value": "Ounce", + "description": "Weight unit in ounces" + }, + { + "name": "Grain", + "value": "Grain", + "description": "Weight unit in grains" + }, + { + "name": "PennyWeight", + "value": "PennyWeight", + "description": "Weight unit in pennyweights" + }, + { + "name": "LongTonBritish", + "value": "LongTonBritish", + "description": "Weight unit in long tons (British)" + }, + { + "name": "ShortTonUS", + "value": "ShortTonUS", + "description": "Weight unit in short tons (US)" + }, + { + "name": "ShortHundredWeightUS", + "value": "ShortHundredWeightUS", + "description": "Weight unit in short hundredweights (US)" + }, + { + "name": "Stone", + "value": "Stone", + "description": "Weight unit in stones" + }, + { + "name": "Dram", + "value": "Dram", + "description": "Weight unit in drams" + } + ] + } + }, + "fhirVersion": { + "type": "string", + "description": "The FHIR Spec version.", + "enum": [ + "4.0.1" + ], + "x-ms-enum": { + "name": "fhirVersion", + "modelAsString": true, + "values": [ + { + "name": "4.0.1", + "value": "4.0.1", + "description": "Version 4.0.1" + } + ] + } + }, + "healthcareDocumentType": { + "type": "string", + "description": "Document type.", + "enum": [ + "None", + "ClinicalTrial", + "DischargeSummary", + "ProgressNote", + "HistoryAndPhysical", + "Consult", + "Imaging", + "Pathology", + "ProcedureNote" + ], + "x-ms-enum": { + "name": "healthcareDocumentType", + "modelAsString": true, + "values": [ + { + "name": "None", + "value": "None", + "description": "None document type" + }, + { + "name": "ClinicalTrial", + "value": "ClinicalTrial", + "description": "Clinical trial document type" + }, + { + "name": "DischargeSummary", + "value": "DischargeSummary", + "description": "Discharge summary document type" + }, + { + "name": "ProgressNote", + "value": "ProgressNote", + "description": "Progress note document type" + }, + { + "name": "HistoryAndPhysical", + "value": "HistoryAndPhysical", + "description": "History and physical document type" + }, + { + "name": "Consult", + "value": "Consult", + "description": "Consult document type" + }, + { + "name": "Imaging", + "value": "Imaging", + "description": "Imaging document type" + }, + { + "name": "Pathology", + "value": "Pathology", + "description": "Pathology document type" + }, + { + "name": "ProcedureNote", + "value": "ProcedureNote", + "description": "Procedure note document type" + } + ] + } + }, + "healthcareEntityCategory": { + "type": "string", + "description": "Healthcare Entity Category.", + "enum": [ + "BodyStructure", + "Age", + "Gender", + "ExaminationName", + "Date", + "Direction", + "Frequency", + "MeasurementValue", + "MeasurementUnit", + "RelationalOperator", + "Time", + "GeneOrProtein", + "Variant", + "AdministrativeEvent", + "CareEnvironment", + "HealthcareProfession", + "Diagnosis", + "SymptomOrSign", + "ConditionQualifier", + "MedicationClass", + "MedicationName", + "Dosage", + "MedicationForm", + "MedicationRoute", + "FamilyRelation", + "TreatmentName", + "Ethnicity", + "Course", + "Expression", + "MutationType", + "ConditionScale", + "Allergen", + "Employment", + "LivingStatus", + "SubstanceUse", + "SubstanceUseAmount" + ], + "x-ms-enum": { + "name": "healthcareEntityCategory", + "modelAsString": true, + "values": [ + { + "name": "BodyStructure", + "value": "BodyStructure", + "description": "Body structure" + }, + { + "name": "Age", + "value": "Age", + "description": "Age" + }, + { + "name": "Gender", + "value": "Gender", + "description": "Gender" + }, + { + "name": "ExaminationName", + "value": "ExaminationName", + "description": "Examination name" + }, + { + "name": "Date", + "value": "Date", + "description": "Date" + }, + { + "name": "Direction", + "value": "Direction", + "description": "Direction" + }, + { + "name": "Frequency", + "value": "Frequency", + "description": "Frequency" + }, + { + "name": "MeasurementValue", + "value": "MeasurementValue", + "description": "Measurement value" + }, + { + "name": "MeasurementUnit", + "value": "MeasurementUnit", + "description": "Measurement unit" + }, + { + "name": "RelationalOperator", + "value": "RelationalOperator", + "description": "Relational operator" + }, + { + "name": "Time", + "value": "Time", + "description": "Time" + }, + { + "name": "GeneOrProtein", + "value": "GeneOrProtein", + "description": "Gene or protein" + }, + { + "name": "Variant", + "value": "Variant", + "description": "Variant" + }, + { + "name": "AdministrativeEvent", + "value": "AdministrativeEvent", + "description": "Administrative event" + }, + { + "name": "CareEnvironment", + "value": "CareEnvironment", + "description": "Care environment" + }, + { + "name": "HealthcareProfession", + "value": "HealthcareProfession", + "description": "Healthcare profession" + }, + { + "name": "Diagnosis", + "value": "Diagnosis", + "description": "Diagnosis" + }, + { + "name": "SymptomOrSign", + "value": "SymptomOrSign", + "description": "Symptom or sign" + }, + { + "name": "ConditionQualifier", + "value": "ConditionQualifier", + "description": "Condition qualifier" + }, + { + "name": "MedicationClass", + "value": "MedicationClass", + "description": "Medication class" + }, + { + "name": "MedicationName", + "value": "MedicationName", + "description": "Medication name" + }, + { + "name": "Dosage", + "value": "Dosage", + "description": "Dosage" + }, + { + "name": "MedicationForm", + "value": "MedicationForm", + "description": "Medication form" + }, + { + "name": "MedicationRoute", + "value": "MedicationRoute", + "description": "Medication route" + }, + { + "name": "FamilyRelation", + "value": "FamilyRelation", + "description": "Family relation" + }, + { + "name": "TreatmentName", + "value": "TreatmentName", + "description": "Treatment name" + }, + { + "name": "Ethnicity", + "value": "Ethnicity", + "description": "Ethnicity" + }, + { + "name": "Course", + "value": "Course", + "description": "Course" + }, + { + "name": "Expression", + "value": "Expression", + "description": "Expression" + }, + { + "name": "MutationType", + "value": "MutationType", + "description": "Mutation type" + }, + { + "name": "ConditionScale", + "value": "ConditionScale", + "description": "Condition scale" + }, + { + "name": "Allergen", + "value": "Allergen", + "description": "Allergen" + }, + { + "name": "Employment", + "value": "Employment", + "description": "Employment" + }, + { + "name": "LivingStatus", + "value": "LivingStatus", + "description": "Living status" + }, + { + "name": "SubstanceUse", + "value": "SubstanceUse", + "description": "Substance use" + }, + { + "name": "SubstanceUseAmount", + "value": "SubstanceUseAmount", + "description": "Substance use amount" + } + ] + } + }, + "relationType": { + "type": "string", + "description": "Type of relation. Examples include: `DosageOfMedication` or 'FrequencyOfMedication', etc.", + "enum": [ + "Abbreviation", + "DirectionOfBodyStructure", + "DirectionOfCondition", + "DirectionOfExamination", + "DirectionOfTreatment", + "DosageOfMedication", + "FormOfMedication", + "FrequencyOfMedication", + "FrequencyOfTreatment", + "QualifierOfCondition", + "RelationOfExamination", + "RouteOfMedication", + "TimeOfCondition", + "TimeOfEvent", + "TimeOfExamination", + "TimeOfMedication", + "TimeOfTreatment", + "UnitOfCondition", + "UnitOfExamination", + "ValueOfCondition", + "ValueOfExamination", + "BodySiteOfCondition", + "BodySiteOfTreatment", + "CourseOfCondition", + "CourseOfExamination", + "CourseOfMedication", + "CourseOfTreatment", + "ExaminationFindsCondition", + "ExpressionOfGene", + "ExpressionOfVariant", + "FrequencyOfCondition", + "MutationTypeOfGene", + "MutationTypeOfVariant", + "ScaleOfCondition", + "VariantOfGene" + ], + "x-ms-enum": { + "name": "relationType", + "modelAsString": true, + "values": [ + { + "name": "Abbreviation", + "value": "Abbreviation", + "description": "Abbreviation" + }, + { + "name": "DirectionOfBodyStructure", + "value": "DirectionOfBodyStructure", + "description": "Direction of body structure" + }, + { + "name": "DirectionOfCondition", + "value": "DirectionOfCondition", + "description": "Direction of condition" + }, + { + "name": "DirectionOfExamination", + "value": "DirectionOfExamination", + "description": "Direction of examination" + }, + { + "name": "DirectionOfTreatment", + "value": "DirectionOfTreatment", + "description": "Direction of treatment" + }, + { + "name": "DosageOfMedication", + "value": "DosageOfMedication", + "description": "Dosage of medication" + }, + { + "name": "FormOfMedication", + "value": "FormOfMedication", + "description": "Form of medication" + }, + { + "name": "FrequencyOfMedication", + "value": "FrequencyOfMedication", + "description": "Frequency of medication" + }, + { + "name": "FrequencyOfTreatment", + "value": "FrequencyOfTreatment", + "description": "Frequency of treatment" + }, + { + "name": "QualifierOfCondition", + "value": "QualifierOfCondition", + "description": "Qualifier of condition" + }, + { + "name": "RelationOfExamination", + "value": "RelationOfExamination", + "description": "Relation of examination" + }, + { + "name": "RouteOfMedication", + "value": "RouteOfMedication", + "description": "Route of medication" + }, + { + "name": "TimeOfCondition", + "value": "TimeOfCondition", + "description": "Time of condition" + }, + { + "name": "TimeOfEvent", + "value": "TimeOfEvent", + "description": "Time of event" + }, + { + "name": "TimeOfExamination", + "value": "TimeOfExamination", + "description": "Time of examination" + }, + { + "name": "TimeOfMedication", + "value": "TimeOfMedication", + "description": "Time of medication" + }, + { + "name": "TimeOfTreatment", + "value": "TimeOfTreatment", + "description": "Time of treatment" + }, + { + "name": "UnitOfCondition", + "value": "UnitOfCondition", + "description": "Unit of condition" + }, + { + "name": "UnitOfExamination", + "value": "UnitOfExamination", + "description": "Unit of examination" + }, + { + "name": "ValueOfCondition", + "value": "ValueOfCondition", + "description": "Value of condition" + }, + { + "name": "ValueOfExamination", + "value": "ValueOfExamination", + "description": "Value of examination" + }, + { + "name": "BodySiteOfCondition", + "value": "BodySiteOfCondition", + "description": "Body site of condition" + }, + { + "name": "BodySiteOfTreatment", + "value": "BodySiteOfTreatment", + "description": "Body site of treatment" + }, + { + "name": "CourseOfCondition", + "value": "CourseOfCondition", + "description": "Course of condition" + }, + { + "name": "CourseOfExamination", + "value": "CourseOfExamination", + "description": "Course of examination" + }, + { + "name": "CourseOfMedication", + "value": "CourseOfMedication", + "description": "Course of medication" + }, + { + "name": "CourseOfTreatment", + "value": "CourseOfTreatment", + "description": "Course of treatment" + }, + { + "name": "ExaminationFindsCondition", + "value": "ExaminationFindsCondition", + "description": "Examination finds condition" + }, + { + "name": "ExpressionOfGene", + "value": "ExpressionOfGene", + "description": "Expression of gene" + }, + { + "name": "ExpressionOfVariant", + "value": "ExpressionOfVariant", + "description": "Expression of variant" + }, + { + "name": "FrequencyOfCondition", + "value": "FrequencyOfCondition", + "description": "Frequency of condition" + }, + { + "name": "MutationTypeOfGene", + "value": "MutationTypeOfGene", + "description": "Mutation type of gene" + }, + { + "name": "MutationTypeOfVariant", + "value": "MutationTypeOfVariant", + "description": "Mutation type of variant" + }, + { + "name": "ScaleOfCondition", + "value": "ScaleOfCondition", + "description": "Scale of condition" + }, + { + "name": "VariantOfGene", + "value": "VariantOfGene", + "description": "Variant of gene" + } + ] + } + } + }, + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "description": "The API version to use for this operation.", + "required": true, + "type": "string", + "minLength": 1, + "x-ms-parameter-location": "method", + "x-ms-client-name": "apiVersion" + } + } + } \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backend-pools.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backend-pools.bicep new file mode 100644 index 000000000..e2063bcad --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backend-pools.bicep @@ -0,0 +1,150 @@ +/** + * @module llm-backend-pools + * @description Creates APIM backend pools that group backends by supported models + * + * This module analyzes the LLM backend configuration and creates backend pools. + * Each pool groups backends that support the same model, enabling: + * - Load balancing across multiple backends for the same model + * - Automatic failover if one backend becomes unavailable + * - Priority-based and weighted routing strategies + * + * Example: If three backends support "gpt-4", they are grouped into a "gpt-4-pool" + * that distributes requests across all three backends. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('Array of backend details from llm-backends module output') +param backendDetails array + +@description('Tags to apply to resources') +param tags object = {} + +// ------------------ +// VARIABLES +// ------------------ + +// Extract model names from supportedModels objects for each backend +// supportedModels can be either array of strings (legacy) or array of objects with 'name' property (new) +var normalizedBackendDetails = [for backend in backendDetails: { + backendId: backend.backendId + backendType: backend.backendType + resourceId: backend.resourceId + priority: backend.priority + weight: backend.weight + // Extract model names - handle both string arrays and object arrays + modelNames: map(backend.supportedModels, m => m.name) +}] + +/** + * Group backends by supported models to create backend pools + * This logic creates a map where each model has an array of backends that support it + */ +var modelToBackendsMap = reduce(normalizedBackendDetails, {}, (acc, backend) => union(acc, reduce(backend.modelNames, {}, (modelAcc, model) => union(modelAcc, { + '${model}': union( + contains(acc, model) ? acc[model] : [], + [ + { + backendId: backend.backendId + backendType: backend.backendType + resourceId: backend.resourceId + priority: backend.priority + weight: backend.weight + } + ] + ) +})))) + +/** + * Create pool configurations only for models supported by multiple backends + * Single-backend models don't need a pool - requests route directly to the backend + */ +var poolConfigs = map( + filter(items(modelToBackendsMap), (item) => length(item.value) > 1), + (item) => { + modelName: item.key + poolName: '${replace(item.key, '.', '')}-backend-pool' + backends: item.value + } +) + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +/** + * Create backend pools for models with multiple backend options + * Each pool enables load balancing and failover for the associated model + */ +resource backendPools 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = [for config in poolConfigs: { + name: config.poolName + parent: apimService + properties: { + description: 'Backend pool for model: ${config.modelName}' + type: 'Pool' + // BCP035: protocol and url are not needed in Pool type (this is a known Bicep limitation) + #disable-next-line BCP035 + pool: { + services: [for backend in config.backends: { + id: '/backends/${backend.backendId}' + priority: backend.priority + weight: backend.weight + }] + } + } +}] + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Array of created backend pool names') +output poolNames array = [for (config, i) in poolConfigs: backendPools[i].name] + +@description('Mapping of models to their backend pool names') +output modelToPoolMap object = reduce(poolConfigs, {}, (acc, config) => union(acc, { + '${config.modelName}': config.poolName +})) + +@description('Mapping of models to backend IDs (for models with single backend)') +output modelToBackendMap object = reduce( + filter(items(modelToBackendsMap), (item) => length(item.value) == 1), + {}, + (acc, item) => union(acc, { + '${item.key}': item.value[0].backendId + }) +) + +@description('Complete pool configurations including backend details') +output poolDetails array = [for (config, i) in poolConfigs: { + modelName: config.modelName + poolName: config.poolName + poolType: 'pool' + backends: config.backends +}] + +@description('Configuration for policy fragment generation') +output policyFragmentConfig object = { + backendPools: map(poolConfigs, config => { + poolName: config.poolName + poolType: length(config.backends) > 0 ? config.backends[0].backendType : 'mixed' + supportedModels: [config.modelName] + }) + directBackends: map( + filter(items(modelToBackendsMap), (item) => length(item.value) == 1), + (item) => { + poolName: item.value[0].backendId + poolType: item.value[0].backendType + supportedModels: [item.key] + } + ) +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backends.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backends.bicep new file mode 100644 index 000000000..0fd0fa983 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-backends.bicep @@ -0,0 +1,152 @@ +/** + * @module llm-backends + * @description Creates APIM backends for LLM services (AI Foundry, Azure OpenAI, and other providers) + * + * This module dynamically creates backend resources based on the provided configuration array. + * Each backend represents an LLM endpoint that can serve one or more model deployments. + * + * Supported backend types: + * - ai-foundry: Azure AI Foundry projects with model deployments + * - azure-openai: Azure OpenAI Service endpoints + * - external: Other LLM providers (OpenAI, Anthropic, etc.) + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('User-assigned managed identity client ID for authentication') +param managedIdentityClientId string + +@description('Configuration array for LLM backends') +@metadata({ + example: [ + { + backendId: 'ai-foundry-gpt4' + backendType: 'ai-foundry' + endpoint: 'https://my-foundry-project.eastus.inference.ml.azure.com' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-4', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '0613' } + { name: 'gpt-4-turbo', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '1106' } + ] + priority: 1 + weight: 100 + } + { + backendId: 'azure-openai-eastus' + backendType: 'azure-openai' + endpoint: 'https://my-openai-eastus.openai.azure.com' + authScheme: 'managedIdentity' + supportedModels: [ + { name: 'gpt-35-turbo', sku: 'Standard', capacity: 120, modelFormat: 'OpenAI', modelVersion: '0613' } + { name: 'text-embedding-ada-002', sku: 'Standard', capacity: 120, modelFormat: 'OpenAI', modelVersion: '2' } + ] + priority: 1 + weight: 100 + } + ] +}) +param llmBackendConfig array + +@description('Whether to configure circuit breaker for backends') +param configureCircuitBreaker bool = true + +@description('Tags to apply to resources') +param tags object = {} + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +/** + * Create individual backends for each LLM endpoint + * Each backend represents a single LLM service endpoint with its configuration + * + * supportedModels can be either: + * - Array of strings (legacy): ['gpt-4', 'gpt-35-turbo'] + * - Array of objects (new): [{ name: 'gpt-4', sku: 'GlobalStandard', capacity: 100, modelFormat: 'OpenAI', modelVersion: '1' }] + */ +resource llmBackends 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = [for (config, i) in llmBackendConfig: { + name: config.backendId + parent: apimService + properties: { + description: 'LLM Backend: ${config.backendType} - ${config.backendId} - Supports models: ${join(map(config.supportedModels, m => m.name), ', ')}' + url: config.endpoint + protocol: 'http' + + // Circuit breaker configuration for resilience + circuitBreaker: configureCircuitBreaker ? { + rules: [ + { + failureCondition: { + count: 3 + errorReasons: [ + 'Server errors' + ] + interval: 'PT5M' + statusCodeRanges: [ + { + min: 429 + max: 429 + } + { + min: 500 + max: 503 + } + ] + } + name: '${config.backendId}-breaker-rule' + tripDuration: 'PT1M' + acceptRetryAfter: true + } + ] + } : null + + // Backend authorization configuration + // managed-identity: configure credentials.managedIdentity on the backend resource (native APIM backend auth) + // other auth types: handled in policy fragments + credentials: { + #disable-next-line BCP037 + managedIdentity: config.authScheme == 'managedIdentity' ? { + clientId: managedIdentityClientId + resource: 'https://cognitiveservices.azure.com' + } : null + header: config.authScheme == 'managedIdentity' ? { + 'x-ms-client-id': [ + managedIdentityClientId + ] + } : {} + } + + // TLS configuration for secure communication + tls: { + validateCertificateChain: true + validateCertificateName: true + } + } +}] + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Array of created backend IDs') +output backendIds array = [for (config, i) in llmBackendConfig: llmBackends[i].name] + +@description('Array of backend configurations with resource IDs') +output backendDetails array = [for (config, i) in llmBackendConfig: { + backendId: config.backendId + backendType: config.backendType + resourceId: llmBackends[i].id + supportedModels: config.supportedModels + priority: config.?priority ?? 1 + weight: config.?weight ?? 100 +}] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.bicep new file mode 100644 index 000000000..b087a6a37 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.bicep @@ -0,0 +1,314 @@ +/** + * @module llm-policy-fragments + * @description Generates APIM policy fragments with backend pool configurations + * + * This module creates policy fragments that contain the dynamically generated + * backend pool configurations based on the LLM backend setup. These fragments + * are used by the Universal LLM API policy to route requests appropriately. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apimServiceName string + +@description('Policy fragment configuration from backend pools module') +param policyFragmentConfig object + +@description('User-assigned managed identity client ID for authentication') +param managedIdentityClientId string + +@description('LLM backend configuration with model metadata for available models response') +param llmBackendConfig array = [] + +// ------------------ +// VARIABLES +// ------------------ + +/** + * Generate the backend pools array for the set-backend-pools fragment + * This creates the C# code that will be injected into the policy fragment + */ +var allPools = union(policyFragmentConfig.backendPools, policyFragmentConfig.directBackends) + +// Generate C# code for each backend pool with unique variable names using index +var backendPoolsArray = [for (pool, index) in allPools: replace(replace(replace(replace(''' +// Pool: POOLNAME (Type: POOLTYPE) +var pool_INDEX = new JObject() +{ + { "poolName", "POOLNAME" }, + { "poolType", "POOLTYPE" }, + { "supportedModels", new JArray(MODELS) } +}; +backendPools.Add(pool_INDEX); +''', 'POOLNAME', pool.poolName), 'POOLTYPE', pool.poolType), 'INDEX', string(index)), 'MODELS', join(map(pool.supportedModels, (model) => '"${model}"'), ', '))] + +var backendPoolsCode = join(backendPoolsArray, '\n') + +// Generate model deployments code using reduce to flatten models from all backends +// Each backend generates code for all its supported models (now with per-model metadata) +// supportedModels is now an array of objects: { name, sku?, capacity?, modelFormat?, modelVersion?, retirementDate? } +var modelDeploymentsCodeResult = reduce(llmBackendConfig, { code: '', index: 0 }, (acc, config) => + reduce(config.supportedModels, acc, (modelAcc, model) => { + code: '${modelAcc.code}\n// Model: ${model.name} from backend: ${config.backendId}\nvar deployment_${modelAcc.index} = new JObject()\n{\n { "id", "${config.backendId}" },\n { "type", "${config.backendType}" },\n { "name", "${model.name}" },\n { "sku", new JObject() { { "name", "${model.?sku ?? 'Standard'}" }, { "capacity", ${model.?capacity ?? 100} } } },\n { "properties", new JObject() {\n { "model", new JObject() { { "format", "${model.?modelFormat ?? 'OpenAI'}" }, { "name", "${model.name}" }, { "version", "${model.?modelVersion ?? '1'}" } } },\n { "capabilities", new JObject() { { "chatCompletion", "true" } } },\n { "provisioningState", "Succeeded" }${!empty(model.?retirementDate) ? ',\n { "retirementDate", "${model.retirementDate}" }' : ''}\n }}\n};\nmodelDeployments.Add(deployment_${modelAcc.index});' + index: modelAcc.index + 1 + }) +) + +var modelDeploymentsCode = modelDeploymentsCodeResult.code + +/** + * Complete policy fragment XML with backend pools configuration + */ +var setBackendPoolsFragmentXml = loadTextContent('./policies/frag-set-backend-pools.xml') + +var updatedSetBackendPoolsFragmentXml = replace(setBackendPoolsFragmentXml, '//{backendPoolsCode}', backendPoolsCode) + +/** + * Generate metadata-config fragment for the Unified AI API + * Maps each model to its backend pool/direct backend + apiVersion + timeout + */ + +// Build model-to-pool/backend mapping: for each model, find which pool or direct backend serves it +// Uses the same allPools array that the backend-pools fragment uses +var metadataModelsResult = reduce(llmBackendConfig, { code: '', seenModels: [] }, (acc, config) => + reduce(config.supportedModels, acc, (modelAcc, model) => { + // Find the pool/backend name for this model from allPools + code: contains(modelAcc.seenModels, model.name) ? modelAcc.code : '${modelAcc.code}${length(modelAcc.seenModels) > 0 ? ',\n' : ''}\t\t\t\'${model.name}\': {\n\t\t\t\t\'backend\': \'${reduce(allPools, '', (poolAcc, pool) => contains(pool.supportedModels, model.name) ? pool.poolName : poolAcc)}\',\n\t\t\t\t\'apiVersion\': \'${model.?apiVersion ?? '2024-02-15-preview'}\',\n\t\t\t\t\'timeout\': ${model.?timeout ?? 120}${!empty(model.?inferenceApiVersion) ? ',\n\t\t\t\t\'inferenceApiVersion\': \'${model.inferenceApiVersion}\'' : ''}\n\t\t\t}' + seenModels: contains(modelAcc.seenModels, model.name) ? modelAcc.seenModels : union(modelAcc.seenModels, [model.name]) + }) +) + +var metadataModelsCode = metadataModelsResult.code + +var metadataConfigFragmentXml = loadTextContent('./policies/frag-metadata-config.xml') +var updatedMetadataConfigFragmentXml = replace(metadataConfigFragmentXml, '//{modelsConfigCode}', metadataModelsCode) + +/** + * Enhanced authorization fragment that supports multiple backend types + */ +var setBackendAuthorizationFragmentXml = loadTextContent('./policies/frag-set-backend-authorization.xml') + +/** + * Get available models fragment template + */ +var getAvailableModelsFragmentTemplate = loadTextContent('./policies/frag-get-available-models.xml') + +// Inject generated model deployments code into available models template +var updatedGetAvailableModelsFragmentXml = replace(getAvailableModelsFragmentTemplate, '//{modelDeploymentsCode}', modelDeploymentsCode) + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +// Named values for AWS Bedrock authentication +// Always created with safe defaults so the policy fragment compiles even when no aws-bedrock backends are configured. +resource awsAccessKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-access-key' + parent: apimService + properties: { + displayName: 'aws-access-key' + value: 'NOT_CONFIGURED' + secret: true + } +} + +resource awsSecretKeyNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-secret-key' + parent: apimService + properties: { + displayName: 'aws-secret-key' + value: 'NOT_CONFIGURED' + secret: true + } +} + +resource awsRegionNamedValue 'Microsoft.ApiManagement/service/namedValues@2024-06-01-preview' = { + name: 'aws-region' + parent: apimService + properties: { + displayName: 'aws-region' + value: 'NOT_CONFIGURED' + secret: false + } +} + +/** + * Policy Fragment: Set Backend Pools + * Contains the dynamically generated backend pool configurations + */ +resource setBackendPoolsFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-backend-pools' + parent: apimService + properties: { + description: 'Dynamically generated backend pool configurations for LLM routing' + format: 'rawxml' + value: updatedSetBackendPoolsFragmentXml + } +} + +/** + * Policy Fragment: Set Backend Authorization + * Handles authentication for different backend types (Azure OpenAI, AI Foundry, External) + */ +resource setBackendAuthorizationFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + name: 'set-backend-authorization' + parent: apimService + dependsOn: [ + awsAccessKeyNamedValue + awsSecretKeyNamedValue + awsRegionNamedValue + ] + properties: { + description: 'Authentication and routing configuration for different LLM backend types' + format: 'rawxml' + value: setBackendAuthorizationFragmentXml + } +} + +/** + * Policy Fragment: Set Target Backend Pool + * Determines which backend pool to route requests to based on model and permissions + */ +resource setTargetBackendPoolPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'set-target-backend-pool' + properties: { + description: 'Determines the target backend pool for LLM requests' + value: loadTextContent('./policies/frag-set-target-backend-pool.xml') + format: 'rawxml' + } +} + +resource setLLMUsagePolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'set-llm-usage' + properties: { + description: 'Collects usage metrics for LLM requests' + value: loadTextContent('./policies/frag-set-llm-usage.xml') + format: 'rawxml' + } +} + +/** + * Policy Fragment: Set LLM Requested Model + * Extracts the requested model from either Azure OpenAI endpoint or inference endpoint + */ +resource setLLMRequestedModelPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'set-llm-requested-model' + properties: { + description: 'Extracts the requested model from deployment-id (Azure OpenAI) or request body (Inference)' + value: loadTextContent('./policies/frag-set-llm-requested-model.xml') + format: 'rawxml' + } +} + +/** + * Policy Fragment: Get Available Models + * Returns a JSON response listing all available model deployments with their capabilities + */ +resource getAvailableModelsFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'get-available-models' + properties: { + description: 'Returns a JSON response listing all available model deployments with their capabilities' + value: updatedGetAvailableModelsFragmentXml + format: 'rawxml' + } +} + +/** + * Policy Fragment: Validate Model Access + * Restricts access to specific models based on the allowedModels variable + */ +resource validateModelAccessFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'validate-model-access' + properties: { + description: 'Validates that the requested model is in the allowed models list for the product' + value: loadTextContent('./policies/frag-validate-model-access.xml') + format: 'rawxml' + } +} + +/** + * Policy Fragment: Responses API ID Security (inbound) + * Enforces per-subscription ownership of OpenAI Responses API response_id values + * and hydrates routing for GET/DELETE operations on /responses/{id}. + */ +resource responsesIdSecurityFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'responses-id-security' + properties: { + description: 'Inbound: validates response_id ownership and hydrates routing for /responses operations' + value: loadTextContent('./policies/frag-responses-id-security.xml') + format: 'rawxml' + } +} + +/** + * Policy Fragment: Responses API ID Cache Store (outbound) + * Records response_id → "||" in APIM cache + * after a successful POST /responses, enabling subsequent ownership checks. + */ +resource responsesIdCacheStoreFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'responses-id-cache-store' + properties: { + description: 'Outbound: caches response_id ownership for newly created Responses API objects' + value: loadTextContent('./policies/frag-responses-id-cache-store.xml') + format: 'rawxml' + } +} + +/** + * Policy Fragment: Metadata Configuration + * Provides centralized configuration for the Unified AI API with dynamically generated model mappings + */ +resource metadataConfigFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'metadata-config' + properties: { + description: 'Dynamically generated metadata configuration for Unified AI API routing' + value: updatedMetadataConfigFragmentXml + format: 'rawxml' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Name of the set-backend-pools fragment') +output setBackendPoolsFragmentName string = setBackendPoolsFragment.name + +@description('Name of the set-backend-authorization fragment') +output setBackendAuthorizationFragmentName string = setBackendAuthorizationFragment.name + +@description('Name of the set-target-backend-pool fragment') +output setTargetBackendPoolFragmentName string = setTargetBackendPoolPolicyFragment.name + +@description('Name of the get-available-models fragment') +output getAvailableModelsFragmentName string = getAvailableModelsFragment.name + +@description('Name of the validate-model-access fragment') +output validateModelAccessFragmentName string = validateModelAccessFragment.name + +@description('Name of the metadata-config fragment') +output metadataConfigFragmentName string = metadataConfigFragment.name + +@description('Name of the responses-id-security fragment') +output responsesIdSecurityFragmentName string = responsesIdSecurityFragment.name + +@description('Name of the responses-id-cache-store fragment') +output responsesIdCacheStoreFragmentName string = responsesIdCacheStoreFragment.name + +@description('Generated backend pools configuration code') +output backendPoolsCode string = backendPoolsCode diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.json new file mode 100644 index 000000000..56df3a922 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/llm-policy-fragments.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.39.26.7824", + "templateHash": "18094385805605856491" + } + }, + "parameters": { + "apimServiceName": { + "type": "string", + "metadata": { + "description": "Name of the API Management service" + } + }, + "policyFragmentConfig": { + "type": "object", + "metadata": { + "description": "Policy fragment configuration from backend pools module" + } + }, + "managedIdentityClientId": { + "type": "string", + "metadata": { + "description": "User-assigned managed identity client ID for authentication" + } + }, + "llmBackendConfig": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "LLM backend configuration with model metadata for available models response" + } + } + }, + "variables": { + "copy": [ + { + "name": "backendPoolsArray", + "count": "[length(variables('allPools'))]", + "input": "[replace(replace(replace(replace('// Pool: POOLNAME (Type: POOLTYPE)\r\nvar pool_INDEX = new JObject()\r\n{\r\n { \"poolName\", \"POOLNAME\" },\r\n { \"poolType\", \"POOLTYPE\" },\r\n { \"supportedModels\", new JArray(MODELS) }\r\n};\r\nbackendPools.Add(pool_INDEX);\r\n', 'POOLNAME', variables('allPools')[copyIndex('backendPoolsArray')].poolName), 'POOLTYPE', variables('allPools')[copyIndex('backendPoolsArray')].poolType), 'INDEX', string(copyIndex('backendPoolsArray'))), 'MODELS', join(map(variables('allPools')[copyIndex('backendPoolsArray')].supportedModels, lambda('model', format('\"{0}\"', lambdaVariables('model')))), ', '))]" + } + ], + "$fxv#0": "\r\n \r\n \r\n \r\n s.Trim()).Where(s => s.Length > 0).ToArray();\r\n\r\n // Find all backend pools that support the requested model\r\n List matchingPools = new List();\r\n foreach (JObject pool in backendPools)\r\n {\r\n JArray supportedModels = (JArray)pool[\"supportedModels\"];\r\n if (supportedModels.Any(model => model.ToString().Equals(requestedModel, StringComparison.OrdinalIgnoreCase)))\r\n {\r\n matchingPools.Add(pool);\r\n }\r\n }\r\n\r\n // If we have matching pools, filter by allowed pools if necessary\r\n if (matchingPools.Count > 0)\r\n {\r\n // If there are no restrictions, use the first matching pool\r\n if (allowedPools == null || allowedPools.Length == 0)\r\n {\r\n return matchingPools[0][\"poolName\"].ToString();\r\n }\r\n \r\n // Otherwise, find the first matching pool that is also in allowed pools\r\n foreach (var pool in matchingPools)\r\n {\r\n string poolName = pool[\"poolName\"].ToString();\r\n if (allowedPools.Contains(poolName))\r\n {\r\n return poolName;\r\n }\r\n }\r\n \r\n // If no allowed pools match, check if default backend pool should be used\r\n if (!string.IsNullOrWhiteSpace(defaultBackendPool))\r\n {\r\n return defaultBackendPool;\r\n }\r\n \r\n // If no default is specified, return error that no allowed pools match\r\n return \"ERROR_NO_ALLOWED_POOLS\";\r\n }\r\n\r\n // If no matching pools at all, check for default backend pool\r\n if (!string.IsNullOrWhiteSpace(defaultBackendPool))\r\n {\r\n return defaultBackendPool;\r\n }\r\n \r\n // No matching pools and no default - error\r\n return \"ERROR_NO_MODEL\";\r\n }\" />\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n application/json\r\n \r\n @{\r\n return new JObject(\r\n new JProperty(\"error\", new JObject(\r\n new JProperty(\"message\", \"Model parameter is required in the request payload\"),\r\n new JProperty(\"type\", \"invalid_request_error\"),\r\n new JProperty(\"code\", \"missing_model_parameter\")\r\n ))\r\n ).ToString();\r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n application/json\r\n \r\n @{\r\n string requestedModel = (string)context.Variables[\"requestedModel\"];\r\n JObject modelMappings = (JObject)context.Variables[\"modelMappings\"];\r\n JArray supportedModels = new JArray();\r\n \r\n foreach (var kvp in modelMappings)\r\n {\r\n supportedModels.Add(kvp.Key);\r\n }\r\n \r\n return new JObject(\r\n new JProperty(\"error\", new JObject(\r\n new JProperty(\"message\", $\"Model '{requestedModel}' is not supported. Supported models: {string.Join(\", \", supportedModels.Select(m => m.ToString()))}\"),\r\n new JProperty(\"type\", \"invalid_request_error\"),\r\n new JProperty(\"code\", \"unsupported_model\"),\r\n new JProperty(\"param\", \"model\"),\r\n new JProperty(\"supported_models\", supportedModels)\r\n ))\r\n ).ToString();\r\n }\r\n \r\n \r\n \r\n \r\n \r\n \r\n application/json\r\n \r\n @{\r\n string requestedModel = (string)context.Variables[\"requestedModel\"];\r\n string allowedBackendPools = (string)context.Variables[\"allowedBackendPools\"];\r\n \r\n return new JObject(\r\n new JProperty(\"error\", new JObject(\r\n new JProperty(\"message\", $\"Access to model '{requestedModel}' is not allowed for this client. You do not have access to any of the backend pools that support this model.\"),\r\n new JProperty(\"type\", \"access_error\"),\r\n new JProperty(\"code\", \"backend_pool_access_forbidden\"),\r\n new JProperty(\"allowed_backend_pools\", allowedBackendPools)\r\n ))\r\n ).ToString();\r\n }\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n\r\n", + "$fxv#1": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n (\"requestedModel\", \"DefaultModel\"))\" />\r\n \r\n \r\n \r\n \r\n \r\n (\"appId\", context.Subscription?.Id ?? \"Portal-Admin-Sub\"))\" />\r\n\r\n \r\n (\"customDimension1\", \"NA\"))\" />\r\n (\"customDimension2\", \"NA\"))\" />\r\n\r\n \r\n", + "$fxv#2": "\r\n \r\n \r\n (preserveContent: true);\r\n if (string.IsNullOrEmpty(bodyText))\r\n {\r\n return string.Empty;\r\n }\r\n \r\n var body = JObject.Parse(bodyText);\r\n return body[\"model\"] != null ? body[\"model\"].ToString() : string.Empty;\r\n }\r\n catch\r\n {\r\n return string.Empty;\r\n }\r\n }\" />\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n application/json\r\n \r\n @{\r\n return new JObject(\r\n new JProperty(\"error\", new JObject(\r\n new JProperty(\"message\", \"Model could not be detected\"),\r\n new JProperty(\"type\", \"invalid_request_error\"),\r\n new JProperty(\"code\", \"missing_model_parameter\"),\r\n new JProperty(\"param\", \"model\")\r\n ))\r\n ).ToString();\r\n }\r\n \r\n \r\n \r\n \r\n \r\n\r\n", + "allPools": "[union(parameters('policyFragmentConfig').backendPools, parameters('policyFragmentConfig').directBackends)]", + "backendPoolsCode": "[join(variables('backendPoolsArray'), '\n')]", + "modelDeploymentsCodeResult": "[reduce(parameters('llmBackendConfig'), createObject('code', '', 'index', 0), lambda('acc', 'config', reduce(lambdaVariables('config').supportedModels, lambdaVariables('acc'), lambda('modelAcc', 'model', createObject('code', format('{0}\n// Model: {1} from backend: {2}\nvar deployment_{3} = new JObject()\n{{\n {{ \"id\", \"{4}\" }},\n {{ \"type\", \"{5}\" }},\n {{ \"name\", \"{6}\" }},\n {{ \"sku\", new JObject() {{ {{ \"name\", \"{7}\" }}, {{ \"capacity\", {8} }} }} }},\n {{ \"properties\", new JObject() {{\n {{ \"model\", new JObject() {{ {{ \"format\", \"{9}\" }}, {{ \"name\", \"{10}\" }}, {{ \"version\", \"{11}\" }} }} }},\n {{ \"capabilities\", new JObject() {{ {{ \"chatCompletion\", \"true\" }} }} }},\n {{ \"provisioningState\", \"Succeeded\" }}\n }}}}\n}};\nmodelDeployments.Add(deployment_{12});', lambdaVariables('modelAcc').code, lambdaVariables('model').name, lambdaVariables('config').backendId, lambdaVariables('modelAcc').index, lambdaVariables('config').backendId, lambdaVariables('config').backendType, lambdaVariables('model').name, coalesce(tryGet(lambdaVariables('model'), 'sku'), 'Standard'), coalesce(tryGet(lambdaVariables('model'), 'capacity'), 100), coalesce(tryGet(lambdaVariables('model'), 'modelFormat'), 'OpenAI'), lambdaVariables('model').name, coalesce(tryGet(lambdaVariables('model'), 'modelVersion'), '1'), lambdaVariables('modelAcc').index), 'index', add(lambdaVariables('modelAcc').index, 1))))))]", + "modelDeploymentsCode": "[variables('modelDeploymentsCodeResult').code]", + "setBackendPoolsFragmentXml": "\r\n \r\n \r\n \r\n \r\n", + "updatedSetBackendPoolsFragmentXml": "[replace(variables('setBackendPoolsFragmentXml'), '//{backendPoolsCode}', variables('backendPoolsCode'))]", + "setBackendAuthorizationFragmentXml": "\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n @(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n @(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n @(\"Bearer \" + (string)context.Variables[\"msi-access-token\"])\r\n \r\n \r\n \r\n \r\n \r\n \r\n", + "getAvailableModelsFragmentTemplate": "\r\n \r\n \r\n \r\n \r\n application/json\r\n \r\n @{\r\n JObject response = new JObject();\r\n JArray modelDeployments = new JArray();\r\n \r\n //{modelDeploymentsCode}\r\n \r\n response[\"value\"] = modelDeployments;\r\n return response.ToString();\r\n }\r\n \r\n\r\n", + "updatedGetAvailableModelsFragmentXml": "[replace(variables('getAvailableModelsFragmentTemplate'), '//{modelDeploymentsCode}', variables('modelDeploymentsCode'))]" + }, + "resources": [ + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'set-backend-pools')]", + "properties": { + "description": "Dynamically generated backend pool configurations for LLM routing", + "format": "rawxml", + "value": "[variables('updatedSetBackendPoolsFragmentXml')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'set-backend-authorization')]", + "properties": { + "description": "Authentication and routing configuration for different LLM backend types", + "format": "rawxml", + "value": "[variables('setBackendAuthorizationFragmentXml')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'set-target-backend-pool')]", + "properties": { + "description": "Determines the target backend pool for LLM requests", + "value": "[variables('$fxv#0')]", + "format": "rawxml" + } + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'set-llm-usage')]", + "properties": { + "description": "Collects usage metrics for LLM requests", + "value": "[variables('$fxv#1')]", + "format": "rawxml" + } + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'set-llm-requested-model')]", + "properties": { + "description": "Extracts the requested model from deployment-id (Azure OpenAI) or request body (Inference)", + "value": "[variables('$fxv#2')]", + "format": "rawxml" + } + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2024-06-01-preview", + "name": "[format('{0}/{1}', parameters('apimServiceName'), 'get-available-models')]", + "properties": { + "description": "Returns a JSON response listing all available model deployments with their capabilities", + "value": "[variables('updatedGetAvailableModelsFragmentXml')]", + "format": "rawxml" + } + } + ], + "outputs": { + "setBackendPoolsFragmentName": { + "type": "string", + "metadata": { + "description": "Name of the set-backend-pools fragment" + }, + "value": "set-backend-pools" + }, + "setBackendAuthorizationFragmentName": { + "type": "string", + "metadata": { + "description": "Name of the set-backend-authorization fragment" + }, + "value": "set-backend-authorization" + }, + "setTargetBackendPoolFragmentName": { + "type": "string", + "metadata": { + "description": "Name of the set-target-backend-pool fragment" + }, + "value": "set-target-backend-pool" + }, + "getAvailableModelsFragmentName": { + "type": "string", + "metadata": { + "description": "Name of the get-available-models fragment" + }, + "value": "get-available-models" + }, + "backendPoolsCode": { + "type": "string", + "metadata": { + "description": "Generated backend pools configuration code" + }, + "value": "[variables('backendPoolsCode')]" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-existing.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-existing.bicep new file mode 100644 index 000000000..d184f09db --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-existing.bicep @@ -0,0 +1,84 @@ +param apimServiceName string +param backendName string +param backendDescription string +param backendURL string +param mcpPolicyXml string = '' +param mcpApiName string = 'ms-learn-mcp' +param mcpDisplayName string = 'Microsoft Learn MCP' +param mcpDescription string = 'Microsoft Learn MCP Server' +param mcpPath string = 'ms-learn-mcp' +param mcpProtocols array = [ + 'https' +] +param mcpSubscriptionRequired bool = false +param mcpTransportType string = 'streamable' + +param appInsightsLoggerName string = 'appinsights-logger' + +var defaultPolicyXml = loadTextContent('policies/mcp-default-policy.xml') +var effectivePolicyXml = empty(mcpPolicyXml) ? defaultPolicyXml : mcpPolicyXml + +resource apim 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +resource backend 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = { + parent: apim + name: backendName + properties: { + description: backendDescription + url: backendURL + protocol: 'http' + } +} + +resource mcp 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + parent: apim + name: mcpApiName + properties: { + type: 'mcp' + displayName: mcpDisplayName + description: mcpDescription + subscriptionRequired: mcpSubscriptionRequired + path: mcpPath + protocols: mcpProtocols + backendId: backend.name + mcpPropperties: { + transportType: mcpTransportType + } + } +} + +resource mcpInsights 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = { + name: 'applicationinsights' + parent: mcp + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: resourceId(resourceGroup().name, 'Microsoft.ApiManagement/service/loggers', apimServiceName, appInsightsLoggerName) + metrics: true + verbosity: 'information' + sampling: { + samplingType: 'fixed' + percentage: 100 + } + } +} + +resource policy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + parent: mcp + name: 'policy' + properties: { + value: effectivePolicyXml + format: 'rawxml' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +output name string = mcp.name +output path string = mcpPath +output endpoint string = '${apim.properties.gatewayUrl}/${mcpPath}/mcp' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-from-api.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-from-api.bicep new file mode 100644 index 000000000..37fb5e360 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/mcp-from-api.bicep @@ -0,0 +1,103 @@ +param apimServiceName string + +param appInsightsLoggerName string = 'appinsights-logger' + +param apiName string = 'default-api' +param operationNames array + +param mcpPath string = 'default-mcp' +param mcpName string = 'default-mcp' +param mcpDisplayName string = 'default MCP' +param mcpDescription string = 'MCP for default data' + +param mcpPolicyXml string = '' + +// ------------------ +// VARIABLES +// ------------------ + +var defaultPolicyXml = loadTextContent('policies/mcp-default-policy.xml') +var effectivePolicyXml = empty(mcpPolicyXml) ? defaultPolicyXml : mcpPolicyXml + +var logSettings = { + headers: [ 'Content-type', 'User-agent', 'x-ms-region', 'x-ratelimit-remaining-tokens' , 'x-ratelimit-remaining-requests' ] + body: { bytes: 8192 } +} + + +resource apim 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimServiceName +} + +resource api 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' existing = { + parent: apim + name: apiName +} + +resource operations 'Microsoft.ApiManagement/service/apis/operations@2024-06-01-preview' existing = [for operationName in operationNames: { + parent: api + name: operationName +}] + + +resource mcp 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + parent: apim + name: mcpName + properties: { + type: 'mcp' + displayName: mcpDisplayName + description: mcpDescription + subscriptionRequired: false + path: mcpPath + protocols: [ + 'https' + ] + mcpTools: [for (operationName, i) in operationNames: { + name: operations[i].name + operationId: operations[i].id + description: operations[i].properties.description + }] + } +} + +resource mcpInsights 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = { + name: 'applicationinsights' + parent: mcp + properties: { + alwaysLog: 'allErrors' + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: resourceId(resourceGroup().name, 'Microsoft.ApiManagement/service/loggers', apimServiceName, appInsightsLoggerName) + metrics: true + verbosity: 'information' + sampling: { + samplingType: 'fixed' + percentage: 100 + } + frontend: { + // request: logSettings + // response: logSettings // Do not access the response body using the context.Response.Body variable within MCP server policies. Doing so triggers response buffering, which interferes with the streaming behavior required by MCP servers and may cause them to malfunction. + } + backend: { + // request: logSettings + // response: logSettings + } + } +} + +resource policy 'Microsoft.ApiManagement/service/apis/policies@2021-12-01-preview' = { + parent: mcp + name: 'policy' + properties: { + value: effectivePolicyXml + format: 'rawxml' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +output name string = mcp.name +output path string = mcpPath +output endpoint string = '${apim.properties.gatewayUrl}/${mcpPath}/mcp' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/OpenAI-Import.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/OpenAI-Import.openapi.yaml new file mode 100644 index 000000000..bb0c248e4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/OpenAI-Import.openapi.yaml @@ -0,0 +1,13731 @@ +openapi: 3.0.1 +info: + title: OpenAI-Import + description: Azure OpenAI APIs for completions and search + version: '1.0' +servers: + - url: https://apim-7c3dvrbie5sui.azure-api.net/openai-import/openai +paths: + '/deployments/{deployment-id}/completions': + post: + summary: 'Creates a completion for the provided prompt, parameters and chosen model.' + description: 'Creates a completion for the provided prompt, parameters and chosen model.' + operationId: Completions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the model which was deployed. + example: davinci + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createCompletionRequest' + example: + prompt: "Negate the following sentence.The price for bubblegum increased on thursday.\n\n Negated Sentence:" + max_tokens: 50 + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/createCompletionResponse' + example: + model: davinci + object: text_completion + id: cmpl-4509KAos68kxOqpE2uYGw81j6m7uo + created: 1637097562 + choices: + - index: 0 + text: The price for bubblegum decreased on thursday. + logprobs: + finish_reason: stop + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/embeddings': + post: + summary: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + description: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + operationId: embeddings_create + parameters: + - name: deployment-id + in: path + description: The deployment id of the model which was deployed. + required: true + schema: + type: string + example: ada-search-index-v1 + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + required: + - input + type: object + properties: + input: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - maxItems: 2048 + minItems: 1 + type: array + items: + minLength: 1 + type: string + example: This is a test. + description: 'Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less.' + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse.' + input_type: + type: string + description: input type of embedding search to use + example: query + encoding_format: + type: string + description: The format to return the embeddings in. Can be either `float` or `base64`. Defaults to `float`. + nullable: true + example: float + dimensions: + type: integer + description: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. + nullable: true + example: 1 + example: + input: { } + user: string + input_type: query + encoding_format: float + dimensions: 1 + responses: + '200': + description: OK + content: + application/json: + schema: + required: + - object + - model + - data + - usage + type: object + properties: + object: + type: string + model: + type: string + data: + type: array + items: + required: + - index + - object + - embedding + type: object + properties: + index: + type: integer + object: + type: string + embedding: + type: array + items: + type: number + usage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + total_tokens: + type: integer + example: + object: string + model: string + data: + - index: 0 + object: string + embedding: + - 0 + usage: + prompt_tokens: 0 + total_tokens: 0 + '/deployments/{deployment-id}/chat/completions': + post: + summary: Creates a completion for the chat message + description: Creates a completion for the chat message + operationId: ChatCompletions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the model which was deployed. + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionRequest' + example: + temperature: 1 + top_p: 1 + stream: false + stop: + max_tokens: 0 + max_completion_tokens: 0 + presence_penalty: 0 + frequency_penalty: 0 + logit_bias: + store: true + metadata: { } + user: user-1234 + messages: + - content: { } + role: developer + name: string + data_sources: + - type: azure_search + reasoning_effort: medium + logprobs: false + top_logprobs: 0 + n: 1 + modalities: + - text + prediction: + type: content + content: { } + audio: + voice: alloy + format: wav + parallel_tool_calls: true + response_format: + type: text + seed: 0 + stream_options: + tools: + - type: function + function: + description: string + name: string + parameters: { } + strict: false + tool_choice: { } + function_call: { } + functions: + - description: string + name: string + parameters: { } + user_security_context: + application_name: string + end_user_id: string + end_user_tenant_id: string + source_ip: string + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/createChatCompletionResponse' + - $ref: '#/components/schemas/createChatCompletionStreamResponse' + example: + id: string + prompt_filter_results: + - prompt_index: 0 + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + choices: + - finish_reason: stop + index: 0 + message: + role: assistant + refusal: string + content: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + function_call: + name: string + arguments: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + context: + citations: + - content: string + title: string + url: string + filepath: string + chunk_id: string + rerank_score: 0 + intent: string + all_retrieved_documents: + - content: string + title: string + url: string + filepath: string + chunk_id: string + rerank_score: 0 + search_queries: + - string + data_source_index: 0 + original_search_score: 0 + filter_reason: score + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + protected_material_text: + filtered: true + detected: true + protected_material_code: + filtered: true + detected: true + citation: + URL: string + license: string + ungrounded_material: + filtered: true + detected: true + details: + - completion_start_offset: 0 + completion_end_offset: 0 + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + refusal: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + created: 0 + model: string + system_fingerprint: string + object: chat.completion + usage: + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/audio/transcriptions': + post: + summary: Transcribes audio into the input language. + description: Transcribes audio into the input language. + operationId: Transcriptions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + multipart/form-data: + schema: + required: + - file + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + 'timestamp_granularities[]': + type: array + description: 'The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.' + default: "[\n \"segment\"\n]" + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/audio/translations': + post: + summary: Transcribes and translates input audio into English text. + description: Transcribes and translates input audio into English text. + operationId: Translations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model which was deployed. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + multipart/form-data: + schema: + required: + - file + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/audio/speech': + post: + summary: Generates audio from the input text. + description: Generates audio from the input text. + operationId: Speech_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the tts model which was deployed. + example: tts-1 + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + multipart/form-data: + schema: + required: + - input + - voice + properties: + input: + type: string + description: The text to synthesize audio for. The maximum length is 4096 characters. + voice: + enum: + - alloy + - echo + - fable + - onyx + - nova + - shimmer + type: string + description: The voice to use for speech synthesis. + response_format: + enum: + - mp3 + - opus + - aac + - flac + - wav + - pcm + type: string + description: The format to synthesize the audio in. + speed: + type: number + description: The speed of the synthesize audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + default: 1 + responses: + '200': + description: OK + content: + application/octet-stream: + schema: + type: string + format: binary + examples: + default: + value: + '/deployments/{deployment-id}/images/generations': + post: + summary: Generates a batch of images from a text caption on a given DALLE or gpt-image-1 model deployment + description: Generates a batch of images from a text caption on a given DALLE or gpt-image-1 model deployment + operationId: ImageGenerations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the gpt-image-1 model which was deployed. + example: gpt-image-1-deployment + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/imageGenerationsRequest' + example: + prompt: a corgi in a field + background: auto + n: 1 + output_compression: 100 + output_format: png + quality: auto + response_format: url + size: auto + style: vivid + user: user123456 + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + example: + created: '1676540381' + data: + - url: https://www.contoso.com + b64_json: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + revised_prompt: string + prompt_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + usage: + total_tokens: 100 + input_tokens: 50 + output_tokens: 50 + input_tokens_details: + text_tokens: 10 + image_tokens: 40 + '400': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + '500': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + '/deployments/{deployment-id}/images/edits': + post: + summary: Edits an image from a text caption on a given gpt-image-1 model deployment + description: Edits an image from a text caption on a given gpt-image-1 model deployment + operationId: ImageGenerations_Edit + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the gpt-image-1 model which was deployed. + example: gpt-image-1-deployment + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/imageEditsRequest' + example: + image: { } + prompt: a corgi in a field + mask: string + n: 1 + quality: auto + response_format: url + size: auto + user: user123456 + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + example: + created: '1676540381' + data: + - url: https://www.contoso.com + b64_json: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + revised_prompt: string + prompt_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + usage: + total_tokens: 100 + input_tokens: 50 + output_tokens: 50 + input_tokens_details: + text_tokens: 10 + image_tokens: 40 + '400': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + '500': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + /assistants: + get: + tags: + - Assistants + summary: Returns a list of assistants. + description: Returns a list of assistants. + operationId: List_Assistants + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListAssistantsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listAssistantsResponse' + example: + object: list + data: + - id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + first_id: string + last_id: string + has_more: false + post: + tags: + - Assistants + summary: Create an assistant with a model and instructions. + description: Create an assistant with a model and instructions. + operationId: Create_Assistant + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createAssistantRequest' + example: + model: { } + name: string + description: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + '/assistants/{assistant_id}': + get: + tags: + - Assistants + summary: Retrieves an assistant. + description: Retrieves an assistant. + operationId: Get_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + post: + tags: + - Assistant + summary: Modifies an assistant. + description: Modifies an assistant. + operationId: Modify_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyAssistantRequest' + example: + model: { } + name: string + description: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + delete: + tags: + - Assistants + summary: Delete an assistant. + description: Delete an assistant. + operationId: Delete_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to delete. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteAssistantResponse' + example: + id: string + deleted: true + object: assistant.deleted + /threads: + post: + tags: + - Assistants + summary: Create a thread. + description: Create a thread. + operationId: Create_Thread + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createThreadRequest' + example: + messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + '/threads/{thread_id}': + get: + tags: + - Assistants + summary: Retrieves a thread. + description: Retrieves a thread. + operationId: Get_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + post: + tags: + - Assistants + summary: Modifies a thread. + description: Modifies a thread. + operationId: Modify_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to modify. Only the `metadata` can be modified. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyThreadRequest' + example: + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + delete: + tags: + - Assistants + summary: Delete a thread. + description: Delete a thread. + operationId: Delete_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to delete. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteThreadResponse' + example: + id: string + deleted: true + object: thread.deleted + '/threads/{thread_id}/messages': + get: + tags: + - Assistants + summary: Returns a list of messages for a given thread. + description: Returns a list of messages for a given thread. + operationId: List_Messages + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) the messages belong to.' + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListMessagesOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: run_id + in: query + description: "Filter messages by the run ID that generated them.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listMessagesResponse' + example: + object: list + data: + - id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + first_id: string + last_id: string + has_more: false + post: + tags: + - Assistants + summary: Create a message. + description: Create a message. + operationId: Create_Message + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to create a message for.' + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createMessageRequest' + example: + role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + '/threads/{thread_id}/messages/{message_id}': + get: + tags: + - Assistants + summary: Retrieve a message. + description: Retrieve a message. + operationId: Get_Message + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to which this message belongs.' + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + post: + tags: + - Assistants + summary: Modifies a message. + description: Modifies a message. + operationId: Modify_Message + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this message belongs. + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyMessageRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + /threads/runs: + post: + tags: + - Assistants + summary: Create a thread and run it in one request. + description: Create a thread and run it in one request. + operationId: Create_Thread_And_Run + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createThreadAndRunRequest' + example: + assistant_id: string + thread: + messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + stream: true + stream_options: + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs': + get: + tags: + - Assistants + summary: Returns a list of runs belonging to a thread. + description: Returns a list of runs belonging to a thread. + operationId: List_Runs + parameters: + - name: thread_id + in: path + description: The ID of the thread the run belongs to. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListRunsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listRunsResponse' + example: + object: list + data: + - id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + first_id: string + last_id: string + has_more: false + post: + tags: + - Assistants + summary: Create a run. + description: Create a run. + operationId: Create_Run + parameters: + - name: thread_id + in: path + description: The ID of the thread to run. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: 'include[]' + in: query + description: 'A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.' + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + x-ms-enum: + name: Include + modelAsString: true + values: + - value: 'step_details.tool_calls[*].file_search.results[*].content' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createRunRequest' + example: + assistant_id: string + model: string + instructions: string + additional_instructions: string + additional_messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tools: + - type: code_interpreter + metadata: { } + temperature: 1 + top_p: 1 + stream: true + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}': + get: + tags: + - Assistants + summary: Retrieves a run. + description: Retrieves a run. + operationId: Get_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + post: + tags: + - Assistants + summary: Modifies a run. + description: Modifies a run. + operationId: Modify_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyRunRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}/submit_tool_outputs': + post: + tags: + - Assistants + summary: "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n" + description: "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n" + operationId: Submit_Tool_Outputs_To_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to which this run belongs.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run that requires the tool output submission. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/submitToolOutputsRunRequest' + example: + tool_outputs: + - tool_call_id: string + output: string + stream: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}/cancel': + post: + tags: + - Assistants + summary: Cancels a run that is `in_progress`. + description: Cancels a run that is `in_progress`. + operationId: Cancel_Run + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this run belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to cancel. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}/steps': + get: + tags: + - Assistants + summary: Returns a list of run steps belonging to a run. + description: Returns a list of run steps belonging to a run. + operationId: List_Run_Steps + parameters: + - name: thread_id + in: path + description: The ID of the thread the run and run steps belong to. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run the run steps belong to. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListRunStepsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: 'include[]' + in: query + description: "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n" + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + x-ms-enum: + name: Include + modelAsString: true + values: + - value: 'step_details.tool_calls[*].file_search.results[*].content' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listRunStepsResponse' + example: + object: list + data: + - id: string + object: assistant.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + first_id: string + last_id: string + has_more: false + '/threads/{thread_id}/runs/{run_id}/steps/{step_id}': + get: + tags: + - Assistants + summary: Retrieves a run step. + description: Retrieves a run step. + operationId: Get_Run_Step + parameters: + - name: thread_id + in: path + description: The ID of the thread to which the run and run step belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to which the run step belongs. + required: true + schema: + type: string + - name: step_id + in: path + description: The ID of the run step to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2025-04-01-preview + - name: 'include[]' + in: query + description: "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n" + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + x-ms-enum: + name: Include + modelAsString: true + values: + - value: 'step_details.tool_calls[*].file_search.results[*].content' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runStepObject' + example: + id: string + object: assistant.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + /vector_stores: + get: + tags: + - Vector stores + summary: Returns a list of vector stores. + description: Returns a list of vector stores. + operationId: List_Vector_Stores + parameters: + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoresOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoresResponse' + example: + object: list + data: + - id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + first_id: vs_abc123 + last_id: vs_abc456 + has_more: false + post: + tags: + - Vector stores + summary: Create a vector store. + description: Create a vector store. + operationId: Create_Vector_Store + parameters: + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreRequest' + example: + file_ids: + - string + name: string + expires_after: + anchor: last_active_at + days: 0 + chunking_strategy: + type: auto + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + '/vector_stores/{vector_store_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store. + description: Retrieves a vector store. + operationId: Get_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + post: + tags: + - Vector stores + summary: Modifies a vector store. + description: Modifies a vector store. + operationId: Modify_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to modify. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/updateVectorStoreRequest' + example: + name: string + expires_after: + anchor: last_active_at + days: 0 + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + delete: + tags: + - Vector stores + summary: Delete a vector store. + description: Delete a vector store. + operationId: Delete_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteVectorStoreResponse' + example: + id: string + deleted: true + object: vector_store.deleted + '/vector_stores/{vector_store_id}/files': + get: + tags: + - Vector stores + summary: Returns a list of vector store files. + description: Returns a list of vector store files. + operationId: List_Vector_Store_Files + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoreFilesOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: 'Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.' + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + x-ms-enum: + name: ListVectorStoreFilesFilter + modelAsString: true + values: + - value: in_progress + - value: completed + - value: failed + - value: cancelled + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: auto + first_id: file-abc123 + last_id: file-abc456 + has_more: false + post: + tags: + - Vector stores + summary: 'Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).' + description: 'Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).' + operationId: Create_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File.\n" + required: true + schema: + type: string + example: vs_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreFileRequest' + example: + file_id: string + chunking_strategy: + type: auto + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: auto + '/vector_stores/{vector_store_id}/files/{file_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store file. + description: Retrieves a vector store file. + operationId: Get_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file being retrieved. + required: true + schema: + type: string + example: file-abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: auto + delete: + tags: + - Vector stores + summary: 'Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.' + description: 'Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.' + operationId: Delete_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + - name: file_id + in: path + description: The ID of the file to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteVectorStoreFileResponse' + example: + id: string + deleted: true + object: vector_store.file.deleted + post: + tags: + - Vector stores + summary: Update attributes on a vector store file. + description: Update attributes on a vector store file. + operationId: updateVectorStoreFileAttributes + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store the file belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file to update attributes. + required: true + schema: + type: string + example: file-abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateVectorStoreFileAttributesRequest' + example: + attributes: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: auto + '/vector_stores/{vector_store_id}/files/{file_id}/content': + get: + tags: + - Vector stores + summary: Retrieve the parsed contents of a vector store file. + description: Retrieve the parsed contents of a vector store file. + operationId: retrieveVectorStoreFileContent + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file within the vector store. + required: true + schema: + type: string + example: file-abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileContentResponse' + example: + object: vector_store.file_content.page + data: + - type: string + text: string + has_more: true + next_page: string + '/vector_stores/{vector_store_id}/search': + post: + tags: + - Vector stores + summary: Search a vector store for relevant chunks based on a query and file attributes filter. + description: Search a vector store for relevant chunks based on a query and file attributes filter. + operationId: searchVectorStore + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to search. + required: true + schema: + type: string + example: vs_abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreSearchRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreSearchResultsPage' + example: + object: vector_store.search_results.page + search_query: + - string + data: + - file_id: string + filename: string + score: 0 + attributes: { } + content: + - type: text + text: string + has_more: true + next_page: string + '/vector_stores/{vector_store_id}/file_batches': + post: + tags: + - Vector stores + summary: Create a vector store file batch. + description: Create a vector store file batch. + operationId: Create_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File Batch.\n" + required: true + schema: + type: string + example: vs_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreFileBatchRequest' + example: + file_ids: + - string + chunking_strategy: + type: auto + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store file batch. + description: Retrieves a vector store file batch. + operationId: Get_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: batch_id + in: path + description: The ID of the file batch being retrieved. + required: true + schema: + type: string + example: vs_b_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel': + post: + tags: + - Vector stores + summary: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + description: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + operationId: Cancel_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch to cancel. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/files': + get: + tags: + - Vector stores + summary: Returns a list of vector store files in a batch. + description: Returns a list of vector store files in a batch. + operationId: List_Vector_Store_File_Batch_Files + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch that the files belong to. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2025-04-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoreFilesOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: 'Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.' + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + x-ms-enum: + name: ListVectorStoreFilesFilter + modelAsString: true + values: + - value: in_progress + - value: completed + - value: failed + - value: cancelled + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: auto + first_id: file-abc123 + last_id: file-abc456 + has_more: false + /realtimeapi/sessions: + post: + tags: + - Realtime + summary: "Create an ephemeral API token for use in client-side applications with the Realtime API. Can be configured with the same session parameters as the `session.update` client event.\nIt responds with a session object, plus a `client_secret` key which contains a usable ephemeral API token that can be used" + description: "Create an ephemeral API token for use in client-side applications with the Realtime API. Can be configured with the same session parameters as the `session.update` client event.\nIt responds with a session object, plus a `client_secret` key which contains a usable ephemeral API token that can be used to authenticate browser clients for the Realtime API.\n" + operationId: create_realtimeSession + requestBody: + description: Create an ephemeral API key with the given session configuration. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeSessionCreateRequest' + example: + modalities: { } + model: string + instructions: string + voice: ash + input_audio_format: pcm16 + output_audio_format: pcm16 + input_audio_transcription: + model: string + language: string + prompt: string + turn_detection: + type: server_vad + eagerness: auto + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + create_response: true + interrupt_response: true + input_audio_noise_reduction: + tools: + - type: function + name: string + description: string + parameters: { } + tool_choice: auto + temperature: 0.8 + max_response_output_tokens: { } + responses: + '200': + description: Session created successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeSessionCreateResponse' + example: + client_secret: + value: string + expires_at: 0 + modalities: { } + instructions: string + voice: ash + input_audio_format: string + output_audio_format: string + input_audio_transcription: + model: string + turn_detection: + type: string + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + tools: + - type: function + name: string + description: string + parameters: { } + tool_choice: string + temperature: 0 + max_response_output_tokens: { } + /realtimeapi/transcription_sessions: + post: + tags: + - Realtime + summary: "Create an ephemeral API token for use in client-side applications with the Realtime API specifically for realtime transcriptions. \nCan be configured with the same session parameters as the `transcription_session.update` client event.\nIt responds with a session object, plus a `client_secret` key whic" + description: "Create an ephemeral API token for use in client-side applications with the Realtime API specifically for realtime transcriptions. \nCan be configured with the same session parameters as the `transcription_session.update` client event.\nIt responds with a session object, plus a `client_secret` key which contains a usable ephemeral API token that can be used to authenticate browser clients for the Realtime API.\n" + operationId: create_transcriptionRealtimeSession + requestBody: + description: Create an ephemeral API key with the given session configuration. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest' + example: + modalities: { } + input_audio_format: pcm16 + input_audio_transcription: + model: gpt-4o-transcribe + language: string + prompt: string + turn_detection: + type: server_vad + eagerness: auto + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + create_response: true + interrupt_response: true + input_audio_noise_reduction: + include: + - string + responses: + '200': + description: Session created successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse' + example: + client_secret: + value: string + expires_at: 0 + modalities: { } + input_audio_format: string + input_audio_transcription: + model: gpt-4o-transcribe + language: string + prompt: string + turn_detection: + type: string + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + /responses: + post: + summary: "Creates a model response. Provide [text](/docs/guides/text) or\n[image](/docs/guides/images) inputs to generate [text](/docs/guides/text)\nor [JSON](/docs/guides/structured-outputs) outputs. Have the model call\nyour own [custom code](/docs/guides/function-calling) or use built-in\n[tools](/docs/guides/" + description: "Creates a model response. Provide [text](/docs/guides/text) or\n[image](/docs/guides/images) inputs to generate [text](/docs/guides/text)\nor [JSON](/docs/guides/structured-outputs) outputs. Have the model call\nyour own [custom code](/docs/guides/function-calling) or use built-in\n[tools](/docs/guides/tools) like [web search](/docs/guides/tools-web-search)\nor [file search](/docs/guides/tools-file-search) to use your own data\nas input for the model's response.\n" + operationId: post-responses + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createResponse' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/response' + text/event-stream: + schema: + $ref: '#/components/schemas/responseStreamEvent' + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/responses/{response_id}': + get: + summary: "Retrieves a model response with the given ID.\n" + description: "Retrieves a model response with the given ID.\n" + operationId: get-responses-response_id + parameters: + - name: response_id + in: path + description: The ID of the response to retrieve. + required: true + schema: + type: string + example: resp_677efb5139a88190b512bc3fef8e535d + - name: include + in: query + description: "Additional fields to include in the response. See the `include`\nparameter for Response creation above for more information.\n" + schema: + type: array + items: + $ref: '#/components/schemas/includable' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/response' + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + delete: + tags: + - Responses + summary: "Deletes a model response with the given ID.\n" + description: "Deletes a model response with the given ID.\n" + operationId: deleteResponse + parameters: + - name: response_id + in: path + description: The ID of the response to delete. + required: true + schema: + type: string + example: resp_677efb5139a88190b512bc3fef8e535d + responses: + '200': + description: OK + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/error' + example: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/responses/{response_id}/input_items': + get: + summary: Returns a list of input items for a given response. + description: Returns a list of input items for a given response. + operationId: get-responses-response_id-input_items + parameters: + - name: response_id + in: path + description: The ID of the response to retrieve input items for. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between\n1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "The order to return the input items in. Default is `asc`.\n- `asc`: Return the input items in ascending order.\n- `desc`: Return the input items in descending order.\n" + schema: + enum: + - asc + - desc + type: string + - name: after + in: query + description: "An item ID to list items after, used in pagination.\n" + schema: + type: string + - name: before + in: query + description: "An item ID to list items before, used in pagination.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/responseItemList' + example: + object: list + data: + - type: message + role: user + status: in_progress + content: + - type: input_text + text: string + id: string + has_more: true + first_id: string + last_id: string + /deployments: + get: + summary: Deployments + description: Get Azure OpenAI resource deployments for AI Foundry integration + operationId: deployments + responses: + '200': + description: '' +components: + schemas: + errorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/error' + errorBase: + type: object + properties: + code: + type: string + message: + type: string + error: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/innerError' + innerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Inner error with additional details. + innerErrorCode: + enum: + - ResponsibleAIPolicyViolation + type: string + description: Error codes for the inner error object. + x-ms-enum: + name: InnerErrorCode + modelAsString: true + values: + - value: ResponsibleAIPolicyViolation + description: The prompt violated one of more content filter rules. + dalleErrorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/dalleError' + dalleError: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/dalleInnerError' + dalleInnerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + description: Inner error with additional details. + contentFilterCompletionTextSpan: + required: + - completion_start_offset + - completion_end_offset + type: object + properties: + completion_start_offset: + type: integer + description: Offset of the UTF32 code point which begins the span. + completion_end_offset: + type: integer + description: Offset of the first UTF32 code point which is excluded from the span. This field is always equal to completion_start_offset for empty spans. This field is always larger than completion_start_offset for non-empty spans. + description: Describes a span within generated completion text. Offset 0 is the first UTF32 code point of the completion text. + contentFilterResultBase: + required: + - filtered + type: object + properties: + filtered: + type: boolean + contentFilterSeverityResult: + required: + - severity + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + severity: + enum: + - safe + - low + - medium + - high + type: string + x-ms-enum: + name: ContentFilterSeverity + modelAsString: true + values: + - value: safe + description: General content or related content in generic or non-harmful contexts. + - value: low + description: Harmful content at a low intensity and risk level. + - value: medium + description: Harmful content at a medium intensity and risk level. + - value: high + description: Harmful content at a high intensity and risk level. + contentFilterDetectedResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + detected: + type: boolean + contentFilterDetectedWithCitationResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + citation: + type: object + properties: + URL: + type: string + license: + type: string + contentFilterDetectedWithCompletionTextSpansResult: + required: + - detected + - filtered + - details + type: object + allOf: + - $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + details: + type: array + items: + $ref: '#/components/schemas/contentFilterCompletionTextSpan' + contentFilterIdResult: + required: + - id + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + id: + type: string + contentFilterResultsBase: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + custom_blocklists: + $ref: '#/components/schemas/contentFilterDetailedResults' + error: + $ref: '#/components/schemas/errorBase' + description: Information about the content filtering results. + contentFilterPromptResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + indirect_attack: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterChoiceResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + protected_material_text: + $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + protected_material_code: + $ref: '#/components/schemas/contentFilterDetectedWithCitationResult' + - properties: + ungrounded_material: + $ref: '#/components/schemas/contentFilterDetectedWithCompletionTextSpansResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterDetailedResults: + required: + - filtered + - details + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + details: + type: array + items: + $ref: '#/components/schemas/contentFilterIdResult' + description: Content filtering results with a detail of content filter ids for the filtered segments. + promptFilterResult: + type: object + properties: + prompt_index: + type: integer + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Content filtering results for a single prompt in the request. + promptFilterResults: + type: array + items: + $ref: '#/components/schemas/promptFilterResult' + description: 'Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.' + dalleContentFilterResults: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + description: Information about the content filtering results. + dalleFilterResults: + type: object + allOf: + - $ref: '#/components/schemas/dalleContentFilterResults' + - properties: + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + custom_blocklists: + $ref: '#/components/schemas/contentFilterDetailedResults' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + chatCompletionsRequestCommon: + type: object + properties: + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or `temperature` but not both." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.' + default: false + nullable: true + stop: + oneOf: + - type: string + nullable: true + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. + default: + max_tokens: + type: integer + description: 'The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens). This is not compatible with o1 series models.' + default: 4096 + max_completion_tokens: + type: integer + description: 'An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. This is only supported in o1 series models. Will expand the support to other models in future API release.' + nullable: true + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + logit_bias: + type: object + description: 'Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.' + nullable: true + store: + type: boolean + description: Whether or not to store the output of this chat completion request for use in our model distillation or evaluation products. + nullable: true + metadata: + type: object + additionalProperties: + type: string + description: Developer-defined tags and values used for filtering completions in the stored completions dashboard. + nullable: true + user: + type: string + description: 'A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.' + example: user-1234 + createCompletionRequest: + required: + - prompt + type: object + properties: + prompt: + oneOf: + - type: string + default: '' + example: This is a test. + - type: array + items: + type: string + default: '' + example: This is a test. + description: "The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.\n\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.\n" + default: <|endoftext|> + nullable: true + best_of: + maximum: 20.0 + minimum: 0.0 + type: integer + description: "Generates `best_of` completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\n\nWhen used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + default: 1 + nullable: true + echo: + type: boolean + description: "Echo back the prompt in addition to the completion\n" + default: false + nullable: true + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n" + default: 0 + nullable: true + logit_bias: + type: object + additionalProperties: + type: integer + description: "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n\nAs an example, you can pass `{\"50256\": -100}` to prevent the <|endoftext|> token from being generated.\n" + default: + nullable: true + x-oaiTypeLabel: map + logprobs: + maximum: 5.0 + minimum: 0.0 + type: integer + description: "Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.\n\nThe maximum value for `logprobs` is 5.\n" + default: + nullable: true + max_tokens: + minimum: 0.0 + type: integer + description: "The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.\n\nThe token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n" + default: 16 + nullable: true + example: 16 + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: "How many completions to generate for each prompt.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + default: 1 + nullable: true + example: 1 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n" + default: 0 + nullable: true + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\n\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + nullable: true + stop: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: "\n" + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + example: '["\n"]' + description: "Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.\n" + default: + nullable: true + stream: + type: boolean + description: "Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n" + default: false + nullable: true + suffix: + type: string + description: "The suffix that comes after a completion of inserted text.\n\nThis parameter is only supported for `gpt-3.5-turbo-instruct`.\n" + default: + nullable: true + example: test. + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + default: 1 + nullable: true + example: 1 + user: + type: string + description: "A unique identifier representing your end-user, which can help to monitor and detect abuse.\n" + example: user-1234 + createCompletionResponse: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + description: A unique identifier for the completion. + choices: + type: array + items: + required: + - finish_reason + - index + - logprobs + - text + type: object + properties: + finish_reason: + enum: + - stop + - length + - content_filter + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\nor `content_filter` if content was omitted due to a flag from our content filters.\n" + index: + type: integer + logprobs: + type: object + properties: + text_offset: + type: array + items: + type: integer + token_logprobs: + type: array + items: + type: number + tokens: + type: array + items: + type: string + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + nullable: true + text: + type: string + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + description: The list of completion choices the model generated for the input prompt. + created: + type: integer + description: The Unix timestamp (in seconds) of when the completion was created. + model: + type: string + description: The model used for completion. + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + enum: + - text_completion + type: string + description: 'The object type, which is always "text_completion"' + usage: + $ref: '#/components/schemas/completionUsage' + description: "Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).\n" + createChatCompletionRequest: + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsRequestCommon' + - required: + - messages + properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessage' + description: 'A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).' + data_sources: + type: array + items: + $ref: '#/components/schemas/azureChatExtensionConfiguration' + description: " The configuration entries for Azure OpenAI chat extensions that use them.\n This additional specification is only compatible with Azure OpenAI." + reasoning_effort: + enum: + - low + - medium + - high + type: string + description: "**o1 models only** \n\n Constrains effort on reasoning for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n\nCurrently supported values are `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." + default: medium + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n" + default: 0 + nullable: true + logit_bias: + type: object + additionalProperties: + type: integer + description: "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n" + default: + nullable: true + logprobs: + type: boolean + description: 'Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of `message`.' + default: false + nullable: true + top_logprobs: + maximum: 20.0 + minimum: 0.0 + type: integer + description: 'An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to `true` if this parameter is used.' + nullable: true + max_tokens: + type: integer + description: "The maximum number of [tokens](/tokenizer) that can be generated in the chat completion.\n\nThe total length of input tokens and generated tokens is limited by the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n" + nullable: true + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + default: 1 + nullable: true + example: 1 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n" + default: 0 + nullable: true + modalities: + $ref: '#/components/schemas/ChatCompletionModalities' + prediction: + oneOf: + - $ref: '#/components/schemas/PredictionContent' + description: 'Configuration for a [Predicted Output](/docs/guides/predicted-outputs), which can greatly improve response times when large parts of the model response are known ahead of time. This is most common when you are regenerating a file with only minor changes to most of the content.' + nullable: true + x-oaiExpandable: true + audio: + required: + - voice + - format + type: object + properties: + voice: + enum: + - alloy + - echo + - fable + - onyx + - nova + - shimmer + type: string + description: "Specifies the voice type. Supported voices are `alloy`, `echo`, \n`fable`, `onyx`, `nova`, and `shimmer`.\n" + format: + enum: + - wav + - mp3 + - flac + - opus + - pcm16 + type: string + description: "Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,\n`opus`, or `pcm16`. \n" + description: "Parameters for audio output. Required when audio output is requested with\n`modalities: [\"audio\"]`. [Learn more](/docs/guides/audio).\n" + x-oaiExpandable: true + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + oneOf: + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + - $ref: '#/components/schemas/ResponseFormatJsonSchema' + description: "An object specifying the format that the model must output. Compatible with [GPT-4o](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4o mini](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4 Turbo](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models) and all [GPT-3.5](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-35) Turbo models newer than `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which guarantees the model will match your supplied JSON schema.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which guarantees the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.\n" + x-oaiExpandable: true + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: "This feature is in Beta.\nIf specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + nullable: true + stop: + oneOf: + - type: string + nullable: true + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + description: "Up to 4 sequences where the API will stop generating further tokens.\n" + default: + stream: + type: boolean + description: "If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n" + default: false + nullable: true + stream_options: + $ref: '#/components/schemas/chatCompletionStreamOptions' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + default: 1 + nullable: true + example: 1 + tools: + type: array + items: + $ref: '#/components/schemas/chatCompletionTool' + description: "A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.\n" + tool_choice: + $ref: '#/components/schemas/chatCompletionToolChoiceOption' + function_call: + oneOf: + - enum: + - none + - auto + type: string + description: "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n" + - $ref: '#/components/schemas/chatCompletionFunctionCallOption' + description: "Deprecated in favor of `tool_choice`.\n\nControls which (if any) function is called by the model.\n`none` means the model will not call a function and instead generates a message.\n`auto` means the model can pick between generating a message or calling a function.\nSpecifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n\n`none` is the default when no functions are present. `auto` is the default if functions are present.\n" + deprecated: true + x-oaiExpandable: true + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionFunctions' + description: "Deprecated in favor of `tools`.\n\nA list of functions the model may generate JSON inputs for.\n" + deprecated: true + user: + type: string + description: "A unique identifier representing your end-user, which can help to monitor and detect abuse.\n" + example: user-1234 + user_security_context: + $ref: '#/components/schemas/userSecurityContext' + userSecurityContext: + type: object + properties: + application_name: + type: string + description: The name of the application. Sensitive personal information should not be included in this field. + end_user_id: + type: string + description: This identifier is the Microsoft Entra ID (formerly Azure Active Directory) user object ID used to authenticate end-users within the generative AI application. Sensitive personal information should not be included in this field. + format: uuid + end_user_tenant_id: + type: string + description: The Microsoft 365 tenant ID the end user belongs to. It's required when the generative AI application is multi tenant. + format: uuid + source_ip: + type: string + description: 'Captures the original client''s IP address, accepting both IPv4 and IPv6 formats.' + description: 'User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. These fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. [Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud.' + chatCompletionFunctions: + required: + - name + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/FunctionParameters' + deprecated: true + chatCompletionFunctionCallOption: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: "Specifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n" + chatCompletionFunctionParameters: + type: object + description: 'The parameters the functions accepts, described as a JSON Schema object. See the [guide/](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.' + chatCompletionRequestMessage: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestDeveloperMessage' + - $ref: '#/components/schemas/chatCompletionRequestSystemMessage' + - $ref: '#/components/schemas/chatCompletionRequestUserMessage' + - $ref: '#/components/schemas/chatCompletionRequestAssistantMessage' + - $ref: '#/components/schemas/chatCompletionRequestToolMessage' + - $ref: '#/components/schemas/chatCompletionRequestFunctionMessage' + ChatCompletionRequestDeveloperMessage: + title: Developer message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the developer message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestDeveloperMessageContentPart' + description: 'An array of content parts with a defined type. For developer messages, only type `text` is supported.' + description: The contents of the developer message. + role: + enum: + - developer + type: string + description: 'The role of the messages author, in this case `developer`.' + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + description: "Developer-provided instructions that the model should follow, regardless of messages sent by the user.\nWith o1 models and newer, `developer` messages replace the previous `system` messages." + chatCompletionRequestSystemMessage: + title: System message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the system message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestSystemMessageContentPart' + description: 'An array of content parts with a defined type. For system messages, only type `text` is supported.' + description: The contents of the system message. + role: + enum: + - system + type: string + description: 'The role of the messages author, in this case `system`.' + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + chatCompletionRequestUserMessage: + title: User message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The text contents of the message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestUserMessageContentPart' + description: 'An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4o` model.' + description: "The contents of the user message.\n" + x-oaiExpandable: true + role: + enum: + - user + type: string + description: 'The role of the messages author, in this case `user`.' + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + chatCompletionRequestAssistantMessage: + title: Assistant message + required: + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the assistant message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestAssistantMessageContentPart' + description: 'An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`.' + description: "The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.\n" + nullable: true + refusal: + type: string + description: The refusal message by the assistant. + nullable: true + role: + enum: + - assistant + type: string + description: 'The role of the messages author, in this case `assistant`.' + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + tool_calls: + $ref: '#/components/schemas/chatCompletionMessageToolCalls' + function_call: + required: + - arguments + - name + type: object + properties: + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + name: + type: string + description: The name of the function to call. + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + nullable: true + deprecated: true + chatCompletionRequestToolMessage: + title: Tool message + required: + - role + - content + - tool_call_id + type: object + properties: + role: + enum: + - tool + type: string + description: 'The role of the messages author, in this case `tool`.' + content: + oneOf: + - title: Text content + type: string + description: The contents of the tool message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestToolMessageContentPart' + description: 'An array of content parts with a defined type. For tool messages, only type `text` is supported.' + description: The contents of the tool message. + tool_call_id: + type: string + description: Tool call that this message is responding to. + chatCompletionRequestFunctionMessage: + title: Function message + required: + - role + - content + - name + type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + content: + type: string + description: The contents of the function message. + nullable: true + name: + type: string + description: The name of the function to call. + deprecated: true + chatCompletionRequestDeveloperMessageContentPart: + oneOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + chatCompletionRequestSystemMessageContentPart: + oneOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + chatCompletionRequestUserMessageContentPart: + oneOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartImage' + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartAudio' + chatCompletionRequestAssistantMessageContentPart: + oneOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartRefusal' + chatCompletionRequestToolMessageContentPart: + oneOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + chatCompletionRequestMessageContentPartText: + title: Text content part + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: The type of the content part. + text: + type: string + description: The text content. + chatCompletionRequestMessageContentPartAudio: + title: Audio content part + required: + - type + - input_audio + type: object + properties: + type: + enum: + - input_audio + type: string + description: The type of the content part. Always `input_audio`. + input_audio: + required: + - data + - format + type: object + properties: + data: + type: string + description: Base64 encoded audio data. + format: + enum: + - wav + - mp3 + type: string + description: "The format of the encoded audio data. Currently supports \"wav\" and \"mp3\".\n" + description: "Learn about [audio inputs](/docs/guides/audio).\n" + chatCompletionRequestMessageContentPartImage: + title: Image content part + required: + - type + - image_url + type: object + properties: + type: + enum: + - image_url + type: string + description: The type of the content part. + image_url: + required: + - url + type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + enum: + - auto + - low + - high + type: string + description: 'Specifies the detail level of the image. Learn more in the [Vision guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/gpt-with-vision?tabs=rest%2Csystem-assigned%2Cresource#detail-parameter-settings-in-image-processing-low-high-auto).' + default: auto + chatCompletionRequestMessageContentPartRefusal: + title: Refusal content part + required: + - type + - refusal + type: object + properties: + type: + enum: + - refusal + type: string + description: The type of the content part. + refusal: + type: string + description: The refusal message generated by the model. + azureChatExtensionConfiguration: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/azureChatExtensionType' + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + discriminator: + propertyName: type + mapping: + azure_search: '#/components/schemas/azureSearchChatExtensionConfiguration' + azure_cosmos_db: '#/components/schemas/azureCosmosDBChatExtensionConfiguration' + elasticsearch: '#/components/schemas/elasticsearchChatExtensionConfiguration' + mongo_db: '#/components/schemas/mongoDBChatExtensionConfiguration' + pinecone: '#/components/schemas/pineconeChatExtensionConfiguration' + azureChatExtensionType: + enum: + - azure_search + - azure_cosmos_db + - elasticsearch + - mongo_db + - pinecone + type: string + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + x-ms-enum: + name: AzureChatExtensionType + modelAsString: true + values: + - name: azureSearch + value: azure_search + description: Represents the use of Azure Search as an Azure OpenAI chat extension. + - name: azureCosmosDB + value: azure_cosmos_db + description: Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + - name: elasticsearch + value: elasticsearch + description: Represents the use of Elasticsearch® index as an Azure OpenAI chat extension. + - name: mongoDB + value: mongo_db + description: Represents the use of Mongo DB as an Azure OpenAI chat extension. + - name: pinecone + value: pinecone + description: Represents the use of Pinecone index as an Azure OpenAI chat extension. + azureSearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureSearchChatExtensionParameters' + description: "A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_search + azureSearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + endpoint: + type: string + description: The absolute endpoint path for the Azure Search resource to use. + format: uri + index_name: + type: string + description: The name of the index to use as available in the referenced Azure Search resource. + fields_mapping: + $ref: '#/components/schemas/azureSearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/azureSearchQueryType' + semantic_configuration: + type: string + description: The additional semantic configuration for the query. + filter: + type: string + description: Search filter. + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + - $ref: '#/components/schemas/onYourDataIntegratedVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters for Azure Search when used as an Azure OpenAI chat extension. + azureSearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + image_vector_fields: + type: array + items: + type: string + description: The names of fields that represent image vector data. + description: Optional settings to control how fields are processed when using a configured Azure Search resource. + azureSearchQueryType: + enum: + - simple + - semantic + - vector + - vector_simple_hybrid + - vector_semantic_hybrid + type: string + description: The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: AzureSearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: semantic + value: semantic + description: Represents the semantic query parser for advanced semantic modeling. + - name: vector + value: vector + description: Represents vector search over computed data. + - name: vectorSimpleHybrid + value: vector_simple_hybrid + description: Represents a combination of the simple query strategy with vector data. + - name: vectorSemanticHybrid + value: vector_semantic_hybrid + description: Represents a combination of semantic search and vector data querying. + azureCosmosDBChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureCosmosDBChatExtensionParameters' + description: "A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_cosmos_db + azureCosmosDBChatExtensionParameters: + required: + - authentication + - container_name + - database_name + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + database_name: + type: string + description: The MongoDB vCore database name to use with Azure Cosmos DB. + container_name: + type: string + description: The name of the Azure Cosmos DB resource container. + index_name: + type: string + description: The MongoDB vCore index name to use with Azure Cosmos DB. + fields_mapping: + $ref: '#/components/schemas/azureCosmosDBFieldMappingOptions' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\nMongoDB vCore." + azureCosmosDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource. + elasticsearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/elasticsearchChatExtensionParameters' + description: "A specific representation of configurable options for Elasticsearch when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: elasticsearch + elasticsearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + endpoint: + type: string + description: The endpoint of Elasticsearch®. + format: uri + index_name: + type: string + description: The index name of Elasticsearch®. + fields_mapping: + $ref: '#/components/schemas/elasticsearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/elasticsearchQueryType' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + - $ref: '#/components/schemas/onYourDataModelIdVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: 'Parameters to use when configuring Elasticsearch® as an Azure OpenAI chat extension. ' + elasticsearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Elasticsearch® resource. + elasticsearchQueryType: + enum: + - simple + - vector + type: string + description: The type of Elasticsearch® retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: ElasticsearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: vector + value: vector + description: Represents vector search over computed data. + mongoDBChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/mongoDBChatExtensionParameters' + description: "A specific representation of configurable options for Mongo DB when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: mongo_db + mongoDBChatExtensionParameters: + required: + - authentication + - app_name + - collection_name + - database_name + - endpoint + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataUsernameAndPasswordAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + endpoint: + type: string + description: The name of the Mongo DB cluster endpoint. + database_name: + type: string + description: The name of the Mongo DB database. + collection_name: + type: string + description: The name of the Mongo DB Collection. + app_name: + type: string + description: The name of the Mongo DB Application. + index_name: + type: string + description: The The name of the Mongo DB index. + fields_mapping: + $ref: '#/components/schemas/mongoDBFieldMappingOptions' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Mongo DB. + mongoDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Mongo DB resource. + pineconeChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/pineconeChatExtensionParameters' + description: "A specific representation of configurable options for Pinecone when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: pinecone + pineconeChatExtensionParameters: + required: + - authentication + - embedding_dependency + - environment + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + environment: + type: string + description: The environment name of Pinecone. + index_name: + type: string + description: The name of the Pinecone database index. + fields_mapping: + $ref: '#/components/schemas/pineconeFieldMappingOptions' + embedding_dependency: + $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters for configuring Azure OpenAI Pinecone chat extensions. + pineconeFieldMappingOptions: + required: + - content_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + description: Optional settings to control how fields are processed when using a configured Pinecone resource. + onYourDataAuthenticationOptions: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataAuthenticationType' + description: The authentication options for Azure OpenAI On Your Data. + discriminator: + propertyName: type + mapping: + api_key: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + connection_string: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + key_and_key_id: '#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions' + encoded_api_key: '#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions' + access_token: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + system_assigned_managed_identity: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + user_assigned_managed_identity: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + username_and_password: '#/components/schemas/onYourDataUsernameAndPasswordAuthenticationOptions' + onYourDataContextProperty: + enum: + - citations + - intent + - all_retrieved_documents + type: string + description: The context property. + x-ms-enum: + name: OnYourDataContextProperty + modelAsString: true + values: + - name: citations + value: citations + description: The `citations` property. + - name: intent + value: intent + description: The `intent` property. + - name: allRetrievedDocuments + value: all_retrieved_documents + description: The `all_retrieved_documents` property. + onYourDataAuthenticationType: + enum: + - api_key + - connection_string + - key_and_key_id + - encoded_api_key + - access_token + - system_assigned_managed_identity + - user_assigned_managed_identity + - username_and_password + type: string + description: The authentication types supported with Azure OpenAI On Your Data. + x-ms-enum: + name: OnYourDataAuthenticationType + modelAsString: true + values: + - name: apiKey + value: api_key + description: Authentication via API key. + - name: connectionString + value: connection_string + description: Authentication via connection string. + - name: keyAndKeyId + value: key_and_key_id + description: Authentication via key and key ID pair. + - name: encodedApiKey + value: encoded_api_key + description: Authentication via encoded API key. + - name: accessToken + value: access_token + description: Authentication via access token. + - name: systemAssignedManagedIdentity + value: system_assigned_managed_identity + description: Authentication via system-assigned managed identity. + - name: userAssignedManagedIdentity + value: user_assigned_managed_identity + description: Authentication via user-assigned managed identity. + - name: usernameAndPassword + value: username_and_password + description: Authentication via username and password. + onYourDataApiKeyAuthenticationOptions: + required: + - key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an API key. + x-ms-discriminator-value: api_key + onYourDataConnectionStringAuthenticationOptions: + required: + - connection_string + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + connection_string: + type: string + description: The connection string to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a connection string. + x-ms-discriminator-value: connection_string + onYourDataKeyAndKeyIdAuthenticationOptions: + required: + - key + - key_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The Elasticsearch key to use for authentication. + key_id: + type: string + description: The Elasticsearch key ID to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an Elasticsearch key and key ID pair. + x-ms-discriminator-value: key_and_key_id + onYourDataEncodedApiKeyAuthenticationOptions: + required: + - encoded_api_key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + encoded_api_key: + type: string + description: The Elasticsearch encoded API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an Elasticsearch encoded API key. + x-ms-discriminator-value: encoded_api_key + onYourDataAccessTokenAuthenticationOptions: + required: + - access_token + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + access_token: + type: string + description: The access token to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using access token. + x-ms-discriminator-value: access_token + onYourDataSystemAssignedManagedIdentityAuthenticationOptions: + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + description: The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity. + x-ms-discriminator-value: system_assigned_managed_identity + onYourDataUserAssignedManagedIdentityAuthenticationOptions: + required: + - managed_identity_resource_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + managed_identity_resource_id: + type: string + description: The resource ID of the user-assigned managed identity to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity. + x-ms-discriminator-value: user_assigned_managed_identity + onYourDataUsernameAndPasswordAuthenticationOptions: + required: + - username + - password + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + username: + type: string + description: The username to use for authentication. + password: + type: string + description: The password. to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a username and a password. + x-ms-discriminator-value: username_and_password + onYourDataVectorizationSource: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataVectorizationSourceType' + description: An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search. + discriminator: + propertyName: type + mapping: + endpoint: '#/components/schemas/onYourDataEndpointVectorizationSource' + deployment_name: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + integrated: '#/components/schemas/onYourDataIntegratedVectorizationSource' + model_id: '#/components/schemas/onYourDataModelIdVectorizationSource' + onYourDataVectorizationSourceType: + enum: + - endpoint + - deployment_name + - integrated + - model_id + type: string + description: "Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\nvector search." + x-ms-enum: + name: OnYourDataVectorizationSourceType + modelAsString: true + values: + - name: endpoint + value: endpoint + description: Represents vectorization performed by public service calls to an Azure OpenAI embedding model. + - name: deploymentName + value: deployment_name + description: "Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\nsearch even in private networks." + - name: integrated + value: integrated + description: Represents the integrated vectorizer defined within the search resource. + - name: modelId + value: model_id + description: "Represents a specific embedding model ID as defined in the search service.\nCurrently only supported by Elasticsearch®." + onYourDataEndpointVectorizationSource: + required: + - authentication + - endpoint + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + endpoint: + type: string + description: Specifies the resource endpoint URL from which embeddings should be retrieved. It should be in the format of https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/embeddings. The api-version query parameter is not allowed. + format: uri + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + dimensions: + type: integer + description: The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models. + format: int32 + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a public Azure OpenAI endpoint call for embeddings." + x-ms-discriminator-value: endpoint + onYourDataDeploymentNameVectorizationSource: + required: + - deployment_name + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + deployment_name: + type: string + description: 'Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.' + dimensions: + type: integer + description: The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models. + format: int32 + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non an internal embeddings model deployment name in the same Azure OpenAI resource." + x-ms-discriminator-value: deployment_name + onYourDataIntegratedVectorizationSource: + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + description: Represents the integrated vectorizer defined within the search resource. + x-ms-discriminator-value: integrated + onYourDataModelIdVectorizationSource: + required: + - model_id + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + model_id: + type: string + description: Specifies the model ID to use for vectorization. This model ID must be defined in the search service. + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a search service model ID. Currently only supported by Elasticsearch®." + x-ms-discriminator-value: model_id + azureChatExtensionsMessageContext: + type: object + properties: + citations: + type: array + items: + $ref: '#/components/schemas/citation' + description: 'The data source retrieval result, used to generate the assistant message in the response.' + x-ms-identifiers: [ ] + intent: + type: string + description: 'The detected intent from the chat history, used to pass to the next turn to carry over the context.' + all_retrieved_documents: + type: array + items: + $ref: '#/components/schemas/retrievedDocument' + description: All the retrieved documents. + description: " A representation of the additional context information available when Azure OpenAI chat extensions are involved\n in the generation of a corresponding chat completions response. This context information is only populated when\n using an Azure OpenAI request configured to use a matching extension." + citation: + required: + - content + type: object + properties: + content: + type: string + description: The content of the citation. + title: + type: string + description: The title of the citation. + url: + type: string + description: The URL of the citation. + filepath: + type: string + description: The file path of the citation. + chunk_id: + type: string + description: The chunk ID of the citation. + rerank_score: + type: number + description: The rerank score of the retrieved document. + format: double + description: citation information for a chat completions response message. + retrievedDocument: + required: + - search_queries + - data_source_index + - original_search_score + type: object + allOf: + - $ref: '#/components/schemas/citation' + - properties: + search_queries: + type: array + items: + type: string + description: The search queries used to retrieve the document. + data_source_index: + type: integer + description: The index of the data source. + format: int32 + original_search_score: + type: number + description: The original search score of the retrieved document. + format: double + filter_reason: + $ref: '#/components/schemas/filterReason' + description: The retrieved document. + filterReason: + enum: + - score + - rerank + type: string + description: The filtering reason of the retrieved document. + x-ms-enum: + name: FilterReason + modelAsString: true + values: + - name: score + value: score + description: The document is filtered by original search score threshold defined by `strictness` configure. + - name: rerank + value: rerank + description: 'The document is not filtered by original search score threshold, but is filtered by rerank score and `top_n_documents` configure.' + chatCompletionMessageToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + $ref: '#/components/schemas/toolCallType' + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: The function that the model called. + toolCallType: + enum: + - function + type: string + description: 'The type of the tool call, in this case `function`.' + x-ms-enum: + name: ToolCallType + modelAsString: true + values: + - value: function + description: The tool call type is function. + chatCompletionRequestMessageTool: + required: + - tool_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + tool_call_id: + type: string + description: Tool call that this message is responding to. + content: + type: string + description: The contents of the message. + nullable: true + nullable: true + chatCompletionRequestMessageFunction: + required: + - function_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + name: + type: string + description: The contents of the message. + content: + type: string + description: The contents of the message. + nullable: true + description: Deprecated. Message that represents a function. + nullable: true + createChatCompletionResponse: + required: + - choices + - created + - id + - model + - object + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + required: + - finish_reason + - index + - message + - logprobs + type: object + properties: + finish_reason: + enum: + - stop + - length + - tool_calls + - content_filter + - function_call + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n" + index: + type: integer + description: The index of the choice in the list of choices. + message: + $ref: '#/components/schemas/chatCompletionResponseMessage' + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + logprobs: + required: + - content + - refusal + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + refusal: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message refusal tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + description: A list of chat completion choices. Can be more than one if `n` is greater than 1. + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + model: + type: string + description: The model used for the chat completion. + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + enum: + - chat.completion + type: string + description: 'The object type, which is always `chat.completion`.' + usage: + $ref: '#/components/schemas/completionUsage' + description: 'Represents a chat completion response returned by model, based on the provided input.' + createChatCompletionStreamResponse: + required: + - choices + - created + - id + - model + - object + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. Each chunk has the same ID. + choices: + type: array + items: + required: + - delta + - finish_reason + - index + type: object + properties: + delta: + $ref: '#/components/schemas/chatCompletionStreamResponseDelta' + logprobs: + required: + - content + - refusal + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + refusal: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message refusal tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + finish_reason: + enum: + - stop + - length + - tool_calls + - content_filter + - function_call + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n" + nullable: true + index: + type: integer + description: The index of the choice in the list of choices. + description: "A list of chat completion choices. Can contain more than one elements if `n` is greater than 1.\n" + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp. + model: + type: string + description: The model to generate the completion. + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + required: + - prompt_tokens + - completion_tokens + - total_tokens + enum: + - chat.completion.chunk + type: string + description: 'The object type, which is always `chat.completion.chunk`.' + description: 'Represents a streamed chunk of a chat completion response returned by model, based on the provided input.' + chatCompletionStreamResponseDelta: + type: object + properties: + content: + type: string + description: The contents of the chunk message. + nullable: true + function_call: + type: object + properties: + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + name: + type: string + description: The name of the function to call. + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + deprecated: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCallChunk' + role: + enum: + - system + - user + - assistant + - tool + type: string + description: The role of the author of this message. + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + description: A chat completion delta generated by streamed model responses. + chatCompletionMessageToolCallChunk: + required: + - index + type: object + properties: + index: + type: integer + id: + type: string + description: The ID of the tool call. + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + chatCompletionStreamOptions: + type: object + properties: + include_usage: + type: boolean + description: "If set, an additional chunk will be streamed before the `data: [DONE]` message. The `usage` field on this chunk shows the token usage statistics for the entire request, and the `choices` field will always be an empty array. All other chunks will also include a `usage` field, but with a null value.\n" + description: "Options for streaming response. Only set this when you set `stream: true`.\n" + default: + nullable: true + chatCompletionChoiceLogProbs: + required: + - content + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + refusal: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message refusal tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + chatCompletionTokenLogprob: + required: + - token + - logprob + - bytes + - top_logprobs + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + top_logprobs: + type: array + items: + required: + - token + - logprob + - bytes + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + description: 'List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned.' + chatCompletionResponseMessage: + required: + - role + - content + - refusal + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionResponseMessageRole' + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + function_call: + $ref: '#/components/schemas/chatCompletionFunctionCall' + audio: + required: + - id + - expires_at + - data + - transcript + type: object + properties: + id: + type: string + description: Unique identifier for this audio response. + expires_at: + type: integer + description: "The Unix timestamp (in seconds) for when this audio response will\nno longer be accessible on the server for use in multi-turn\nconversations.\n" + data: + type: string + description: "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request.\n" + transcript: + type: string + description: Transcript of the audio generated by the model. + description: "If the audio output modality is requested, this object contains data\nabout the audio response from the model. [Learn more](/docs/guides/audio).\n" + x-oaiExpandable: true + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + description: A chat completion message generated by the model. + chatCompletionResponseMessageRole: + enum: + - assistant + type: string + description: The role of the author of the response message. + chatCompletionToolChoiceOption: + oneOf: + - enum: + - none + - auto + - required + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - $ref: '#/components/schemas/chatCompletionNamedToolChoice' + description: 'Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools. Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. `none` is the default when no tools are present. `auto` is the default if tools are present.' + chatCompletionNamedToolChoice: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific function. + ParallelToolCalls: + type: boolean + description: Whether to enable parallel function calling during tool use. + default: true + PredictionContent: + title: Static Content + required: + - content + - type + type: object + properties: + type: + enum: + - content + type: string + description: The type of the predicted content you want to provide. This type is currently always `content`. + content: + oneOf: + - title: Text Content + type: string + description: The content used for a Predicted Output. This is often the text of a file you are regenerating with minor changes. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPartText' + description: 'An array of content parts with a defined type. Supported options differ based on the [model](/docs/models) being used to generate the response. Can contain text inputs.' + description: 'The content that should be matched when generating a model response. If generated tokens would match this content, the entire model response can be returned much more quickly.' + x-oaiExpandable: true + description: 'Static predicted output content, such as the content of a text file that is being regenerated.' + chatCompletionMessageToolCalls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + ChatCompletionModalities: + type: array + items: + enum: + - text + - audio + type: string + description: "Output types that you would like the model to generate for this request.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to [generate audio](/docs/guides/audio). To\nrequest that this model generate both text and audio responses, you can\nuse:\n\n`[\"text\", \"audio\"]`\n" + chatCompletionFunctionCall: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + completionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + prompt_tokens_details: + type: object + properties: + audio_tokens: + type: integer + description: Audio input tokens present in the prompt. + cached_tokens: + type: integer + description: The number of cached prompt tokens. + description: Details of the prompt tokens. + completion_tokens_details: + type: object + properties: + accepted_prediction_tokens: + type: integer + description: 'When using Predicted Outputs, the number of tokens in the prediction that appeared in the completion.' + audio_tokens: + type: integer + description: Audio input tokens generated by the model. + reasoning_tokens: + type: integer + description: Tokens generated by the model for reasoning. + rejected_prediction_tokens: + type: integer + description: 'When using Predicted Outputs, the number of tokens in the prediction that did not appear in the completion. However, like reasoning tokens, these tokens are still counted in the total completion tokens for purposes of billing, output, and context window limits.' + description: Breakdown of tokens used in a completion. + description: Usage statistics for the completion request. + chatCompletionTool: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + $ref: '#/components/schemas/FunctionObject' + FunctionParameters: + type: object + description: "The parameters the functions accepts, described as a JSON Schema object. See the guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. \n\nOmitting `parameters` defines a function with an empty parameter list." + FunctionObject: + required: + - name + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/FunctionParameters' + strict: + type: boolean + description: 'Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](docs/guides/function-calling).' + default: false + nullable: true + ResponseFormatText: + required: + - type + type: object + properties: + type: + enum: + - text + type: string + description: 'The type of response format being defined: `text`' + ResponseFormatJsonObject: + required: + - type + type: object + properties: + type: + enum: + - json_object + type: string + description: 'The type of response format being defined: `json_object`' + ResponseFormatJsonSchemaSchema: + type: object + description: 'The schema for the response format, described as a JSON Schema object.' + ResponseFormatJsonSchema: + required: + - type + - json_schema + type: object + properties: + type: + enum: + - json_schema + type: string + description: 'The type of response format being defined: `json_schema`' + json_schema: + required: + - schema + - name + type: object + properties: + description: + type: string + description: 'A description of what the response format is for, used by the model to determine how to respond in the format.' + name: + type: string + description: 'The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + schema: + $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema' + strict: + type: boolean + description: 'Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`.' + default: false + nullable: true + chatCompletionChoiceCommon: + type: object + properties: + index: + type: integer + finish_reason: + type: string + createTranslationRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + description: Translation request. + audioResponse: + required: + - text + type: object + properties: + text: + type: string + description: Translated or transcribed text. + description: Translation or transcription response when response_format was json + audioVerboseResponse: + required: + - text + type: object + allOf: + - $ref: '#/components/schemas/audioResponse' + - properties: + task: + enum: + - transcribe + - translate + type: string + description: Type of audio task. + x-ms-enum: + modelAsString: true + language: + type: string + description: Language. + duration: + type: number + description: Duration. + segments: + type: array + items: + $ref: '#/components/schemas/audioSegment' + words: + type: array + items: + $ref: '#/components/schemas/audioWord' + description: Translation or transcription response when response_format was verbose_json + audioResponseFormat: + title: AudioResponseFormat + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: Defines the format of the output. + x-ms-enum: + modelAsString: true + createTranscriptionRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + 'timestamp_granularities[]': + type: array + items: + enum: + - word + - segment + type: string + description: 'The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.' + default: + - segment + description: Transcription request. + audioSegment: + type: object + properties: + id: + type: integer + description: Segment identifier. + seek: + type: number + description: Offset of the segment. + start: + type: number + description: Segment start offset. + end: + type: number + description: Segment end offset. + text: + type: string + description: Segment text. + tokens: + type: array + items: + type: number + description: Tokens of the text. + temperature: + type: number + description: Temperature. + avg_logprob: + type: number + description: Average log probability. + compression_ratio: + type: number + description: Compression ratio. + no_speech_prob: + type: number + description: Probability of 'no speech'. + description: Transcription or translation segment. + audioWord: + type: object + properties: + word: + type: string + description: Word + start: + type: number + description: Word start offset. + end: + type: number + description: Word end offset. + description: Transcription or translation word. + createSpeechRequest: + required: + - input + - voice + type: object + properties: + input: + maxLength: 4096 + type: string + description: The text to synthesize audio for. The maximum length is 4096 characters. + voice: + enum: + - alloy + - echo + - fable + - onyx + - nova + - shimmer + type: string + description: The voice to use for speech synthesis. + response_format: + enum: + - mp3 + - opus + - aac + - flac + - wav + - pcm + type: string + description: The format to synthesize the audio in. + speed: + maximum: 4.0 + minimum: 0.25 + type: number + description: The speed of the synthesize audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + default: 1 + description: Speech request. + imageQuality: + enum: + - auto + - high + - medium + - low + - hd + - standard + type: string + description: The quality of the image that will be generated. + default: auto + x-ms-enum: + name: ImageQuality + modelAsString: true + values: + - value: auto + description: Automatically selects the best quality for the given model. + - value: high + description: High quality. Supported for gpt-image-1. + - value: medium + description: Medium quality. Supported for gpt-image-1. + - value: low + description: Low quality. Supported for gpt-image-1. + - value: hd + description: HD quality. Supported for dall-e-3. + - value: standard + description: Standard quality. Supported for dall-e-3. + imagesResponseFormat: + enum: + - url + - b64_json + type: string + description: The format in which the generated images are returned. + default: url + x-ms-enum: + name: ImagesResponseFormat + modelAsString: true + values: + - value: url + description: The URL that provides temporary access to download the generated images. Only supported for dall-e-3. + name: Url + - value: b64_json + description: The generated images are returned as base64 encoded string. + name: Base64Json + imagesOutputFormat: + enum: + - png + - jpeg + type: string + description: The file format in which the generated images are returned. Only supported for gpt-image-1. + default: png + x-ms-enum: + name: imagesOutputFormat + modelAsString: true + values: + - value: png + description: The generated image is in png format. + name: png + - value: jpeg + description: The generated image is in jpeg format. + name: jpeg + imageSize: + enum: + - auto + - 1792x1024 + - 1024x1792 + - 1024x1024 + - 1024x1536 + - 1536x1024 + type: string + description: The size of the generated images. + default: auto + x-ms-enum: + name: Size + modelAsString: true + values: + - value: auto + description: The size of the generated image is automatically selected based on the model. Only supported for gpt-image-1. + name: Auto + - value: 1792x1024 + description: The desired size of the generated image is 1792x1024 pixels. Only supported for dall-e-3. + name: Size1792x1024 + - value: 1024x1792 + description: The desired size of the generated image is 1024x1792 pixels. Only supported for dall-e-3. + name: Size1024x1792 + - value: 1024x1024 + description: The desired size of the generated image is 1024x1024 pixels. + name: Size1024x1024 + - value: 1024x1536 + description: The desired size of the generated image is 1024x1536 pixels. Only supported for gpt-image-1. + name: Size1024x1536 + - value: 1536x1024 + description: The desired size of the generated image is 1536x1024 pixels Only supported for gpt-image-1. + name: Size1536x1024 + imageStyle: + enum: + - vivid + - natural + type: string + description: The style of the generated images. Only supported for dall-e-3. + default: vivid + x-ms-enum: + name: Style + modelAsString: true + values: + - value: vivid + description: Vivid creates images that are hyper-realistic and dramatic. + name: Vivid + - value: natural + description: Natural creates images that are more natural and less hyper-realistic. + name: Natural + imageBackground: + enum: + - transparent + - opaque + - auto + type: string + description: Allows to set transparency for the background of the generated image(s). This parameter is only supported for gpt-image-1. + default: auto + x-ms-enum: + name: Background + modelAsString: true + values: + - value: transparent + description: 'The background of the generated image will be transparent. The output format needs to support transparency (e.g., png).' + name: Transparent + - value: opaque + description: The background of the generated image will be opaque. + name: Opaque + - value: auto + description: The background of the generated image will be opaque. + name: Auto + imageGenerationsRequest: + required: + - prompt + type: object + properties: + prompt: + minLength: 1 + type: string + description: A text description of the desired image(s). The maximum length is 32000 characters for gpt-image-1 and 4000 characters for dall-e-3 + format: string + example: a corgi in a field + background: + $ref: '#/components/schemas/imageBackground' + n: + maximum: 10.0 + minimum: 1.0 + type: integer + description: 'The number of images to generate. For dall-e-3, only n=1 is supported.' + default: 1 + output_compression: + maximum: 100.0 + minimum: 0.0 + type: integer + description: The compression level (0-100%) for the generated images. This parameter is only supported for gpt-image-1 with the jpeg output format. + default: 100 + output_format: + $ref: '#/components/schemas/imagesOutputFormat' + quality: + $ref: '#/components/schemas/imageQuality' + response_format: + $ref: '#/components/schemas/imagesResponseFormat' + size: + $ref: '#/components/schemas/imageSize' + style: + $ref: '#/components/schemas/imageStyle' + user: + type: string + description: 'A unique identifier representing your end-user, which can help to monitor and detect abuse.' + format: string + example: user123456 + imageEditsRequest: + required: + - image + - prompt + type: object + properties: + image: + oneOf: + - type: string + format: binary + - type: array + items: + type: string + format: binary + description: 'The image(s) to edit. Must be a supported image file or an array of images. Each image should be a png, or jpg file less than 25MB.' + prompt: + minLength: 1 + type: string + description: A text description of the desired image(s). The maximum length is 32000 characters. + format: string + example: a corgi in a field + mask: + type: string + description: 'An additional image whose fully transparent areas (e.g., where alpha is zero) indicate where the image should be edited. If there are multiple images provided, the mask will be applied to the first image. Must be a valid PNG file, less than 4MB, and have the same dimensions as the image.' + format: binary + n: + maximum: 10.0 + minimum: 1.0 + type: integer + description: The number of images to generate. + default: 1 + quality: + $ref: '#/components/schemas/imageQuality' + response_format: + $ref: '#/components/schemas/imagesResponseFormat' + size: + $ref: '#/components/schemas/imageSize' + user: + type: string + description: 'A unique identifier representing your end-user, which can help to monitor and detect abuse.' + format: string + example: user123456 + generateImagesResponse: + required: + - created + - data + type: object + properties: + created: + type: integer + description: The unix timestamp when the operation was created. + format: unixtime + example: '1676540381' + data: + type: array + items: + $ref: '#/components/schemas/imageResult' + description: 'The result data of the operation, if successful' + usage: + $ref: '#/components/schemas/imageGenerationsUsage' + imageResult: + type: object + properties: + url: + type: string + description: The image url. + example: https://www.contoso.com + b64_json: + type: string + description: The base64 encoded image + content_filter_results: + $ref: '#/components/schemas/dalleContentFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + prompt_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + description: 'The image url or encoded image if successful, and an error otherwise.' + imageGenerationsUsage: + type: object + properties: + total_tokens: + type: integer + description: The total number of tokens used. + example: 100 + input_tokens: + type: integer + description: The number of input tokens. + example: 50 + output_tokens: + type: integer + description: The number of output tokens. + example: 50 + input_tokens_details: + type: object + properties: + text_tokens: + type: integer + description: The number of text tokens. + example: 10 + image_tokens: + type: integer + description: The number of image tokens. + example: 40 + description: A detailed breakdown of the input tokens. + description: Represents token usage details for image generation requests. Only for gpt-image-1. + line: + required: + - text + - spans + type: object + properties: + text: + type: string + spans: + type: array + items: + $ref: '#/components/schemas/span' + description: An array of spans that represent detected objects and its bounding box information. + description: 'A content line object consisting of an adjacent sequence of content elements, such as words and selection marks.' + span: + required: + - text + - offset + - length + - polygon + type: object + properties: + text: + type: string + description: The text content of the span that represents the detected object. + offset: + type: integer + description: 'The character offset within the text where the span begins. This offset is defined as the position of the first character of the span, counting from the start of the text as Unicode codepoints.' + length: + type: integer + description: 'The length of the span in characters, measured in Unicode codepoints.' + polygon: + type: array + items: + type: object + properties: + x: + type: number + description: The x-coordinate of the point. + y: + type: number + description: The y-coordinate of the point. + description: An array of objects representing points in the polygon that encloses the detected object. + description: A span object that represents a detected object and its bounding box information. + runCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: 'Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.).' + nullable: true + runStepCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run step. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run step. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`. + nullable: true + assistantsApiResponseFormatOption: + oneOf: + - enum: + - auto + type: string + description: "`auto` is the default value\n" + x-ms-enum: + name: AssistantsApiResponseFormatOption + modelAsString: true + values: + - value: auto + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + - $ref: '#/components/schemas/ResponseFormatJsonSchema' + description: "Specifies the format that the model must output. Compatible with GPT-4o, GPT-4 Turbo, and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the Structured Outputs guide.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length." + assistantsApiResponseFormat: + type: object + properties: + type: + enum: + - text + - json_object + type: string + description: Must be one of `text` or `json_object`. + default: text + example: json_object + x-ms-enum: + name: AssistantsApiResponseFormat + modelAsString: true + values: + - value: text + - value: json_object + description: "An object describing the expected output of the model. If `json_object` only `function` type `tools` are allowed to be passed to the Run. If `text` the model can return text or any value needed.\n" + assistantObject: + title: Assistant + required: + - id + - object + - created_at + - name + - description + - model + - instructions + - tools + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - assistant + type: string + description: 'The object type, which is always `assistant`.' + x-ms-enum: + name: AssistantObjectType + modelAsString: true + values: + - value: assistant + description: 'The object type, which is always assistant' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the assistant was created. + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + model: + type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + description: Represents an `assistant` that can call the model and use tools. + createAssistantRequest: + required: + - model + type: object + properties: + model: + anyOf: + - type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + chunking_strategy: + type: object + oneOf: + - title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + x-ms-enum: + name: AutoChunkingStrategyType + modelAsString: true + values: + - value: auto + additionalProperties: false + description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + - title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-ms-enum: + name: StaticChunkingStrategyType + modelAsString: true + values: + - value: static + static: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`. + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + additionalProperties: false + description: 'The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + x-oaiTypeLabel: map + description: "A helper to create a vector store with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + modifyAssistantRequest: + type: object + properties: + model: + anyOf: + - type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 32768 + type: string + description: "The system instructions that the assistant uses. The maximum length is 32768 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "Overrides the list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "Overrides the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + deleteAssistantResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - assistant.deleted + type: string + x-ms-enum: + name: DeleteAssistantResponseState + modelAsString: true + values: + - value: assistant.deleted + listAssistantsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/assistantObject' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + example: false + assistantToolsCode: + title: Code interpreter tool + required: + - type + type: object + properties: + type: + enum: + - code_interpreter + type: string + description: 'The type of tool being defined: `code_interpreter`' + x-ms-enum: + name: assistantToolsCodeType + modelAsString: true + values: + - value: code_interpreter + assistantToolsFileSearch: + title: FileSearch tool + required: + - type + type: object + properties: + type: + enum: + - file_search + type: string + description: 'The type of tool being defined: `file_search`' + x-ms-enum: + name: assistantToolsFileSearchType + modelAsString: true + values: + - value: file_search + file_search: + type: object + properties: + max_num_results: + maximum: 50.0 + minimum: 1.0 + type: integer + description: "The maximum number of results the file search tool should output. The default is 20 for gpt-4* models and 5 for gpt-3.5-turbo. This number should be between 1 and 50 inclusive.\n\nNote that the file search tool may output fewer than `max_num_results` results. \n" + description: Overrides for the file search tool. + assistantToolsFileSearchTypeOnly: + title: FileSearch tool + required: + - type + type: object + properties: + type: + enum: + - file_search + type: string + description: 'The type of tool being defined: `file_search`' + x-ms-enum: + name: assistantToolsFileSearchType + modelAsString: true + values: + - value: file_search + assistantToolsFunction: + title: Function tool + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of tool being defined: `function`' + x-ms-enum: + name: assistantToolsFunction + modelAsString: true + values: + - value: function + function: + required: + - name + - parameters + - description + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + description: The function definition. + truncationObject: + title: Thread Truncation Controls + required: + - type + type: object + properties: + type: + enum: + - auto + - last_messages + type: string + description: 'The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`.' + x-ms-enum: + name: TruncationType + modelAsString: true + values: + - value: auto + - value: last_messages + last_messages: + minimum: 1.0 + type: integer + description: The number of most recent messages from the thread when constructing the context for the run. + nullable: true + description: Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run. + assistantsApiToolChoiceOption: + oneOf: + - enum: + - none + - auto + type: string + description: "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n" + x-ms-enum: + name: AssistantsApiToolChoiceOption + modelAsString: true + values: + - value: none + - value: auto + - $ref: '#/components/schemas/assistantsNamedToolChoice' + description: "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tools and instead generates a message.\n`auto` is the default value and means the model can pick between generating a message or calling a tool.\nSpecifying a particular tool like `{\"type\": \"file_search\"}` or `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n" + assistantsNamedToolChoice: + required: + - type + type: object + properties: + type: + enum: + - function + - code_interpreter + - file_search + type: string + description: 'The type of the tool. If type is `function`, the function name must be set' + x-ms-enum: + name: AssistantsNamedToolChoiceType + modelAsString: true + values: + - value: function + - value: code_interpreter + - value: file_search + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific tool. + runObject: + title: A run on a thread + required: + - id + - object + - created_at + - thread_id + - assistant_id + - status + - required_action + - last_error + - expires_at + - started_at + - cancelled_at + - failed_at + - completed_at + - model + - instructions + - tools + - metadata + - usage + - incomplete_details + - max_prompt_tokens + - max_completion_tokens + - truncation_strategy + - tool_choice + - response_format + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.run + type: string + description: 'The object type, which is always `thread.run`.' + x-ms-enum: + name: runObjectType + modelAsString: true + values: + - value: thread.run + description: The run object type which is always thread.run + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was created. + thread_id: + type: string + description: 'The ID of the [thread](/docs/api-reference/threads) that was executed on as a part of this run.' + assistant_id: + type: string + description: The ID of the assistant used for execution of this run. + status: + enum: + - queued + - in_progress + - requires_action + - cancelling + - cancelled + - failed + - completed + - expired + type: string + description: 'The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or `expired`.' + x-ms-enum: + name: RunObjectStatus + modelAsString: true + values: + - value: queued + description: The queued state + - value: in_progress + description: The in_progress state + - value: requires_action + description: The required_action state + - value: cancelling + description: The cancelling state + - value: cancelled + description: The cancelled state + - value: failed + description: The failed state + - value: completed + description: The completed state + - value: expired + description: The expired state + required_action: + required: + - type + - submit_tool_outputs + type: object + properties: + type: + enum: + - submit_tool_outputs + type: string + description: 'For now, this is always `submit_tool_outputs`.' + submit_tool_outputs: + required: + - tool_calls + type: object + properties: + tool_calls: + type: array + items: + $ref: '#/components/schemas/runToolCallObject' + description: A list of the relevant tool calls. + description: Details on the tool outputs needed for this run to continue. + description: Details on the action required to continue the run. Will be `null` if no action is required. + nullable: true + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + type: string + description: One of `server_error` or `rate_limit_exceeded`. + x-ms-enum: + name: LastErrorCode + modelAsString: true + values: + - value: server_error + description: The server failed to respond to request due to server error + - value: rate_limit_exceeded + description: The server failed to respond to request due to rate limit exceeded + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this run. Will be `null` if there are no errors. + nullable: true + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the run will expire. + started_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was started. + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was completed. + nullable: true + incomplete_details: + type: object + properties: + reason: + enum: + - max_completion_tokens + - max_prompt_tokens + type: string + description: The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run. + x-ms-enum: + name: IncompleteDetailsReason + modelAsString: true + values: + - value: max_completion_tokens + - value: max_prompt_tokens + description: Details on why the run is incomplete. Will be `null` if the run is not incomplete. + nullable: true + model: + type: string + description: The model that the assistant used for this run. + instructions: + type: string + description: The instructions that the assistant used for this run. + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: The list of tools that the assistant used for this run. + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + usage: + $ref: '#/components/schemas/runCompletionUsage' + temperature: + type: number + description: 'The sampling temperature used for this run. If not set, defaults to 1.' + nullable: true + top_p: + type: number + description: 'The nucleus sampling value used for this run. If not set, defaults to 1.' + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens specified to have been used over the course of the run.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens specified to have been used over the course of the run.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + description: 'Represents an execution run on a [thread](/docs/api-reference/threads).' + createRunRequest: + required: + - thread_id + - assistant_id + type: object + properties: + assistant_id: + type: string + description: The ID of the assistant to use to execute this run. + model: + type: string + description: 'The ID of the Model to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.' + nullable: true + instructions: + type: string + description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + nullable: true + additional_instructions: + type: string + description: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. + nullable: true + additional_messages: + type: array + items: + $ref: '#/components/schemas/createMessageRequest' + description: Adds additional messages to the thread before creating the run. + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + listRunsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/runObject' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + example: false + modifyRunRequest: + type: object + properties: + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + submitToolOutputsRunRequest: + required: + - tool_outputs + type: object + properties: + tool_outputs: + type: array + items: + type: object + properties: + tool_call_id: + type: string + description: The ID of the tool call in the `required_action` object within the run object the output is being submitted for. + output: + type: string + description: The output of the tool call to be submitted to continue the run. + description: A list of tools for which the outputs are being submitted. + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + additionalProperties: false + runToolCallObject: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: 'The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](/docs/api-reference/runs/submitToolOutputs) endpoint.' + type: + enum: + - function + type: string + description: 'The type of tool call the output is required for. For now, this is always `function`.' + x-ms-enum: + name: RunToolCallObjectType + modelAsString: true + values: + - value: function + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments that the model expects you to pass to the function. + description: The function definition. + description: Tool call objects + createThreadAndRunRequest: + required: + - thread_id + - assistant_id + type: object + properties: + assistant_id: + type: string + description: The ID of the assistant to use to execute this run. + thread: + $ref: '#/components/schemas/createThreadRequest' + model: + type: string + description: 'The ID of the [Model](/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.' + nullable: true + instructions: + type: string + description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + nullable: true + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + stream_options: + $ref: '#/components/schemas/chatCompletionStreamOptions' + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + threadObject: + title: Thread + required: + - id + - object + - created_at + - tool_resources + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread + type: string + description: 'The object type, which is always `thread`.' + x-ms-enum: + name: ThreadObjectType + modelAsString: true + values: + - value: thread + description: The type of thread object which is always `thread` + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the thread was created. + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: 'Represents a thread that contains [messages](/docs/api-reference/messages).' + createThreadRequest: + type: object + properties: + messages: + type: array + items: + $ref: '#/components/schemas/createMessageRequest' + description: 'A list of [messages](/docs/api-reference/messages) to start the thread with.' + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + chunking_strategy: + type: object + oneOf: + - title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + x-ms-enum: + name: AutoChunkingStrategyType + modelAsString: true + values: + - value: auto + additionalProperties: false + description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + - title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-ms-enum: + name: StaticChunkingStrategyType + modelAsString: true + values: + - value: static + static: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`. + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + additionalProperties: false + description: 'The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + description: "A helper to create a vector store with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + modifyThreadRequest: + type: object + properties: + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of File IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + deleteThreadResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.deleted + type: string + x-ms-enum: + name: DeleteThreadResponseObjectState + modelAsString: true + values: + - value: thread.deleted + description: The delete thread response object state which is `thread.deleted` + listThreadsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/threadObject' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + example: false + messageObject: + title: The message object + required: + - id + - object + - created_at + - thread_id + - status + - incomplete_details + - completed_at + - incomplete_at + - role + - content + - assistant_id + - run_id + - attachments + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.message + type: string + description: 'The object type, which is always `thread.message`.' + x-ms-enum: + name: MessageObjectType + modelAsString: true + values: + - value: thread.message + description: The message object type which is `thread.message` + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was created. + thread_id: + type: string + description: 'The [thread](/docs/api-reference/threads) ID that this message belongs to.' + status: + enum: + - in_progress + - incomplete + - completed + type: string + description: 'The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.' + x-ms-enum: + name: MessageObjectStatus + modelAsString: true + values: + - value: in_progress + - value: incomplete + - value: completed + incomplete_details: + required: + - reason + type: object + properties: + reason: + enum: + - content_filter + - max_tokens + - run_cancelled + - run_expired + - run_failed + type: string + description: The reason the message is incomplete. + x-ms-enum: + name: MessageObjectIncompleteReason + modelAsString: true + values: + - value: content_filter + - value: max_tokens + - value: run_cancelled + - value: run_expired + - value: run_failed + description: 'On an incomplete message, details about why the message is incomplete.' + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was completed. + nullable: true + incomplete_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was marked as incomplete. + nullable: true + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + x-ms-enum: + name: MessageObjectRole + modelAsString: true + values: + - value: user + - value: assistant + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageContentImageFileObject' + - $ref: '#/components/schemas/messageContentTextObject' + description: The content of the message in array of text and/or images. + assistant_id: + type: string + description: 'If applicable, the ID of the assistant that authored this message.' + nullable: true + run_id: + type: string + description: 'If applicable, the ID of the [run](/docs/api-reference/runs) associated with the authoring of this message.' + nullable: true + attachments: + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearchTypeOnly' + description: The tools to add this file to. + description: 'A list of files attached to the message, and the tools they were added to.' + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: 'Represents a message within a [thread](/docs/api-reference/threads).' + messageDeltaObject: + title: Message delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: 'The identifier of the message, which can be referenced in API endpoints.' + object: + enum: + - thread.message.delta + type: string + description: 'The object type, which is always `thread.message.delta`.' + x-ms-enum: + name: MessageDeltaObjectType + modelAsString: true + values: + - value: thread.message.delta + delta: + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + x-ms-enum: + name: MessageDeltaObjectDeltaRole + modelAsString: true + values: + - value: user + - value: assistant + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageDeltaContentImageFileObject' + - $ref: '#/components/schemas/messageDeltaContentTextObject' + description: The content of the message in array of text and/or images. + description: The delta containing the fields that have changed on the Message. + description: "Represents a message delta i.e. any changed fields on a message during streaming.\n" + createMessageRequest: + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: "The role of the entity that is creating the message. Allowed values include:\n- `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.\n- `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.\n" + x-ms-enum: + name: CreateMessageRequestRole + modelAsString: true + values: + - value: user + - value: assistant + content: + maxLength: 256000 + minLength: 1 + type: string + description: The content of the message. + attachments: + required: + - file_id + - tools + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearchTypeOnly' + description: The tools to add this file to. + description: 'A list of files attached to the message, and the tools they should be added to.' + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + modifyMessageRequest: + type: object + properties: + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + deleteMessageResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.message.deleted + type: string + x-ms-enum: + name: DeleteMessageResponseObject + modelAsString: true + values: + - value: thread.message.deleted + description: The delete message response object state + listMessagesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/messageObject' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + example: false + messageContentImageFileObject: + title: Image file + required: + - type + - image_file + type: object + properties: + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-ms-enum: + name: MessageContentImageFileObjectType + modelAsString: true + values: + - value: image_file + description: The message content image file type + image_file: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The File ID of the image in the message content. + description: References an image File in the content of a message. + messageContentTextObject: + title: Text + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: Always `text`. + x-ms-enum: + name: messageContentTextObjectType + modelAsString: true + values: + - value: text + description: The message content text Object type + text: + required: + - value + - annotations + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/messageContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + messageContentTextAnnotationsFileCitationObject: + title: File citation + required: + - type + - text + - file_citation + - start_index + - end_index + type: object + properties: + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-ms-enum: + name: FileCitationObjectType + modelAsString: true + values: + - value: file_citation + description: The file citation object type + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "retrieval" tool to search files. + messageContentTextAnnotationsFilePathObject: + title: File path + required: + - type + - text + - file_path + - start_index + - end_index + type: object + properties: + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-ms-enum: + name: FilePathObjectType + modelAsString: true + values: + - value: file_path + description: The file path object type + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + messageDeltaContentImageFileObject: + title: Image file + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-ms-enum: + name: MessageDeltaContentImageFileObjectType + modelAsString: true + values: + - value: image_file + image_file: + type: object + properties: + file_id: + type: string + description: The File ID of the image in the message content. + description: References an image File in the content of a message. + messageDeltaContentTextObject: + title: Text + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - text + type: string + description: Always `text`. + x-ms-enum: + name: MessageDeltaContentTextObjectType + modelAsString: true + values: + - value: text + text: + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageDeltaContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/messageDeltaContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + messageDeltaContentTextAnnotationsFileCitationObject: + title: File citation + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-ms-enum: + name: MessageDeltaContentTextAnnotationsFileCitationObjectType + modelAsString: true + values: + - value: file_citation + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + quote: + type: string + description: The specific quote in the file. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + messageDeltaContentTextAnnotationsFilePathObject: + title: File path + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-ms-enum: + name: MessageDeltaContentTextAnnotationsFilePathObjectType + modelAsString: true + values: + - value: file_path + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + runStepObject: + title: Run steps + required: + - id + - object + - created_at + - assistant_id + - thread_id + - run_id + - type + - status + - step_details + - last_error + - expired_at + - cancelled_at + - failed_at + - completed_at + - metadata + type: object + properties: + id: + type: string + description: 'The identifier of the run step, which can be referenced in API endpoints.' + object: + enum: + - assistant.run.step + type: string + description: 'The object type, which is always `assistant.run.step``.' + x-ms-enum: + name: RunStepObjectType + modelAsString: true + values: + - value: assistant.run.step + description: 'The object type, which is always `assistant.run.step`' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was created. + assistant_id: + type: string + description: 'The ID of the [assistant](/docs/api-reference/assistants) associated with the run step.' + thread_id: + type: string + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + run_id: + type: string + description: 'The ID of the [run](/docs/api-reference/runs) that this run step is a part of.' + type: + enum: + - message_creation + - tool_calls + type: string + description: 'The type of run step, which can be either `message_creation` or `tool_calls`.' + x-ms-enum: + name: RunStepObjectType + modelAsString: true + values: + - value: message_creation + description: The message_creation run step + - value: tool_calls + description: The tool_calls run step + status: + enum: + - in_progress + - cancelled + - failed + - completed + - expired + type: string + description: 'The status of the run, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`.' + x-ms-enum: + name: RunStepObjectStatus + modelAsString: true + values: + - value: in_progress + description: The in_progress run status + - value: cancelled + description: The cancelled run status + - value: failed + description: The cancelled run status + - value: completed + description: The cancelled run status + - value: expired + description: The cancelled run status + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsObject' + description: The details of the run step. + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + type: string + description: One of `server_error` or `rate_limit_exceeded`. + x-ms-enum: + name: LastErrorCode + modelAsString: true + values: + - value: server_error + description: The server_error + - value: rate_limit_exceeded + description: The rate_limit_exceeded status + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this run step. Will be `null` if there are no errors. + nullable: true + expired_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired. + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step completed. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: "Represents a step in execution of a run.\n" + runStepDeltaObject: + title: Run step delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: 'The identifier of the run step, which can be referenced in API endpoints.' + object: + enum: + - thread.run.step.delta + type: string + description: 'The object type, which is always `thread.run.step.delta`.' + x-ms-enum: + name: RunStepDeltaObjectType + modelAsString: true + values: + - value: thread.run.step.delta + delta: + type: object + properties: + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsObject' + description: The details of the run step. + description: The delta containing the fields that have changed on the run step. + description: "Represents a run step delta i.e. any changed fields on a run step during streaming.\n" + listRunStepsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/runStepObject' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + example: false + runStepDetailsMessageCreationObject: + title: Message creation + required: + - type + - message_creation + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation``. + x-ms-enum: + name: RunStepDetailsMessageCreationObjectType + modelAsString: true + values: + - value: message_creation + message_creation: + required: + - message_id + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + runStepDeltaStepDetailsMessageCreationObject: + title: Message creation + required: + - type + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation`. + x-ms-enum: + name: RunStepDeltaStepDetailsMessageCreationObjectType + modelAsString: true + values: + - value: message_creation + message_creation: + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + runStepDetailsToolCallsObject: + title: Tool calls + required: + - type + - tool_calls + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-ms-enum: + name: RunStepDetailsToolCallsObjectType + modelAsString: true + values: + - value: tool_calls + tool_calls: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsFunctionObject' + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `retrieval` or `function`.\n" + description: Details of the tool call. + runStepDeltaStepDetailsToolCallsObject: + title: Tool calls + required: + - type + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsObjectType + modelAsString: true + values: + - value: tool_calls + tool_calls: + type: array + items: + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsFunctionObject' + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search` or `function`.\n" + description: Details of the tool call. + runStepDetailsToolCallsCodeObject: + title: Code interpreter tool call + required: + - id + - type + - code_interpreter + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsCodeObjectType + modelAsString: true + values: + - value: code_interpreter + code_interpreter: + required: + - input + - outputs + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeOutputImageObject' + description: 'The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.' + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + runStepDeltaStepDetailsToolCallsCodeObject: + title: Code interpreter tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeObjectType + modelAsString: true + values: + - value: code_interpreter + code_interpreter: + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputImageObject' + description: 'The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.' + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + runStepDetailsToolCallsCodeOutputLogsObject: + title: Code interpreter log output + required: + - type + - logs + type: object + properties: + type: + enum: + - logs + type: string + description: Always `logs`. + x-ms-enum: + name: RunStepDetailsToolCallsCodeOutputLogsObjectType + modelAsString: true + values: + - value: logs + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + runStepDeltaStepDetailsToolCallsCodeOutputLogsObject: + title: Code interpreter log output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - logs + type: string + description: Always `logs`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeOutputLogsObjectType + modelAsString: true + values: + - value: logs + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + runStepDetailsToolCallsCodeOutputImageObject: + title: Code interpreter image output + required: + - type + - image + type: object + properties: + type: + enum: + - image + type: string + description: Always `image`. + x-ms-enum: + name: RunStepDetailsToolCallsCodeOutputImageObjectType + modelAsString: true + values: + - value: image + image: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The File ID of the image. + runStepDeltaStepDetailsToolCallsCodeOutputImageObject: + title: Code interpreter image output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - image + type: string + description: Always `image`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeOutputImageObject + modelAsString: true + values: + - value: image + image: + type: object + properties: + file_id: + type: string + description: The file ID of the image. + runStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - id + - type + - file_search + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: The type of tool call. This is always going to be `file_search` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFileSearchObjectType + modelAsString: true + values: + - value: file_search + file_search: + type: object + properties: + results: + type: array + items: + $ref: '#/components/schemas/runStepDetailsToolCallsFileSearchResultObject' + description: The results of the file search. + description: 'For now, this is always going to be an empty object.' + runStepDetailsToolCallsFileSearchResultObject: + title: File search tool call result + required: + - file_id + - file_name + - score + type: object + properties: + file_id: + type: string + description: The ID of the file that result was found in. + file_name: + type: string + description: The name of the file that result was found in. + score: + maximum: 1.0 + minimum: 0.0 + type: number + description: The score of the result. All values must be a floating point number between 0 and 1. + content: + type: array + items: + type: object + properties: + type: + enum: + - text + type: string + description: The type of the content. + text: + type: string + description: The text content of the file. + description: The content of the result that was found. The content is only included if requested via the include query parameter. + description: A result instance of the file search. + x-oaiTypeLabel: map + runStepDeltaStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - index + - type + - file_search + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: The type of tool call. This is always going to be `retrieval` for this type of tool call. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsFileSearchObjectType + modelAsString: true + values: + - value: file_search + file_search: + type: object + description: 'For now, this is always going to be an empty object.' + runStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: The type of tool call. This is always going to be `function` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFunctionObjectType + modelAsString: true + values: + - value: function + function: + required: + - name + - arguments + - output + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: 'The output of the function. This will be `null` if the outputs have not been [submitted](/docs/api-reference/runs/submitToolOutputs) yet.' + nullable: true + description: The definition of the function that was called. + runStepDeltaStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: The type of tool call. This is always going to be `function` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFunctionObjectType + modelAsString: true + values: + - value: function + function: + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: The output of the function. This will be `null` if the outputs have not been submitted yet. + nullable: true + description: The definition of the function that was called. + vectorStoreExpirationAfter: + title: Vector store expiration policy + required: + - anchor + - days + type: object + properties: + anchor: + enum: + - last_active_at + type: string + description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`.' + x-ms-enum: + name: VectorStoreExpirationAfterAnchor + modelAsString: true + values: + - value: last_active_at + description: The anchor timestamp after which the expiration policy applies. + days: + maximum: 365.0 + minimum: 1.0 + type: integer + description: The number of days after the anchor time that the vector store will expire. + description: The expiration policy for a vector store. + vectorStoreObject: + title: Vector store + required: + - id + - object + - usage_bytes + - created_at + - status + - last_active_at + - name + - bytes + - file_counts + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store + type: string + description: 'The object type, which is always `vector_store`.' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store was created. + name: + type: string + description: The name of the vector store. + usage_bytes: + type: integer + description: The total number of bytes used by the files in the vector store. + file_counts: + required: + - in_progress + - completed + - failed + - cancelled + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been successfully processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that were cancelled. + total: + type: integer + description: The total number of files. + status: + enum: + - expired + - in_progress + - completed + type: string + description: 'The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use.' + x-ms-enum: + name: VectorStoreObjectStatus + modelAsString: true + values: + - value: expired + - value: in_progress + - value: completed + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store will expire. + nullable: true + last_active_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store was last active. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: A vector store is a collection of processed files can be used by the `file_search` tool. + createVectorStoreRequest: + type: object + properties: + file_ids: + maxItems: 500 + type: array + items: + type: string + description: A list of file IDs that the vector store should use. Useful for tools like `file_search` that can access files. + name: + type: string + description: The name of the vector store. + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + chunking_strategy: + type: object + oneOf: + - $ref: '#/components/schemas/autoChunkingStrategyRequestParam' + - $ref: '#/components/schemas/staticChunkingStrategyRequestParam' + description: 'The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + updateVectorStoreRequest: + type: object + properties: + name: + type: string + description: The name of the vector store. + nullable: true + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + listVectorStoresResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/vectorStoreObject' + first_id: + type: string + example: vs_abc123 + last_id: + type: string + example: vs_abc456 + has_more: + type: boolean + example: false + deleteVectorStoreResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.deleted + type: string + x-ms-enum: + name: DeleteVectorStoreResponseObject + modelAsString: true + values: + - value: vector_store.deleted + description: The delete vector store response object state + vectorStoreFileObject: + title: Vector store files + required: + - id + - object + - usage_bytes + - created_at + - vector_store_id + - status + - last_error + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.file + type: string + description: 'The object type, which is always `vector_store.file`.' + x-ms-enum: + name: VectorStoreFileObjectType + modelAsString: true + values: + - value: vector_store.file + usage_bytes: + type: integer + description: The total vector store usage in bytes. Note that this may be different from the original file size. + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store file was created. + vector_store_id: + type: string + description: The ID of the vector store that the file is attached to. + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: 'The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.' + x-ms-enum: + name: VectorStoreFileObjectStatus + modelAsString: true + values: + - value: in_progress + - value: completed + - value: cancelled + - value: failed + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - unsupported_file + - invalid_file + type: string + description: One of `server_error` or `invalid_file` or `unsupported_file`. + x-ms-enum: + name: VectorStoreFileObjectLastErrorCode + modelAsString: true + values: + - value: server_error + - value: unsupported_file + - value: invalid_file + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this vector store file. Will be `null` if there are no errors. + nullable: true + chunking_strategy: + type: object + oneOf: + - $ref: '#/components/schemas/autoChunkingStrategyRequestParam' + - $ref: '#/components/schemas/staticChunkingStrategyRequestParam' + description: 'The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.' + description: A list of files attached to a vector store. + otherChunkingStrategyResponseParam: + title: Other Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - other + type: string + description: Always `other`. + x-ms-enum: + name: OtherChunkingStrategyResponseParamType + modelAsString: true + values: + - value: other + additionalProperties: false + description: 'This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API.' + staticChunkingStrategyResponseParam: + title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-ms-enum: + name: StaticChunkingStrategyResponseParamType + modelAsString: true + values: + - value: static + static: + $ref: '#/components/schemas/staticChunkingStrategy' + additionalProperties: false + staticChunkingStrategy: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`. + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + autoChunkingStrategyRequestParam: + title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + additionalProperties: false + description: The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`. + staticChunkingStrategyRequestParam: + title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + static: + $ref: '#/components/schemas/staticChunkingStrategy' + additionalProperties: false + chunkingStrategyRequestParam: + type: object + oneOf: + - $ref: '#/components/schemas/autoChunkingStrategyRequestParam' + - $ref: '#/components/schemas/staticChunkingStrategyRequestParam' + description: 'The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.' + createVectorStoreFileRequest: + required: + - file_id + type: object + properties: + file_id: + type: string + description: A File ID that the vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: + $ref: '#/components/schemas/chunkingStrategyRequestParam' + additionalProperties: false + listVectorStoreFilesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/vectorStoreFileObject' + first_id: + type: string + example: file-abc123 + last_id: + type: string + example: file-abc456 + has_more: + type: boolean + example: false + deleteVectorStoreFileResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.file.deleted + type: string + x-ms-enum: + name: DeleteVectorStoreFileResponseObject + modelAsString: true + values: + - value: vector_store.file.deleted + vectorStoreFileBatchObject: + title: Vector store file batch + required: + - id + - object + - created_at + - vector_store_id + - status + - file_counts + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.files_batch + type: string + description: 'The object type, which is always `vector_store.file_batch`.' + x-ms-enum: + name: VectorStoreFileBatchObjectType + modelAsString: true + values: + - value: vector_store.files_batch + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store files batch was created. + vector_store_id: + type: string + description: The ID of the vector store that the File is attached to. + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: 'The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.' + x-ms-enum: + name: VectorStoreFileBatchObjectStatus + modelAsString: true + values: + - value: in_progress + - value: completed + - value: cancelled + - value: failed + file_counts: + required: + - in_progress + - completed + - cancelled + - failed + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that where cancelled. + total: + type: integer + description: The total number of files. + description: A batch of files attached to a vector store. + createVectorStoreFileBatchRequest: + required: + - file_ids + type: object + properties: + file_ids: + maxItems: 500 + minItems: 1 + type: array + items: + type: string + description: A list of File IDs that the vector store should use. Useful for tools like `file_search` that can access files. + chunking_strategy: + $ref: '#/components/schemas/chunkingStrategyRequestParam' + additionalProperties: false + assistantStreamEvent: + oneOf: + - $ref: '#/components/schemas/threadStreamEvent' + - $ref: '#/components/schemas/runStreamEvent' + - $ref: '#/components/schemas/runStepStreamEvent' + - $ref: '#/components/schemas/messageStreamEvent' + - $ref: '#/components/schemas/errorEvent' + - $ref: '#/components/schemas/doneEvent' + description: "Represents an event emitted when streaming a Run.\n\nEach event in a server-sent events stream has an `event` and `data` property:\n\n```\nevent: thread.created\ndata: {\"id\": \"thread_123\", \"object\": \"thread\", ...}\n```\n\nWe emit events whenever a new object is created, transitions to a new state, or is being\nstreamed in parts (deltas). For example, we emit `thread.run.created` when a new run\nis created, `thread.run.completed` when a run completes, and so on. When an Assistant chooses\nto create a message during a run, we emit a `thread.message.created event`, a\n`thread.message.in_progress` event, many `thread.message.delta` events, and finally a\n`thread.message.completed` event.\n\nWe may add additional events over time, so we recommend handling unknown events gracefully\nin your code." + threadStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.created + type: string + x-ms-enum: + name: ThreadStreamEventEnum + modelAsString: true + values: + - value: thread.created + description: The thread created event + data: + $ref: '#/components/schemas/threadObject' + description: Occurs when a new thread is created. + runStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.created + type: string + x-ms-enum: + name: RunStreamEventCreated + modelAsString: true + values: + - value: thread.run.created + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a new run is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.queued + type: string + x-ms-enum: + name: RunStreamEventQueued + modelAsString: true + values: + - value: thread.run.queued + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `queued` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.in_progress + type: string + x-ms-enum: + name: RunStreamEventInProgress + modelAsString: true + values: + - value: thread.run.in_progress + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to an `in_progress` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.requires_action + type: string + x-ms-enum: + name: RunStreamEventRequiresAction + modelAsString: true + values: + - value: thread.run.requires_action + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `requires_action` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.completed + type: string + x-ms-enum: + name: RunStreamEventCompleted + modelAsString: true + values: + - value: thread.run.completed + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.failed + type: string + x-ms-enum: + name: RunStreamEventFailed + modelAsString: true + values: + - value: thread.run.failed + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run fails. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelling + type: string + x-ms-enum: + name: RunStreamEventCancelling + modelAsString: true + values: + - value: thread.run.cancelling + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `cancelling` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelled + type: string + x-ms-enum: + name: RunStreamEventCancelled + modelAsString: true + values: + - value: thread.run.cancelled + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run is cancelled. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.expired + type: string + x-ms-enum: + name: RunStreamEventExpired + modelAsString: true + values: + - value: thread.run.expired + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run expires. + runStepStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.created + type: string + x-ms-enum: + name: RunStepStreamEventCreated + modelAsString: true + values: + - value: thread.run.step.created + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.in_progress + type: string + x-ms-enum: + name: RunStepStreamEventInProgress + modelAsString: true + values: + - value: thread.run.step.in_progress + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step moves to an `in_progress` state. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.delta + type: string + x-ms-enum: + name: RunStepStreamEventDelta + modelAsString: true + values: + - value: thread.run.step.delta + data: + $ref: '#/components/schemas/runStepDeltaObject' + description: Occurs when parts of a run step are being streamed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.completed + type: string + x-ms-enum: + name: RunStepStreamEventCompleted + modelAsString: true + values: + - value: thread.run.step.completed + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.failed + type: string + x-ms-enum: + name: RunStepStreamEventFailed + modelAsString: true + values: + - value: thread.run.step.failed + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step fails. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.cancelled + type: string + x-ms-enum: + name: RunStepStreamEventCancelled + modelAsString: true + values: + - value: thread.run.step.cancelled + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is cancelled. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.expired + type: string + x-ms-enum: + name: RunStepStreamEventExpired + modelAsString: true + values: + - value: thread.run.step.expired + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step expires. + messageStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.created + type: string + x-ms-enum: + name: MessageStreamEventCreated + modelAsString: true + values: + - value: thread.message.created + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.in_progress + type: string + x-ms-enum: + name: MessageStreamEventInProgress + modelAsString: true + values: + - value: thread.message.in_progress + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message moves to an `in_progress` state. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.delta + type: string + x-ms-enum: + name: MessageStreamEventDelta + modelAsString: true + values: + - value: thread.message.delta + data: + $ref: '#/components/schemas/messageDeltaObject' + description: Occurs when parts of a message are being streamed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.completed + type: string + x-ms-enum: + name: MessageStreamEventCompleted + modelAsString: true + values: + - value: thread.message.completed + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.incomplete + type: string + x-ms-enum: + name: MessageStreamEventIncomplete + modelAsString: true + values: + - value: thread.message.incomplete + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message ends before it is completed. + Annotation: + oneOf: + - $ref: '#/components/schemas/FileCitation' + - $ref: '#/components/schemas/UrlCitation' + - $ref: '#/components/schemas/FilePath' + Click: + title: Click + required: + - type + - button + - x + - y + type: object + properties: + type: + enum: + - click + type: string + description: "Specifies the event type. For a click action, this property is \nalways set to `click`.\n" + default: click + x-stainless-const: true + button: + enum: + - left + - right + - wheel + - back + - forward + type: string + description: "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`.\n" + x: + type: integer + description: "The x-coordinate where the click occurred.\n" + y: + type: integer + description: "The y-coordinate where the click occurred.\n" + description: "A click action.\n" + CodeInterpreterFileOutput: + title: Code interpreter file output + required: + - type + - files + type: object + properties: + type: + enum: + - files + type: string + description: "The type of the code interpreter file output. Always `files`.\n" + x-stainless-const: true + files: + type: array + items: + required: + - mime_type + - file_id + type: object + properties: + mime_type: + type: string + description: "The MIME type of the file.\n" + file_id: + type: string + description: "The ID of the file.\n" + description: "The output of a code interpreter tool call that is a file.\n" + CodeInterpreterTextOutput: + title: Code interpreter text output + required: + - type + - logs + type: object + properties: + type: + enum: + - logs + type: string + description: "The type of the code interpreter text output. Always `logs`.\n" + x-stainless-const: true + logs: + type: string + description: "The logs of the code interpreter tool call.\n" + description: "The output of a code interpreter tool call that is text.\n" + CodeInterpreterTool: + title: Code interpreter + required: + - type + - file_ids + type: object + properties: + type: + enum: + - code_interpreter + type: string + description: "The type of the code interpreter tool. Always `code_interpreter`.\n" + x-stainless-const: true + file_ids: + type: array + items: + type: string + description: "The IDs of the files to run the code on.\n" + description: "A tool that runs code.\n" + CodeInterpreterToolCall: + title: Code interpreter tool call + required: + - id + - type + - code + - status + - results + type: object + properties: + id: + type: string + description: "The unique ID of the code interpreter tool call.\n" + type: + enum: + - code_interpreter_call + type: string + description: "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + x-stainless-const: true + code: + type: string + description: "The code to run.\n" + status: + enum: + - in_progress + - interpreting + - completed + type: string + description: "The status of the code interpreter tool call.\n" + results: + type: array + items: + $ref: '#/components/schemas/CodeInterpreterToolOutput' + description: "The results of the code interpreter tool call.\n" + description: "A tool call to run code.\n" + CodeInterpreterToolOutput: + oneOf: + - $ref: '#/components/schemas/CodeInterpreterTextOutput' + - $ref: '#/components/schemas/CodeInterpreterFileOutput' + ComparisonFilter: + title: Comparison Filter + required: + - type + - key + - value + type: object + properties: + type: + enum: + - eq + - ne + - gt + - gte + - lt + - lte + type: string + description: "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal\n" + default: eq + key: + type: string + description: The key to compare against the value. + value: + oneOf: + - type: string + - type: number + - type: boolean + description: 'The value to compare against the attribute key; supports string, number, or boolean types.' + additionalProperties: false + description: "A filter used to compare a specified attribute key to a given value using a defined comparison operation.\n" + x-oaiMeta: + name: ComparisonFilter + CompoundFilter: + title: Compound Filter + required: + - type + - filters + type: object + properties: + type: + enum: + - and + - or + type: string + description: 'Type of operation: `and` or `or`.' + filters: + type: array + items: + oneOf: + - $ref: '#/components/schemas/ComparisonFilter' + - $ref: '#/components/schemas/CompoundFilter' + description: Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`. + additionalProperties: false + description: Combine multiple filters using `and` or `or`. + x-oaiMeta: + name: CompoundFilter + ComputerAction: + oneOf: + - $ref: '#/components/schemas/Click' + - $ref: '#/components/schemas/DoubleClick' + - $ref: '#/components/schemas/Drag' + - $ref: '#/components/schemas/KeyPress' + - $ref: '#/components/schemas/Move' + - $ref: '#/components/schemas/Screenshot' + - $ref: '#/components/schemas/Scroll' + - $ref: '#/components/schemas/Type' + - $ref: '#/components/schemas/Wait' + ComputerScreenshotImage: + required: + - type + type: object + properties: + type: + enum: + - computer_screenshot + type: string + description: "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n" + default: computer_screenshot + x-stainless-const: true + image_url: + type: string + description: The URL of the screenshot image. + file_id: + type: string + description: The identifier of an uploaded file that contains the screenshot. + description: "A computer screenshot image used with the computer use tool.\n" + ComputerTool: + title: Computer use + required: + - type + - display_width + - display_height + - environment + type: object + properties: + type: + enum: + - computer-preview + type: string + description: "The type of the computer use tool. Always `computer_use_preview`.\n" + x-stainless-const: true + display_width: + type: number + description: "The width of the computer display.\n" + display_height: + type: number + description: "The height of the computer display.\n" + environment: + enum: + - mac + - windows + - ubuntu + - browser + type: string + description: "The type of computer environment to control.\n" + description: "A tool that controls a virtual computer. Learn more about the \n[computer tool](/docs/guides/tools-computer-use).\n" + ComputerToolCall: + title: Computer tool call + required: + - type + - id + - action + - call_id + - pending_safety_checks + - status + type: object + properties: + type: + enum: + - computer_call + type: string + description: The type of the computer call. Always `computer_call`. + default: computer_call + id: + type: string + description: The unique ID of the computer call. + call_id: + type: string + description: "An identifier used when responding to the tool call with output.\n" + action: + $ref: '#/components/schemas/ComputerAction' + pending_safety_checks: + type: array + items: + $ref: '#/components/schemas/ComputerToolCallSafetyCheck' + description: "The pending safety checks for the computer call.\n" + x-oaiExpandable: true + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A tool call to a computer use tool. See the \n[computer use guide](/docs/guides/tools-computer-use) for more information.\n" + ComputerToolCallOutput: + title: Computer tool call output + required: + - type + - call_id + - output + type: object + properties: + type: + enum: + - computer_call_output + type: string + description: "The type of the computer tool call output. Always `computer_call_output`.\n" + default: computer_call_output + x-stainless-const: true + id: + type: string + description: "The ID of the computer tool call output.\n" + call_id: + type: string + description: "The ID of the computer tool call that produced the output.\n" + acknowledged_safety_checks: + type: array + items: + $ref: '#/components/schemas/ComputerToolCallSafetyCheck' + description: "The safety checks reported by the API that have been acknowledged by the \ndeveloper.\n" + x-oaiExpandable: true + output: + $ref: '#/components/schemas/ComputerScreenshotImage' + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + description: "The output of a computer tool call.\n" + ComputerToolCallOutputResource: + allOf: + - $ref: '#/components/schemas/ComputerToolCallOutput' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the computer call tool output.\n" + ComputerToolCallSafetyCheck: + required: + - id + - code + - message + type: object + properties: + id: + type: string + description: The ID of the pending safety check. + code: + type: string + description: The type of the pending safety check. + message: + type: string + description: Details about the pending safety check. + description: "A pending safety check for the computer call.\n" + Content: + oneOf: + - $ref: '#/components/schemas/InputContent' + - $ref: '#/components/schemas/OutputContent' + description: "Multi-modal input and output contents.\n" + Coordinate: + title: Coordinate + required: + - x + - y + type: object + properties: + x: + type: integer + description: "The x-coordinate.\n" + y: + type: integer + description: "The y-coordinate.\n" + description: "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.\n" + CreateModelResponseProperties: + allOf: + - $ref: '#/components/schemas/ModelResponseProperties' + createResponse: + allOf: + - $ref: '#/components/schemas/CreateModelResponseProperties' + - $ref: '#/components/schemas/ResponseProperties' + - required: + - model + - input + type: object + properties: + input: + oneOf: + - title: Text input + type: string + description: "A text input to the model, equivalent to a text input with the \n`user` role.\n" + - title: Input item list + type: array + items: + $ref: '#/components/schemas/InputItem' + description: "A list of one or many input items to the model, containing \ndifferent content types.\n" + description: "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Image inputs](/docs/guides/images)\n- [File inputs](/docs/guides/pdf-files)\n- [Conversation state](/docs/guides/conversation-state)\n- [Function calling](/docs/guides/function-calling)\n" + x-oaiExpandable: true + include: + type: array + items: + $ref: '#/components/schemas/includable' + description: "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n" + nullable: true + parallel_tool_calls: + type: boolean + description: "Whether to allow the model to run tool calls in parallel.\n" + default: true + nullable: true + store: + type: boolean + description: "Whether to store the generated model response for later retrieval via\nAPI.\n" + default: true + nullable: true + stream: + type: boolean + description: "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](/docs/api-reference/responses-streaming)\nfor more information.\n" + default: false + nullable: true + DoubleClick: + title: DoubleClick + required: + - type + - x + - y + type: object + properties: + type: + enum: + - double_click + type: string + description: "Specifies the event type. For a double click action, this property is \nalways set to `double_click`.\n" + default: double_click + x-stainless-const: true + x: + type: integer + description: "The x-coordinate where the double click occurred.\n" + y: + type: integer + description: "The y-coordinate where the double click occurred.\n" + description: "A double click action.\n" + Drag: + title: Drag + required: + - type + - path + type: object + properties: + type: + enum: + - drag + type: string + description: "Specifies the event type. For a drag action, this property is \nalways set to `drag`.\n" + default: drag + x-stainless-const: true + path: + type: array + items: + $ref: '#/components/schemas/Coordinate' + description: "An array of coordinates representing the path of the drag action. Coordinates will appear as an array\nof objects, eg\n```\n[\n { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```\n" + x-oaiExpandable: true + description: "A drag action.\n" + EasyInputMessage: + title: Input message + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + - system + - developer + type: string + description: "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + content: + oneOf: + - title: Text input + type: string + description: "A text input to the model.\n" + - $ref: '#/components/schemas/InputMessageContentList' + description: "Text, image, or audio input to the model, used to generate a response.\nCan also contain previous assistant responses.\n" + x-oaiExpandable: true + type: + enum: + - message + type: string + description: "The type of the message input. Always `message`.\n" + x-stainless-const: true + description: "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n" + FileCitation: + title: File citation + required: + - type + - index + - file_id + type: object + properties: + type: + enum: + - file_citation + type: string + description: "The type of the file citation. Always `file_citation`.\n" + x-stainless-const: true + index: + type: integer + description: "The index of the file in the list of files.\n" + file_id: + type: string + description: "The ID of the file.\n" + description: "A citation to a file.\n" + FilePath: + title: File path + required: + - type + - file_id + - index + type: object + properties: + type: + enum: + - file_path + type: string + description: "The type of the file path. Always `file_path`.\n" + x-stainless-const: true + file_id: + type: string + description: "The ID of the file.\n" + index: + type: integer + description: "The index of the file in the list of files.\n" + description: "A path to a file.\n" + FileSearchRanker: + enum: + - auto + - default_2024_08_21 + type: string + description: The ranker to use for the file search. If not specified will use the `auto` ranker. + FileSearchTool: + title: File search + required: + - type + - vector_store_ids + type: object + properties: + type: + enum: + - file_search + type: string + description: "The type of the file search tool. Always `file_search`.\n" + x-stainless-const: true + vector_store_ids: + type: array + items: + type: string + description: "The IDs of the vector stores to search.\n" + max_num_results: + type: integer + description: "The maximum number of results to return. This number should be between 1 \nand 50 inclusive.\n" + filters: + oneOf: + - $ref: '#/components/schemas/ComparisonFilter' + - $ref: '#/components/schemas/CompoundFilter' + description: A filter to apply based on file attributes. + x-oaiExpandable: true + ranking_options: + type: object + properties: + ranker: + enum: + - auto + - default-2024-11-15 + type: string + description: The ranker to use for the file search. + default: auto + score_threshold: + maximum: 1.0 + minimum: 0.0 + type: number + description: "The score threshold for the file search, a number between 0 and 1.\nNumbers closer to 1 will attempt to return only the most relevant\nresults, but may return fewer results.\n" + default: 0 + additionalProperties: false + description: Ranking options for search. + description: "A tool that searches for relevant content from uploaded files.\nLearn more about the [file search tool](/docs/guides/tools-file-search).\n" + FileSearchToolCall: + title: File search tool call + required: + - id + - type + - status + - queries + type: object + properties: + id: + type: string + description: "The unique ID of the file search tool call.\n" + type: + enum: + - file_search_call + type: string + description: "The type of the file search tool call. Always `file_search_call`.\n" + x-stainless-const: true + status: + enum: + - in_progress + - searching + - completed + - incomplete + - failed + type: string + description: "The status of the file search tool call. One of `in_progress`, \n`searching`, `incomplete` or `failed`,\n" + queries: + type: array + items: + type: string + description: "The queries used to search for files.\n" + results: + type: array + items: + type: object + properties: + file_id: + type: string + description: "The unique ID of the file.\n" + text: + type: string + description: "The text that was retrieved from the file.\n" + filename: + type: string + description: "The name of the file.\n" + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + score: + type: number + description: "The relevance score of the file - a value between 0 and 1.\n" + format: float + description: "The results of the file search tool call.\n" + nullable: true + description: "The results of a file search tool call. See the \n[file search guide](/docs/guides/tools-file-search) for more information.\n" + FunctionTool: + title: Function + required: + - type + - name + - parameters + - strict + type: object + properties: + type: + enum: + - function + type: string + description: "The type of the function tool. Always `function`.\n" + x-stainless-const: true + name: + type: string + description: "The name of the function to call.\n" + description: + type: string + description: "A description of the function. Used by the model to determine whether\nor not to call the function.\n" + nullable: true + parameters: + type: object + description: "A JSON schema object describing the parameters of the function.\n" + strict: + type: boolean + description: "Whether to enforce strict parameter validation. Default `true`.\n" + description: "Defines a function in your own code the model can choose to call. Learn more\nabout [function calling](/docs/guides/function-calling).\n" + FunctionToolCall: + title: Function tool call + required: + - id + - type + - call_id + - name + - arguments + type: object + properties: + id: + type: string + description: "The unique ID of the function tool call.\n" + type: + enum: + - function_call + type: string + description: "The type of the function tool call. Always `function_call`.\n" + x-stainless-const: true + call_id: + type: string + description: "The unique ID of the function tool call generated by the model.\n" + name: + type: string + description: "The name of the function to run.\n" + arguments: + type: string + description: "A JSON string of the arguments to pass to the function.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A tool call to run a function. See the \n[function calling guide](/docs/guides/function-calling) for more information.\n" + FunctionToolCallOutput: + title: Function tool call output + required: + - type + - call_id + - output + type: object + properties: + id: + type: string + description: "The unique ID of the function tool call output. Populated when this item\nis returned via API.\n" + type: + enum: + - function_call_output + type: string + description: "The type of the function tool call output. Always `function_call_output`.\n" + x-stainless-const: true + call_id: + type: string + description: "The unique ID of the function tool call generated by the model.\n" + output: + type: string + description: "A JSON string of the output of the function tool call.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "The output of a function tool call.\n" + FunctionToolCallOutputResource: + allOf: + - $ref: '#/components/schemas/FunctionToolCallOutput' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the function call tool output.\n" + includable: + enum: + - file_search_call.results + - message.input_image.image_url + - computer_call_output.output.image_url + type: string + description: "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n" + InputAudio: + title: Audio input + required: + - type + - data + - format + type: object + properties: + type: + enum: + - input_audio + type: string + description: "The type of the input item. Always `input_audio`.\n" + x-stainless-const: true + data: + type: string + description: "Base64-encoded audio data.\n" + format: + enum: + - mp3 + - wav + type: string + description: "The format of the audio data. Currently supported formats are `mp3` and\n`wav`.\n" + description: "An audio input to the model.\n" + InputContent: + oneOf: + - $ref: '#/components/schemas/InputText' + - $ref: '#/components/schemas/InputImage' + - $ref: '#/components/schemas/InputFile' + x-oaiExpandable: true + InputFile: + title: File input + required: + - type + type: object + properties: + type: + enum: + - input_file + type: string + description: "The type of the input item. Always `input_file`.\n" + x-stainless-const: true + file_id: + type: string + description: "The ID of the file to be sent to the model.\n" + filename: + type: string + description: "The name of the file to be sent to the model.\n" + file_data: + type: string + description: "The content of the file to be sent to the model.\n" + description: "A file input to the model.\n" + InputImage: + title: Image input + required: + - type + - detail + type: object + properties: + type: + enum: + - input_image + type: string + description: "The type of the input item. Always `input_image`.\n" + x-stainless-const: true + image_url: + type: string + description: "The URL of the image to be sent to the model. A fully qualified URL or\nbase64 encoded image in a data URL.\n" + nullable: true + file_id: + type: string + description: "The ID of the file to be sent to the model.\n" + nullable: true + detail: + enum: + - high + - low + - auto + type: string + description: "The detail level of the image to be sent to the model. One of `high`,\n`low`, or `auto`. Defaults to `auto`.\n" + default: auto + description: "An image input to the model. Learn about [image inputs](/docs/guides/vision).\n" + InputItem: + oneOf: + - $ref: '#/components/schemas/EasyInputMessage' + - $ref: '#/components/schemas/Item' + - $ref: '#/components/schemas/ItemReference' + discriminator: + propertyName: type + InputMessage: + title: Input message + required: + - role + - content + type: object + properties: + type: + enum: + - message + type: string + description: "The type of the message input. Always set to `message`.\n" + x-stainless-const: true + role: + enum: + - user + - system + - developer + type: string + description: "The role of the message input. One of `user`, `system`, or `developer`.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + content: + $ref: '#/components/schemas/InputMessageContentList' + description: "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role.\n" + InputMessageContentList: + title: Input item content list + type: array + items: + $ref: '#/components/schemas/InputContent' + description: "A list of one or many input items to the model, containing different content \ntypes.\n" + x-oaiExpandable: true + InputMessageResource: + allOf: + - $ref: '#/components/schemas/InputMessage' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the message input.\n" + InputText: + title: Text input + required: + - type + - text + type: object + properties: + type: + enum: + - input_text + type: string + description: "The type of the input item. Always `input_text`.\n" + x-stainless-const: true + text: + type: string + description: "The text input to the model.\n" + description: "A text input to the model.\n" + Item: + type: object + oneOf: + - $ref: '#/components/schemas/InputMessage' + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ComputerToolCallOutput' + - $ref: '#/components/schemas/FunctionToolCall' + - $ref: '#/components/schemas/FunctionToolCallOutput' + - $ref: '#/components/schemas/ReasoningItem' + description: "Content item used to generate a response.\n" + discriminator: + propertyName: type + x-oaiExpandable: true + ItemReference: + title: Item reference + required: + - id + - type + type: object + properties: + id: + type: string + description: "The ID of the item to reference.\n" + type: + enum: + - item_reference + type: string + description: "The type of item to reference. Always `item_reference`.\n" + x-stainless-const: true + description: "An internal identifier for an item to reference.\n" + ItemResource: + oneOf: + - $ref: '#/components/schemas/InputMessageResource' + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ComputerToolCallOutputResource' + - $ref: '#/components/schemas/FunctionToolCall' + - $ref: '#/components/schemas/FunctionToolCallOutputResource' + description: "Content item used to generate a response.\n" + discriminator: + propertyName: type + x-oaiExpandable: true + KeyPress: + title: KeyPress + required: + - type + - keys + type: object + properties: + type: + enum: + - keypress + type: string + description: "Specifies the event type. For a keypress action, this property is \nalways set to `keypress`.\n" + default: keypress + x-stainless-const: true + keys: + type: array + items: + type: string + description: "One of the keys the model is requesting to be pressed.\n" + description: "The combination of keys the model is requesting to be pressed. This is an\narray of strings, each representing a key.\n" + description: "A collection of keypresses the model would like to perform.\n" + Metadata: + type: object + additionalProperties: + type: string + description: "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. \n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.\n" + nullable: true + x-oaiTypeLabel: map + ModelResponseProperties: + type: object + properties: + model: + type: string + description: Model used to generate the responses. + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + default: 1 + nullable: true + example: 1 + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + Move: + title: Move + required: + - type + - x + - y + type: object + properties: + type: + enum: + - move + type: string + description: "Specifies the event type. For a move action, this property is \nalways set to `move`.\n" + default: move + x-stainless-const: true + x: + type: integer + description: "The x-coordinate to move to.\n" + y: + type: integer + description: "The y-coordinate to move to.\n" + description: "A mouse move action.\n" + OutputAudio: + title: Output audio + required: + - type + - data + - transcript + type: object + properties: + type: + enum: + - output_audio + type: string + description: "The type of the output audio. Always `output_audio`.\n" + x-stainless-const: true + data: + type: string + description: "Base64-encoded audio data from the model.\n" + transcript: + type: string + description: "The transcript of the audio data from the model.\n" + description: "An audio output from the model.\n" + OutputContent: + oneOf: + - $ref: '#/components/schemas/OutputText' + - $ref: '#/components/schemas/Refusal' + OutputItem: + anyOf: + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/FunctionToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ReasoningItem' + discriminator: + propertyName: type + x-oaiExpandable: true + OutputMessage: + title: Output message + required: + - id + - type + - role + - content + - status + type: object + properties: + id: + type: string + description: "The unique ID of the output message.\n" + type: + enum: + - message + type: string + description: "The type of the output message. Always `message`.\n" + x-stainless-const: true + role: + enum: + - assistant + type: string + description: "The role of the output message. Always `assistant`.\n" + x-stainless-const: true + content: + type: array + items: + $ref: '#/components/schemas/OutputContent' + description: "The content of the output message.\n" + x-oaiExpandable: true + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + description: "An output message from the model.\n" + OutputText: + title: Output text + required: + - type + - text + - annotations + type: object + properties: + type: + enum: + - output_text + type: string + description: "The type of the output text. Always `output_text`.\n" + x-stainless-const: true + text: + type: string + description: "The text output from the model.\n" + annotations: + type: array + items: + $ref: '#/components/schemas/Annotation' + description: "The annotations of the text output.\n" + description: "A text output from the model.\n" + RealtimeSessionCreateRequest: + type: object + properties: + modalities: + items: + enum: + - text + - audio + type: string + default: + - text + - audio + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + model: + type: string + description: "The name of the deployment used for this session.\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\n\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + input_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian byte order.\n" + default: pcm16 + output_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, output audio is sampled at a rate of 24kHz.\n" + default: pcm16 + input_audio_transcription: + type: object + properties: + model: + type: string + description: "The model to use for transcription, current options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment.\nFor `whisper-1`, the [prompt is a list of keywords](/docs/guides/speech-to-text#prompting).\nFor `gpt-4o-transcribe` models, the prompt is a free text string, for example \"expect words related to technology\".\n" + description: "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the Transcriptions endpoint](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference-preview#transcriptions---create) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + turn_detection: + type: object + properties: + type: + enum: + - server_vad + - semantic_vad + type: string + description: "Type of turn detection.\n" + default: server_vad + eagerness: + enum: + - low + - medium + - high + - auto + type: string + description: "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`.\n" + default: auto + threshold: + type: number + description: "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher threshold will require louder audio to activate the model, and thus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults \nto 500ms. With shorter values the model will respond more quickly, \nbut may jump in on short pauses from the user.\n" + create_response: + type: boolean + description: "Whether or not to automatically generate a response when a VAD stop event occurs.\n" + default: true + interrupt_response: + type: boolean + description: "Whether or not to automatically interrupt any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs.\n" + default: true + description: "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\nSemantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n" + input_audio_noise_reduction: + type: object + properties: + type: + enum: + - near_field + - far_field + type: string + description: "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n" + description: "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + default: + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how \nto call it, and guidance about what to tell the user when calling \n(if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or \nspecify a function.\n" + default: auto + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a temperature of 0.8 is highly recommended for best performance.\n" + default: 0.8 + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + description: Realtime session object configuration. + RealtimeSessionCreateResponse: + required: + - client_secret + type: object + properties: + client_secret: + required: + - value + - expires_at + type: object + properties: + value: + type: string + description: "Ephemeral key usable in client environments to authenticate connections to the Realtime API. Use this in client-side environments rather than a standard API token, which should only be used server-side.\n" + expires_at: + type: integer + description: "Timestamp for when the token expires. Currently, all tokens expire after one minute.\n" + description: Ephemeral key returned by the API. + modalities: + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + input_audio_format: + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + output_audio_format: + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + input_audio_transcription: + type: object + properties: + model: + type: string + description: "The model to use for transcription, `whisper-1` is the only currently supported model.\n" + description: "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through Whisper and should be treated as rough guidance rather than the representation understood by the model.\n" + turn_detection: + type: object + properties: + type: + type: string + description: "Type of turn detection, only `server_vad` is currently supported.\n" + threshold: + type: number + description: "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher threshold will require louder audio to activate the model, and thus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. With shorter values the model will respond more quickly, but may jump in on short pauses from the user.\n" + description: "Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\n" + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how to call it, and guidance about what to tell the user when calling (if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or specify a function.\n" + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.\n" + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + description: "A new Realtime session configuration, with an ephemeral key. Default TTL for keys is one minute.\n" + RealtimeTranscriptionSessionCreateRequest: + type: object + properties: + modalities: + items: + enum: + - text + - audio + type: string + default: + - text + - audio + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + input_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate, single channel (mono), and little-endian byte order.\n" + default: pcm16 + input_audio_transcription: + type: object + properties: + model: + enum: + - gpt-4o-transcribe + - gpt-4o-mini-transcribe + - whisper-1 + type: string + description: "The model to use for transcription, current options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment.\nFor `whisper-1`, the prompt is a list of keywords.\nFor `gpt-4o-transcribe` models, the prompt is a free text string, for example \"expect words related to technology\".\n" + description: "Configuration for input audio transcription. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + turn_detection: + type: object + properties: + type: + enum: + - server_vad + - semantic_vad + type: string + description: "Type of turn detection.\n" + default: server_vad + eagerness: + enum: + - low + - medium + - high + - auto + type: string + description: "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`.\n" + default: auto + threshold: + type: number + description: "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A \nhigher threshold will require louder audio to activate the model, and \nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. With shorter values the model will respond more quickly, but may jump in on short pauses from the user.\n" + create_response: + type: boolean + description: "Whether or not to automatically generate a response when a VAD stop event occurs. Not available for transcription sessions.\n" + default: true + interrupt_response: + type: boolean + description: "Whether or not to automatically interrupt any ongoing response with output to the default conversation (i.e. `conversation` of `auto`) when a VAD start event occurs. Not available for transcription sessions.\n" + default: true + description: "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\nSemantic VAD is more advanced and uses a turn detection model (in conjunction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n" + input_audio_noise_reduction: + type: object + properties: + type: + enum: + - near_field + - far_field + type: string + description: "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n" + description: "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + default: + include: + type: array + items: + type: string + description: "The set of items to include in the transcription. Current available items are:\n- `item.input_audio_transcription.logprobs`\n" + description: Realtime transcription session object configuration. + RealtimeTranscriptionSessionCreateResponse: + required: + - client_secret + type: object + properties: + client_secret: + required: + - value + - expires_at + type: object + properties: + value: + type: string + description: "Ephemeral key usable in client environments to authenticate connections to the Realtime API. Use this in client-side environments rather than a standard API token, which should only be used server-side.\n" + expires_at: + type: integer + description: "Timestamp for when the token expires. Currently, all tokens expire after one minute.\n" + description: "Ephemeral key returned by the API. Only present when the session is\ncreated on the server via REST API.\n" + modalities: + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + input_audio_format: + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + input_audio_transcription: + type: object + properties: + model: + enum: + - gpt-4o-transcribe + - gpt-4o-mini-transcribe + - whisper-1 + type: string + description: "The model to use for transcription. Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.\n" + description: "Configuration of the transcription model.\n" + turn_detection: + type: object + properties: + type: + type: string + description: "Type of turn detection, only `server_vad` is currently supported.\n" + threshold: + type: number + description: "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A higher threshold will require louder audio to activate the model, and thus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Duration of silence to detect speech stop (in milliseconds). Defaults to 500ms. With shorter values the model will respond more quickly, but may jump in on short pauses from the user.\n" + description: "Configuration for turn detection. Can be set to `null` to turn off. Server VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\n" + description: "A new Realtime transcription session configuration.\n\nWhen a session is created on the server via REST API, the session object also contains an ephemeral key. Default TTL for keys is one minute. This property is not present when a session is updated via the WebSocket API.\n" + Reasoning: + title: Reasoning + required: + - effort + type: object + properties: + effort: + $ref: '#/components/schemas/ReasoningEffort' + summary: + enum: + - concise + - detailed + type: string + description: "**o-series models only** \n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `concise` or `detailed`.\n" + nullable: true + description: "**o-series models only**\n\nConfiguration options for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n" + x-oaiExpandable: true + ReasoningItem: + title: Reasoning + required: + - id + - content + - type + type: object + properties: + type: + enum: + - reasoning + type: string + description: "The type of the object. Always `reasoning`.\n" + x-stainless-const: true + id: + type: string + description: "The unique identifier of the reasoning content.\n" + content: + type: array + items: + required: + - type + - text + type: object + properties: + type: + enum: + - reasoning_summary + type: string + description: "The type of the object. Always `text`.\n" + x-stainless-const: true + text: + type: string + description: "A short summary of the reasoning used by the model when generating\nthe response.\n" + description: "Reasoning text contents.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A description of the chain of thought used by a reasoning model while generating\na response.\n" + x-oaiExpandable: true + Refusal: + title: Refusal + required: + - type + - refusal + type: object + properties: + type: + enum: + - refusal + type: string + description: "The type of the refusal. Always `refusal`.\n" + x-stainless-const: true + refusal: + type: string + description: "The refusal explanationfrom the model.\n" + description: "A refusal from the model.\n" + response: + allOf: + - $ref: '#/components/schemas/ModelResponseProperties' + - $ref: '#/components/schemas/ResponseProperties' + - required: + - id + - object + - created_at + - error + - incomplete_details + - instructions + - model + - tools + - output + - parallel_tool_calls + - metadata + - tool_choice + - temperature + - top_p + type: object + properties: + id: + type: string + description: "Unique identifier for this Response.\n" + object: + enum: + - response + type: string + description: "The object type of this resource - always set to `response`.\n" + x-stainless-const: true + status: + enum: + - completed + - failed + - in_progress + - incomplete + type: string + description: "The status of the response generation. One of `completed`, `failed`, \n`in_progress`, or `incomplete`.\n" + created_at: + type: number + description: "Unix timestamp (in seconds) of when this Response was created.\n" + error: + $ref: '#/components/schemas/ResponseError' + incomplete_details: + type: object + properties: + reason: + enum: + - max_output_tokens + - content_filter + type: string + description: The reason why the response is incomplete. + description: "Details about why the response is incomplete.\n" + nullable: true + output: + type: array + items: + $ref: '#/components/schemas/OutputItem' + description: "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and \n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n" + x-oaiExpandable: true + output_text: + type: string + description: "SDK-only convenience property that contains the aggregated text output \nfrom all `output_text` items in the `output` array, if any are present. \nSupported in the Python and JavaScript SDKs.\n" + nullable: true + x-oaiSupportedSDKs: + - python + - javascript + usage: + $ref: '#/components/schemas/ResponseUsage' + parallel_tool_calls: + type: boolean + description: "Whether to allow the model to run tool calls in parallel.\n" + default: true + x-oaiMeta: + name: The response object + group: responses + example: "{\n \"id\": \"resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41\",\n \"object\": \"response\",\n \"created_at\": 1741476777,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [\n {\n \"type\": \"message\",\n \"id\": \"msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41\",\n \"status\": \"completed\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"The image depicts a scenic landscape with a wooden boardwalk or pathway leading through lush, green grass under a blue sky with some clouds. The setting suggests a peaceful natural area, possibly a park or nature reserve. There are trees and shrubs in the background.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1.0,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1.0,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 328,\n \"input_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"output_tokens\": 52,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 380\n },\n \"user\": null,\n \"metadata\": {}\n}\n" + ResponseAudioDeltaEvent: + required: + - type + - delta + type: object + properties: + type: + enum: + - response.audio.delta + type: string + description: "The type of the event. Always `response.audio.delta`.\n" + x-stainless-const: true + delta: + type: string + description: "A chunk of Base64 encoded response audio bytes.\n" + description: Emitted when there is a partial audio response. + x-oaiMeta: + name: response.audio.delta + group: responses + example: "{\n \"type\": \"response.audio.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \"base64encoded...\"\n}\n" + ResponseAudioDoneEvent: + required: + - type + - response_id + type: object + properties: + type: + enum: + - response.audio.done + type: string + description: "The type of the event. Always `response.audio.done`.\n" + x-stainless-const: true + description: Emitted when the audio response is complete. + x-oaiMeta: + name: response.audio.done + group: responses + example: "{\n \"type\": \"response.audio.done\",\n \"response_id\": \"resp-123\"\n}\n" + ResponseAudioTranscriptDeltaEvent: + required: + - type + - response_id + - delta + type: object + properties: + type: + enum: + - response.audio.transcript.delta + type: string + description: "The type of the event. Always `response.audio.transcript.delta`.\n" + x-stainless-const: true + delta: + type: string + description: "The partial transcript of the audio response.\n" + description: Emitted when there is a partial transcript of audio. + x-oaiMeta: + name: response.audio.transcript.delta + group: responses + example: "{\n \"type\": \"response.audio.transcript.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \" ... partial transcript ... \"\n}\n" + ResponseAudioTranscriptDoneEvent: + required: + - type + - response_id + type: object + properties: + type: + enum: + - response.audio.transcript.done + type: string + description: "The type of the event. Always `response.audio.transcript.done`.\n" + x-stainless-const: true + description: Emitted when the full audio transcript is completed. + x-oaiMeta: + name: response.audio.transcript.done + group: responses + example: "{\n \"type\": \"response.audio.transcript.done\",\n \"response_id\": \"resp_123\"\n}\n" + ResponseCodeInterpreterCallCodeDeltaEvent: + required: + - type + - response_id + - output_index + - delta + type: object + properties: + type: + enum: + - response.code_interpreter_call.code.delta + type: string + description: "The type of the event. Always `response.code_interpreter_call.code.delta`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + delta: + type: string + description: "The partial code snippet added by the code interpreter.\n" + description: Emitted when a partial code snippet is added by the code interpreter. + x-oaiMeta: + name: response.code_interpreter_call.code.delta + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.code.delta\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"delta\": \"partial code\"\n}\n" + ResponseCodeInterpreterCallCodeDoneEvent: + required: + - type + - response_id + - output_index + - code + type: object + properties: + type: + enum: + - response.code_interpreter_call.code.done + type: string + description: "The type of the event. Always `response.code_interpreter_call.code.done`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code: + type: string + description: "The final code snippet output by the code interpreter.\n" + description: Emitted when code snippet output is finalized by the code interpreter. + x-oaiMeta: + name: response.code_interpreter_call.code.done + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.code.done\",\n \"response_id\": \"resp-123\",\n \"output_index\": 3,\n \"code\": \"console.log('done');\"\n}\n" + ResponseCodeInterpreterCallCompletedEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.completed + type: string + description: "The type of the event. Always `response.code_interpreter_call.completed`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: Emitted when the code interpreter call is completed. + x-oaiMeta: + name: response.code_interpreter_call.completed + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.completed\",\n \"response_id\": \"resp-123\",\n \"output_index\": 5,\n \"code_interpreter_call\": {}\n}\n" + ResponseCodeInterpreterCallInProgressEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.in_progress + type: string + description: "The type of the event. Always `response.code_interpreter_call.in_progress`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: Emitted when a code interpreter call is in progress. + x-oaiMeta: + name: response.code_interpreter_call.in_progress + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.in.progress\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"code_interpreter_call\": {}\n}\n" + ResponseCodeInterpreterCallInterpretingEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.interpreting + type: string + description: "The type of the event. Always `response.code_interpreter_call.interpreting`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: Emitted when the code interpreter is actively interpreting the code snippet. + x-oaiMeta: + name: response.code_interpreter_call.interpreting + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.interpreting\",\n \"response_id\": \"resp-123\",\n \"output_index\": 4,\n \"code_interpreter_call\": {}\n}\n" + ResponseCompletedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.completed + type: string + description: "The type of the event. Always `response.completed`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/response' + description: Emitted when the model response is complete. + x-oaiMeta: + name: response.completed + group: responses + example: "{\n \"type\": \"response.completed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"input\": [],\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [\n {\n \"id\": \"msg_123\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 0,\n \"output_tokens\": 0,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 0\n },\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseContentPartAddedEvent: + required: + - type + - item_id + - output_index + - content_index + - part + type: object + properties: + type: + enum: + - response.content_part.added + type: string + description: "The type of the event. Always `response.content_part.added`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the content part was added to.\n" + output_index: + type: integer + description: "The index of the output item that the content part was added to.\n" + content_index: + type: integer + description: "The index of the content part that was added.\n" + part: + $ref: '#/components/schemas/OutputContent' + description: Emitted when a new content part is added. + x-oaiMeta: + name: response.content_part.added + group: responses + example: "{\n \"type\": \"response.content_part.added\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"\",\n \"annotations\": []\n }\n}\n" + ResponseContentPartDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - part + type: object + properties: + type: + enum: + - response.content_part.done + type: string + description: "The type of the event. Always `response.content_part.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the content part was added to.\n" + output_index: + type: integer + description: "The index of the output item that the content part was added to.\n" + content_index: + type: integer + description: "The index of the content part that is done.\n" + part: + $ref: '#/components/schemas/OutputContent' + description: Emitted when a content part is done. + x-oaiMeta: + name: response.content_part.done + group: responses + example: "{\n \"type\": \"response.content_part.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n}\n" + ResponseCreatedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.created + type: string + description: "The type of the event. Always `response.created`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/response' + description: "An event that is emitted when a response is created.\n" + x-oaiMeta: + name: response.created + group: responses + example: "{\n \"type\": \"response.created\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseError: + required: + - code + - message + type: object + properties: + code: + $ref: '#/components/schemas/ResponseErrorCode' + message: + type: string + description: "A human-readable description of the error.\n" + description: "An error object returned when the model fails to generate a Response.\n" + nullable: true + ResponseErrorCode: + enum: + - server_error + - rate_limit_exceeded + - invalid_prompt + - vector_store_timeout + - invalid_image + - invalid_image_format + - invalid_base64_image + - invalid_image_url + - image_too_large + - image_too_small + - image_parse_error + - image_content_policy_violation + - invalid_image_mode + - image_file_too_large + - unsupported_image_media_type + - empty_image_file + - failed_to_download_image + - image_file_not_found + type: string + description: "The error code for the response.\n" + ResponseErrorEvent: + required: + - type + - code + - message + - param + type: object + properties: + type: + enum: + - error + type: string + description: "The type of the event. Always `error`.\n" + x-stainless-const: true + code: + type: string + description: "The error code.\n" + nullable: true + message: + type: string + description: "The error message.\n" + param: + type: string + description: "The error parameter.\n" + nullable: true + description: Emitted when an error occurs. + x-oaiMeta: + name: error + group: responses + example: "{\n \"type\": \"error\",\n \"code\": \"ERR_SOMETHING\",\n \"message\": \"Something went wrong\",\n \"param\": null\n}\n" + ResponseFailedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.failed + type: string + description: "The type of the event. Always `response.failed`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/response' + description: "An event that is emitted when a response fails.\n" + x-oaiMeta: + name: response.failed + group: responses + example: "{\n \"type\": \"response.failed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"failed\",\n \"error\": {\n \"code\": \"server_error\",\n \"message\": \"The model failed to generate a response.\"\n },\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseFileSearchCallCompletedEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.completed + type: string + description: "The type of the event. Always `response.file_search_call.completed`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is initiated.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search call is completed (results found). + x-oaiMeta: + name: response.file_search_call.completed + group: responses + example: "{\n \"type\": \"response.file_search_call.completed\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFileSearchCallInProgressEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.in_progress + type: string + description: "The type of the event. Always `response.file_search_call.in_progress`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is initiated.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search call is initiated. + x-oaiMeta: + name: response.file_search_call.in_progress + group: responses + example: "{\n \"type\": \"response.file_search_call.in_progress\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFileSearchCallSearchingEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.searching + type: string + description: "The type of the event. Always `response.file_search_call.searching`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is searching.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search is currently searching. + x-oaiMeta: + name: response.file_search_call.searching + group: responses + example: "{\n \"type\": \"response.file_search_call.searching\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFunctionCallArgumentsDeltaEvent: + required: + - type + - item_id + - output_index + - delta + type: object + properties: + type: + enum: + - response.function_call_arguments.delta + type: string + description: "The type of the event. Always `response.function_call_arguments.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the function-call arguments delta is added to.\n" + output_index: + type: integer + description: "The index of the output item that the function-call arguments delta is added to.\n" + delta: + type: string + description: "The function-call arguments delta that is added.\n" + description: Emitted when there is a partial function-call arguments delta. + x-oaiMeta: + name: response.function_call_arguments.delta + group: responses + example: "{\n \"type\": \"response.function_call_arguments.delta\",\n \"item_id\": \"item-abc\",\n \"output_index\": 0,\n \"delta\": \"{ \\\"arg\\\":\"\n}\n" + ResponseFunctionCallArgumentsDoneEvent: + required: + - type + - item_id + - output_index + - arguments + type: object + properties: + type: + enum: + - response.function_call_arguments.done + type: string + x-stainless-const: true + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item. + arguments: + type: string + description: The function-call arguments. + description: Emitted when function-call arguments are finalized. + x-oaiMeta: + name: response.function_call_arguments.done + group: responses + example: "{\n \"type\": \"response.function_call_arguments.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"arguments\": \"{ \\\"arg\\\": 123 }\"\n}\n" + ResponseInProgressEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.in_progress + type: string + description: "The type of the event. Always `response.in_progress`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/response' + description: Emitted when the response is in progress. + x-oaiMeta: + name: response.in_progress + group: responses + example: "{\n \"type\": \"response.in_progress\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseIncompleteEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.incomplete + type: string + description: "The type of the event. Always `response.incomplete`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/response' + description: "An event that is emitted when a response finishes as incomplete.\n" + x-oaiMeta: + name: response.incomplete + group: responses + example: "{\n \"type\": \"response.incomplete\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"incomplete\",\n \"error\": null, \n \"incomplete_details\": {\n \"reason\": \"max_tokens\"\n },\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + responseItemList: + required: + - object + - data + - has_more + - first_id + - last_id + type: object + properties: + object: + enum: + - list + type: string + description: 'The type of object returned, must be `list`.' + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/ItemResource' + description: A list of items used to generate this response. + has_more: + type: boolean + description: Whether there are more items available. + first_id: + type: string + description: The ID of the first item in the list. + last_id: + type: string + description: The ID of the last item in the list. + description: A list of Response items. + x-oaiExpandable: true + x-oaiMeta: + name: The input item list + group: responses + example: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"id\": \"msg_abc123\",\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_text\",\n \"text\": \"Tell me a three sentence bedtime story about a unicorn.\"\n }\n ]\n }\n ],\n \"first_id\": \"msg_abc123\",\n \"last_id\": \"msg_abc123\",\n \"has_more\": false\n}\n" + ResponseModalities: + type: array + items: + enum: + - text + - audio + type: string + description: "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to \n[generate audio](/docs/guides/audio). To request that this model generate \nboth text and audio responses, you can use:\n\n`[\"text\", \"audio\"]`\n" + nullable: true + ResponseModalitiesTextOnly: + type: array + items: + enum: + - text + type: string + description: "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThis API will soon support other output modalities, including audio and images.\n" + nullable: true + ResponseOutputItemAddedEvent: + required: + - type + - output_index + - item + type: object + properties: + type: + enum: + - response.output_item.added + type: string + description: "The type of the event. Always `response.output_item.added`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that was added.\n" + item: + $ref: '#/components/schemas/OutputItem' + description: Emitted when a new output item is added. + x-oaiMeta: + name: response.output_item.added + group: responses + example: "{\n \"type\": \"response.output_item.added\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"in_progress\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": []\n }\n}\n" + ResponseOutputItemDoneEvent: + required: + - type + - output_index + - item + type: object + properties: + type: + enum: + - response.output_item.done + type: string + description: "The type of the event. Always `response.output_item.done`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that was marked done.\n" + item: + $ref: '#/components/schemas/OutputItem' + description: Emitted when an output item is marked done. + x-oaiMeta: + name: response.output_item.done + group: responses + example: "{\n \"type\": \"response.output_item.done\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"completed\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n}\n" + ResponseProperties: + type: object + properties: + previous_response_id: + type: string + description: "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about \n[conversation state](/docs/guides/conversation-state).\n" + nullable: true + reasoning: + $ref: '#/components/schemas/Reasoning' + max_output_tokens: + type: integer + description: "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n" + nullable: true + instructions: + type: string + description: "Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will be not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + nullable: true + text: + type: object + properties: + format: + $ref: '#/components/schemas/TextResponseFormatConfiguration' + description: "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)\n" + tools: + type: array + items: + $ref: '#/components/schemas/Tool' + description: "An array of tools the model may call while generating a response. You \ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](/docs/guides/tools-web-search)\n or [file search](/docs/guides/tools-file-search). Learn more about\n [built-in tools](/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code. Learn more about\n [function calling](/docs/guides/function-calling).\n" + tool_choice: + oneOf: + - $ref: '#/components/schemas/ToolChoiceOptions' + - $ref: '#/components/schemas/ToolChoiceTypes' + - $ref: '#/components/schemas/ToolChoiceFunction' + description: "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n" + x-oaiExpandable: true + truncation: + enum: + - auto + - disabled + type: string + description: "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the \n response to fit the context window by dropping input items in the\n middle of the conversation. \n- `disabled` (default): If a model response will exceed the context window \n size for a model, the request will fail with a 400 error.\n" + default: disabled + nullable: true + ResponseRefusalDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - delta + type: object + properties: + type: + enum: + - response.refusal.delta + type: string + description: "The type of the event. Always `response.refusal.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the refusal text is added to.\n" + output_index: + type: integer + description: "The index of the output item that the refusal text is added to.\n" + content_index: + type: integer + description: "The index of the content part that the refusal text is added to.\n" + delta: + type: string + description: "The refusal text that is added.\n" + description: Emitted when there is a partial refusal text. + x-oaiMeta: + name: response.refusal.delta + group: responses + example: "{\n \"type\": \"response.refusal.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"refusal text so far\"\n}\n" + ResponseRefusalDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - refusal + type: object + properties: + type: + enum: + - response.refusal.done + type: string + description: "The type of the event. Always `response.refusal.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the refusal text is finalized.\n" + output_index: + type: integer + description: "The index of the output item that the refusal text is finalized.\n" + content_index: + type: integer + description: "The index of the content part that the refusal text is finalized.\n" + refusal: + type: string + description: "The refusal text that is finalized.\n" + description: Emitted when refusal text is finalized. + x-oaiMeta: + name: response.refusal.done + group: responses + example: "{\n \"type\": \"response.refusal.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"content_index\": 2,\n \"refusal\": \"final refusal text\"\n}\n" + responseStreamEvent: + anyOf: + - $ref: '#/components/schemas/ResponseAudioDeltaEvent' + - $ref: '#/components/schemas/ResponseAudioDoneEvent' + - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent' + - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent' + - $ref: '#/components/schemas/ResponseCompletedEvent' + - $ref: '#/components/schemas/ResponseContentPartAddedEvent' + - $ref: '#/components/schemas/ResponseContentPartDoneEvent' + - $ref: '#/components/schemas/ResponseCreatedEvent' + - $ref: '#/components/schemas/ResponseErrorEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent' + - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent' + - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent' + - $ref: '#/components/schemas/ResponseInProgressEvent' + - $ref: '#/components/schemas/ResponseFailedEvent' + - $ref: '#/components/schemas/ResponseIncompleteEvent' + - $ref: '#/components/schemas/ResponseOutputItemAddedEvent' + - $ref: '#/components/schemas/ResponseOutputItemDoneEvent' + - $ref: '#/components/schemas/ResponseRefusalDeltaEvent' + - $ref: '#/components/schemas/ResponseRefusalDoneEvent' + - $ref: '#/components/schemas/ResponseTextAnnotationDeltaEvent' + - $ref: '#/components/schemas/ResponseTextDeltaEvent' + - $ref: '#/components/schemas/ResponseTextDoneEvent' + discriminator: + propertyName: type + ResponseTextAnnotationDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - annotation_index + - annotation + type: object + properties: + type: + enum: + - response.output_text.annotation.added + type: string + description: "The type of the event. Always `response.output_text.annotation.added`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text annotation was added to.\n" + output_index: + type: integer + description: "The index of the output item that the text annotation was added to.\n" + content_index: + type: integer + description: "The index of the content part that the text annotation was added to.\n" + annotation_index: + type: integer + description: "The index of the annotation that was added.\n" + annotation: + $ref: '#/components/schemas/Annotation' + description: Emitted when a text annotation is added. + x-oaiMeta: + name: response.output_text.annotation.added + group: responses + example: "{\n \"type\": \"response.output_text.annotation.added\",\n \"item_id\": \"msg_abc123\",\n \"output_index\": 1,\n \"content_index\": 0,\n \"annotation_index\": 0,\n \"annotation\": {\n \"type\": \"file_citation\",\n \"index\": 390,\n \"file_id\": \"file-4wDz5b167pAf72nx1h9eiN\",\n \"filename\": \"dragons.pdf\"\n }\n}\n" + ResponseTextDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - delta + type: object + properties: + type: + enum: + - response.output_text.delta + type: string + description: "The type of the event. Always `response.output_text.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text delta was added to.\n" + output_index: + type: integer + description: "The index of the output item that the text delta was added to.\n" + content_index: + type: integer + description: "The index of the content part that the text delta was added to.\n" + delta: + type: string + description: "The text delta that was added.\n" + description: Emitted when there is an additional text delta. + x-oaiMeta: + name: response.output_text.delta + group: responses + example: "{\n \"type\": \"response.output_text.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"In\"\n}\n" + ResponseTextDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - text + type: object + properties: + type: + enum: + - response.output_text.done + type: string + description: "The type of the event. Always `response.output_text.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text content is finalized.\n" + output_index: + type: integer + description: "The index of the output item that the text content is finalized.\n" + content_index: + type: integer + description: "The index of the content part that the text content is finalized.\n" + text: + type: string + description: "The text content that is finalized.\n" + description: Emitted when text content is finalized. + x-oaiMeta: + name: response.output_text.done + group: responses + example: "{\n \"type\": \"response.output_text.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\"\n}\n" + ResponseUsage: + required: + - input_tokens + - output_tokens + - output_tokens_details + - total_tokens + type: object + properties: + input_tokens: + type: integer + description: The number of input tokens. + output_tokens: + type: integer + description: The number of output tokens. + output_tokens_details: + required: + - reasoning_tokens + type: object + properties: + reasoning_tokens: + type: integer + description: The number of reasoning tokens. + description: A detailed breakdown of the output tokens. + total_tokens: + type: integer + description: The total number of tokens used. + description: "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n" + Screenshot: + title: Screenshot + required: + - type + type: object + properties: + type: + enum: + - screenshot + type: string + description: "Specifies the event type. For a screenshot action, this property is \nalways set to `screenshot`.\n" + default: screenshot + x-stainless-const: true + description: "A screenshot action.\n" + Scroll: + title: Scroll + required: + - type + - x + - y + - scroll_x + - scroll_y + type: object + properties: + type: + enum: + - scroll + type: string + description: "Specifies the event type. For a scroll action, this property is \nalways set to `scroll`.\n" + default: scroll + x-stainless-const: true + x: + type: integer + description: "The x-coordinate where the scroll occurred.\n" + y: + type: integer + description: "The y-coordinate where the scroll occurred.\n" + scroll_x: + type: integer + description: "The horizontal scroll distance.\n" + scroll_y: + type: integer + description: "The vertical scroll distance.\n" + description: "A scroll action.\n" + StopConfiguration: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: "\n" + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + example: '["\n"]' + description: "Up to 4 sequences where the API will stop generating further tokens. The\nreturned text will not contain the stop sequence.\n" + default: + nullable: true + TextResponseFormatConfiguration: + oneOf: + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/TextResponseFormatJsonSchema' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + description: "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs, \nwhich ensures the model will match your supplied JSON schema. Learn more in the \n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n" + x-oaiExpandable: true + TextResponseFormatJsonSchema: + title: JSON schema + required: + - type + - schema + type: object + properties: + type: + enum: + - json_schema + type: string + description: The type of response format being defined. Always `json_schema`. + x-stainless-const: true + description: + type: string + description: "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + name: + type: string + description: "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + schema: + $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema' + strict: + type: boolean + description: "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n" + default: false + nullable: true + description: "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n" + Tool: + oneOf: + - $ref: '#/components/schemas/FileSearchTool' + - $ref: '#/components/schemas/FunctionTool' + - $ref: '#/components/schemas/ComputerTool' + x-oaiExpandable: true + ToolChoiceFunction: + title: Function tool + required: + - type + - name + type: object + properties: + type: + enum: + - function + type: string + description: 'For function calling, the type is always `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function to call. + description: "Use this option to force the model to call a specific function.\n" + ToolChoiceOptions: + title: Tool choice mode + enum: + - none + - auto + - required + type: string + description: "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n" + ToolChoiceTypes: + title: Hosted tool + required: + - type + type: object + properties: + type: + enum: + - file_search + - computer_use_preview + type: string + description: "The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `computer_use_preview`\n" + description: "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n" + Type: + title: Type + required: + - type + - text + type: object + properties: + type: + enum: + - type + type: string + description: "Specifies the event type. For a type action, this property is \nalways set to `type`.\n" + default: type + x-stainless-const: true + text: + type: string + description: "The text to type.\n" + description: "An action to type in text.\n" + UpdateVectorStoreFileAttributesRequest: + required: + - attributes + type: object + properties: + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + additionalProperties: false + x-oaiMeta: + name: Update vector store file attributes request + UrlCitation: + title: URL citation + required: + - url + - title + - type + - start_index + - end_index + type: object + properties: + url: + type: string + description: "The URL of the web resource.\n" + title: + type: string + description: "The title of the web resource.\n" + type: + enum: + - url_citation + type: string + description: "The type of the URL citation. Always `url_citation`.\n" + x-stainless-const: true + start_index: + type: integer + description: "The index of the first character of the URL citation in the message.\n" + end_index: + type: integer + description: "The index of the last character of the URL citation in the message.\n" + description: "A citation for a web resource used to generate a model response.\n" + VectorStoreFileAttributes: + maxProperties: 16 + type: object + additionalProperties: + oneOf: + - maxLength: 512 + type: string + - type: number + - type: boolean + description: "Set of 16 key-value pairs that can be attached to an object. This can be \nuseful for storing additional information about the object in a structured \nformat, and querying for objects via API or the dashboard. Keys are strings \nwith a maximum length of 64 characters. Values are strings with a maximum \nlength of 512 characters, booleans, or numbers.\n" + nullable: true + x-oaiTypeLabel: map + VectorStoreFileContentResponse: + required: + - object + - data + - has_more + - next_page + type: object + properties: + object: + enum: + - vector_store.file_content.page + type: string + description: 'The object type, which is always `vector_store.file_content.page`' + x-stainless-const: true + data: + type: array + items: + type: object + properties: + type: + type: string + description: The content type (currently only `"text"`) + text: + type: string + description: The text content + description: Parsed content of the file. + has_more: + type: boolean + description: Indicates if there are more content pages to fetch. + next_page: + type: string + description: 'The token for the next page, if any.' + nullable: true + description: Represents the parsed content of a vector store file. + VectorStoreSearchRequest: + required: + - query + type: object + properties: + query: + oneOf: + - type: string + - type: array + items: + minItems: 1 + type: string + description: A list of queries to search for. + description: A query string for a search + rewrite_query: + type: boolean + description: Whether to rewrite the natural language query for vector search. + default: false + max_num_results: + maximum: 50.0 + minimum: 1.0 + type: integer + description: The maximum number of results to return. This number should be between 1 and 50 inclusive. + default: 10 + filters: + oneOf: + - $ref: '#/components/schemas/ComparisonFilter' + - $ref: '#/components/schemas/CompoundFilter' + description: A filter to apply based on file attributes. + x-oaiExpandable: true + ranking_options: + type: object + properties: + ranker: + enum: + - auto + - default-2024-11-15 + type: string + default: auto + score_threshold: + maximum: 1.0 + minimum: 0.0 + type: number + default: 0 + additionalProperties: false + description: Ranking options for search. + additionalProperties: false + x-oaiMeta: + name: Vector store search request + VectorStoreSearchResultContentObject: + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: The type of content. + text: + type: string + description: The text content returned from search. + additionalProperties: false + x-oaiMeta: + name: Vector store search result content object + VectorStoreSearchResultItem: + required: + - file_id + - filename + - score + - attributes + - content + type: object + properties: + file_id: + type: string + description: The ID of the vector store file. + filename: + type: string + description: The name of the vector store file. + score: + maximum: 1.0 + minimum: 0.0 + type: number + description: The similarity score for the result. + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + content: + type: array + items: + $ref: '#/components/schemas/VectorStoreSearchResultContentObject' + description: Content chunks from the file. + additionalProperties: false + x-oaiMeta: + name: Vector store search result item + VectorStoreSearchResultsPage: + required: + - object + - search_query + - data + - has_more + - next_page + type: object + properties: + object: + enum: + - vector_store.search_results.page + type: string + description: 'The object type, which is always `vector_store.search_results.page`' + x-stainless-const: true + search_query: + type: array + items: + minItems: 1 + type: string + description: The query used for this search. + data: + type: array + items: + $ref: '#/components/schemas/VectorStoreSearchResultItem' + description: The list of search result items. + has_more: + type: boolean + description: Indicates if there are more results to fetch. + next_page: + type: string + description: 'The token for the next page, if any.' + nullable: true + additionalProperties: false + x-oaiMeta: + name: Vector store search results page + VoiceIdsShared: + anyOf: + - type: string + - enum: + - alloy + - ash + - ballad + - coral + - echo + - fable + - onyx + - nova + - sage + - shimmer + - verse + type: string + example: ash + Wait: + title: Wait + required: + - type + type: object + properties: + type: + enum: + - wait + type: string + description: "Specifies the event type. For a wait action, this property is \nalways set to `wait`.\n" + default: wait + x-stainless-const: true + description: "A wait action.\n" + ReasoningEffort: + enum: + - low + - medium + - high + type: string + description: "**o-series models only** \n\nConstrains effort on reasoning for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n" + default: medium + nullable: true + errorEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - error + type: string + x-ms-enum: + name: ErrorEventEnum + modelAsString: true + values: + - value: error + data: + $ref: '#/components/schemas/error' + description: Occurs when an error occurs. This can happen due to an internal server error or a timeout. + doneEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - done + type: string + x-ms-enum: + name: DoneEventEnum + modelAsString: true + values: + - value: done + data: + enum: + - '[DONE]' + type: string + x-ms-enum: + name: DoneEventDataEnum + modelAsString: true + values: + - value: '[DONE]' + description: Occurs when a stream ends. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-02-01.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-02-01.yaml new file mode 100644 index 000000000..d263d5fc3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-02-01.yaml @@ -0,0 +1,2320 @@ +openapi: 3.0.1 +info: + title: Azure OpenAI API + description: 'Azure OpenAI APIs for completions, images, transcription and search' + version: '1.0' +servers: + - url: https://REPLACE/ai-hub-gw +paths: + '/deployments/{deployment-id}/completions': + post: + summary: 'Creates a completion for the provided prompt, parameters and chosen model.' + description: 'Creates a completion for the provided prompt, parameters and chosen model.' + operationId: Completions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the model which was deployed. + example: davinci + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-02-01T00:00:00Z' + requestBody: + content: + application/json: + schema: + type: object + properties: + prompt: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - type: array + items: + type: string + default: '' + example: This is a test. + description: Array size minimum of 1 and maximum of 2048 + description: "The prompt(s) to generate completions for, encoded as a string or array of strings.\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. Maximum allowed size of string list is 2048." + max_tokens: + type: integer + description: 'The token count of your prompt plus max_tokens cannot exceed the model''s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096). Has minimum of 0.' + default: 16 + nullable: true + example: 16 + temperature: + type: number + description: "What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.\nWe generally recommend altering this or top_p but not both." + default: 1 + nullable: true + example: 1 + top_p: + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or temperature but not both." + default: 1 + nullable: true + example: 1 + logit_bias: + type: object + description: 'Defaults to null. Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. As an example, you can pass {"50256" : -100} to prevent the <|endoftext|> token from being generated.' + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse' + n: + type: integer + description: "How many completions to generate for each prompt. Minimum of 1 and maximum of 128 allowed.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.' + default: false + nullable: true + logprobs: + type: integer + description: "Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.\nMinimum of 0 and maximum of 5 allowed." + default: + nullable: true + suffix: + type: string + description: The suffix that comes after a completion of inserted text. + nullable: true + echo: + type: boolean + description: Echo back the prompt in addition to the completion + default: false + nullable: true + stop: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: '' + - type: array + items: + type: string + example: '' + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + completion_config: + type: string + nullable: true + presence_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + best_of: + type: integer + description: "Generates best_of completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\nWhen used with n, best_of controls the number of candidate completions and n specifies how many to return - best_of must be greater than n.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop. Has maximum value of 128." + example: + prompt: "Negate the following sentence.The price for bubblegum increased on thursday.\n Negated Sentence:" + max_tokens: 50 + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + object: + type: string + created: + type: integer + model: + type: string + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + properties: + text: + type: string + index: + type: integer + logprobs: + type: object + properties: + tokens: + type: array + items: + type: string + token_logprobs: + type: array + items: + type: number + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + text_offset: + type: array + items: + type: integer + nullable: true + finish_reason: + type: string + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + usage: + required: + - prompt_tokens + - total_tokens + - completion_tokens + type: object + properties: + completion_tokens: + type: number + format: int32 + prompt_tokens: + type: number + format: int32 + total_tokens: + type: number + format: int32 + example: + model: davinci + object: text_completion + id: cmpl-4509KAos68kxOqpE2uYGw81j6m7uo + created: 1637097562 + choices: + - index: 0 + text: The price for bubblegum decreased on thursday. + logprobs: + finish_reason: stop + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/embeddings': + post: + summary: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + description: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + operationId: embeddings_create + parameters: + - name: deployment-id + in: path + description: The deployment id of the model which was deployed. + required: true + schema: + enum: + - embedding + type: string + example: ada-search-index-v1 + - name: api-version + in: query + description: Azure OpenAI API version + required: true + schema: + enum: + - '2024-02-01' + type: string + description: api version + example: '2024-02-01' + requestBody: + content: + application/json: + schema: + required: + - input + type: object + properties: + input: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - maxItems: 2048 + minItems: 1 + type: array + items: + minLength: 1 + type: string + example: This is a test. + description: "Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length.\nUnless you are embedding code, we suggest replacing newlines (\\n) in your input with a single space, as we have observed inferior results when newlines are present." + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse.' + input_type: + type: string + description: input type of embedding search to use + example: query + example: + input: I love Azure API Management + user: APIM + responses: + '200': + description: OK + content: + application/json: + schema: + required: + - object + - model + - data + - usage + type: object + properties: + object: + type: string + model: + type: string + data: + type: array + items: + required: + - index + - object + - embedding + type: object + properties: + index: + type: integer + object: + type: string + embedding: + type: array + items: + type: number + usage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + total_tokens: + type: integer + example: + object: string + model: string + data: + - index: 0 + object: string + embedding: + - 0 + usage: + prompt_tokens: 0 + total_tokens: 0 + '/deployments/{deployment-id}/chat/completions': + post: + summary: Creates a completion for the chat message + description: Creates a completion for the chat message + operationId: ChatCompletions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - chat + type: string + description: Deployment id of the model which was deployed. + default: chat + - name: api-version + in: query + required: true + schema: + enum: + - '2024-02-01' + type: string + description: api version + default: '2024-02-01' + example: '2024-02-01' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionRequest' + example: + model: chat + messages: + - role: user + content: Hello! my name is Zaki and I'm asking about distance between the moon and earth + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionResponse' + example: + id: string + object: chat.completion + created: 0 + model: string + usage: + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + system_fingerprint: string + prompt_filter_results: + - prompt_index: 0 + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + choices: + - index: 0 + finish_reason: string + message: + role: assistant + content: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + function_call: + name: string + arguments: string + context: + citations: + - content: string + title: string + url: string + filepath: string + chunk_id: string + intent: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + protected_material_text: + filtered: true + detected: true + protected_material_code: + filtered: true + detected: true + citation: + URL: string + license: string + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/audio/transcriptions': + post: + summary: Transcribes audio into the input language. + description: Transcribes audio into the input language. + operationId: Transcriptions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-02-01' + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + prompt: + type: string + response_format: + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + temperature: + type: number + default: 0 + language: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/audio/translations': + post: + summary: Transcribes and translates input audio into English text. + description: Transcribes and translates input audio into English text. + operationId: Translations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model which was deployed. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-02-01' + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + prompt: + type: string + response_format: + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + temperature: + type: number + default: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/images/generations': + post: + summary: Generates a batch of images from a text caption on a given DALLE model deployment + description: Generates a batch of images from a text caption on a given DALLE model deployment + operationId: ImageGenerations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the dalle model which was deployed. + example: dalle-deployment + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-02-01' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/imageGenerationsRequest' + example: + prompt: a corgi in a field + n: 1 + size: 1024x1024 + response_format: url + user: user123456 + quality: standard + style: vivid + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + example: + created: '1676540381' + data: + - url: https://www.contoso.com + b64_json: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + revised_prompt: string + prompt_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + '400': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + revised_prompt: string + code: string + message: string + '500': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + revised_prompt: string + code: string + message: string +components: + schemas: + errorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/error' + errorBase: + type: object + properties: + code: + type: string + message: + type: string + error: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/innerError' + innerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Inner error with additional details. + innerErrorCode: + enum: + - ResponsibleAIPolicyViolation + type: string + description: Error codes for the inner error object. + x-ms-enum: + name: InnerErrorCode + modelAsString: true + values: + - value: ResponsibleAIPolicyViolation + description: The prompt violated one of more content filter rules. + dalleErrorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/dalleError' + dalleError: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/dalleInnerError' + dalleInnerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + description: Inner error with additional details. + contentFilterResultBase: + required: + - filtered + type: object + properties: + filtered: + type: boolean + contentFilterSeverityResult: + required: + - severity + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + severity: + enum: + - safe + - low + - medium + - high + type: string + x-ms-enum: + name: ContentFilterSeverity + modelAsString: true + values: + - value: safe + description: General content or related content in generic or non-harmful contexts. + - value: low + description: Harmful content at a low intensity and risk level. + - value: medium + description: Harmful content at a medium intensity and risk level. + - value: high + description: Harmful content at a high intensity and risk level. + contentFilterDetectedResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + detected: + type: boolean + contentFilterDetectedWithCitationResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + citation: + type: object + properties: + URL: + type: string + license: + type: string + contentFilterResultsBase: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + error: + $ref: '#/components/schemas/errorBase' + description: Information about the content filtering results. + contentFilterPromptResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterChoiceResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + protected_material_text: + $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + protected_material_code: + $ref: '#/components/schemas/contentFilterDetectedWithCitationResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + promptFilterResult: + type: object + properties: + prompt_index: + type: integer + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Content filtering results for a single prompt in the request. + promptFilterResults: + type: array + items: + $ref: '#/components/schemas/promptFilterResult' + description: 'Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.' + dalleContentFilterResults: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + description: Information about the content filtering results. + dalleFilterResults: + type: object + allOf: + - $ref: '#/components/schemas/dalleContentFilterResults' + - properties: + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + chatCompletionsRequestCommon: + type: object + properties: + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or `temperature` but not both." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.' + default: false + nullable: true + stop: + oneOf: + - type: string + nullable: true + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. + default: + max_tokens: + type: integer + description: 'The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens).' + default: 4096 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + logit_bias: + type: object + description: 'Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.' + nullable: true + user: + type: string + description: 'A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.' + example: user-1234 + createChatCompletionRequest: + required: + - messages + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsRequestCommon' + - properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessage' + description: 'A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).' + data_sources: + type: array + items: + $ref: '#/components/schemas/azureChatExtensionConfiguration' + description: " The configuration entries for Azure OpenAI chat extensions that use them.\n This additional specification is only compatible with Azure OpenAI." + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: How many chat completion choices to generate for each input message. + default: 1 + nullable: true + example: 1 + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: 'If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.' + default: 0 + nullable: true + example: 1 + response_format: + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionResponseFormat' + description: An object specifying the format that the model must output. Used to enable JSON mode. + tools: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionTool' + description: 'A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.' + tool_choice: + $ref: '#/components/schemas/chatCompletionToolChoiceOption' + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionFunction' + description: Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for. + function_call: + oneOf: + - enum: + - none + - auto + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: 'Specifying a particular function via `{"name": "my_function"}` forces the model to call that function.' + description: 'Deprecated in favor of `tool_choice`. Controls how the model responds to function calls. "none" means the model does not call a function, and responds to the end-user. "auto" means the model can pick between an end-user or calling a function. Specifying a particular function via `{"name":\ "my_function"}` forces the model to call that function. "none" is the default when no functions are present. "auto" is the default if functions are present.' + chatCompletionResponseFormat: + enum: + - text + - json_object + type: string + description: Setting to `json_object` enables JSON mode. This guarantees that the message the model generates is valid JSON. + default: text + nullable: true + example: json_object + x-ms-enum: + name: ChatCompletionResponseFormat + modelAsString: true + values: + - value: text + description: Response format is a plain text string. + - value: json_object + description: Response format is a JSON object. + chatCompletionFunction: + required: + - name + type: object + properties: + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + description: + type: string + description: The description of what the function does. + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + chatCompletionFunctionParameters: + type: object + description: 'The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.' + chatCompletionRequestMessage: + required: + - role + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionRequestMessageRole' + discriminator: + propertyName: role + mapping: + system: '#/components/schemas/chatCompletionRequestMessageSystem' + user: '#/components/schemas/chatCompletionRequestMessageUser' + assistant: '#/components/schemas/chatCompletionRequestMessageAssistant' + tool: '#/components/schemas/chatCompletionRequestMessageTool' + function: '#/components/schemas/chatCompletionRequestMessageFunction' + chatCompletionRequestMessageRole: + enum: + - system + - user + - assistant + - tool + - function + type: string + description: The role of the messages author. + x-ms-enum: + name: ChatCompletionRequestMessageRole + modelAsString: true + values: + - value: system + description: The message author role is system. + - value: user + description: The message author role is user. + - value: assistant + description: The message author role is assistant. + - value: tool + description: The message author role is tool. + - value: function + description: Deprecated. The message author role is function. + chatCompletionRequestMessageSystem: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + chatCompletionRequestMessageUser: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + oneOf: + - type: string + description: The contents of the message. + - minimum: 1.0 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + description: 'An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4-visual-preview` model.' + nullable: true + chatCompletionRequestMessageContentPart: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPartType' + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/chatCompletionRequestMessageContentPartText' + image_url: '#/components/schemas/chatCompletionRequestMessageContentPartImage' + chatCompletionRequestMessageContentPartType: + enum: + - text + - image_url + type: string + description: The type of the content part. + x-ms-enum: + name: ChatCompletionRequestMessageContentPartType + modelAsString: true + values: + - value: text + description: The content part type is text. + - value: image_url + description: The content part type is image_url. + chatCompletionRequestMessageContentPartText: + required: + - text + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + text: + type: string + description: The text content. + chatCompletionRequestMessageContentPartImage: + required: + - url + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + $ref: '#/components/schemas/imageDetailLevel' + imageDetailLevel: + enum: + - auto + - low + - high + type: string + description: Specifies the detail level of the image. + default: auto + x-ms-enum: + name: ImageDetailLevel + modelAsString: true + values: + - value: auto + description: The image detail level is auto. + - value: low + description: The image detail level is low. + - value: high + description: The image detail level is high. + chatCompletionRequestMessageAssistant: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + azureChatExtensionConfiguration: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/azureChatExtensionType' + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + discriminator: + propertyName: type + mapping: + azure_search: '#/components/schemas/azureSearchChatExtensionConfiguration' + azure_cosmos_db: '#/components/schemas/azureCosmosDBChatExtensionConfiguration' + azureChatExtensionType: + enum: + - azure_search + - azure_cosmos_db + type: string + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + x-ms-enum: + name: AzureChatExtensionType + modelAsString: true + values: + - name: azureSearch + value: azure_search + description: Represents the use of Azure Search as an Azure OpenAI chat extension. + - name: azureCosmosDB + value: azure_cosmos_db + description: Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + azureSearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureSearchChatExtensionParameters' + description: "A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_search + azureSearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + endpoint: + type: string + description: The absolute endpoint path for the Azure Search resource to use. + format: uri + index_name: + type: string + description: The name of the index to use as available in the referenced Azure Search resource. + fields_mapping: + $ref: '#/components/schemas/azureSearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/azureSearchQueryType' + semantic_configuration: + type: string + description: The additional semantic configuration for the query. + filter: + type: string + description: Search filter. + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + description: Parameters for Azure Search when used as an Azure OpenAI chat extension. + azureSearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Search resource. + azureSearchQueryType: + enum: + - simple + - semantic + - vector + - vector_simple_hybrid + - vector_semantic_hybrid + type: string + description: The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: azureSearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: semantic + value: semantic + description: Represents the semantic query parser for advanced semantic modeling. + - name: vector + value: vector + description: Represents vector search over computed data. + - name: vectorSimpleHybrid + value: vector_simple_hybrid + description: Represents a combination of the simple query strategy with vector data. + - name: vectorSemanticHybrid + value: vector_semantic_hybrid + description: Represents a combination of semantic search and vector data querying. + azureCosmosDBChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureCosmosDBChatExtensionParameters' + description: "A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_cosmos_db + azureCosmosDBChatExtensionParameters: + required: + - authentication + - container_name + - database_name + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + database_name: + type: string + description: The MongoDB vCore database name to use with Azure Cosmos DB. + container_name: + type: string + description: The name of the Azure Cosmos DB resource container. + index_name: + type: string + description: The MongoDB vCore index name to use with Azure Cosmos DB. + fields_mapping: + $ref: '#/components/schemas/azureCosmosDBFieldMappingOptions' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + description: "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\nMongoDB vCore." + azureCosmosDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource. + onYourDataAuthenticationOptions: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataAuthenticationType' + description: The authentication options for Azure OpenAI On Your Data. + discriminator: + propertyName: type + mapping: + api_key: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + connection_string: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + system_assigned_managed_identity: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + user_assigned_managed_identity: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + onYourDataAuthenticationType: + enum: + - api_key + - connection_string + - system_assigned_managed_identity + - user_assigned_managed_identity + type: string + description: The authentication types supported with Azure OpenAI On Your Data. + x-ms-enum: + name: OnYourDataAuthenticationType + modelAsString: true + values: + - name: apiKey + value: api_key + description: Authentication via API key. + - name: connectionString + value: connection_string + description: Authentication via connection string. + - name: systemAssignedManagedIdentity + value: system_assigned_managed_identity + description: Authentication via system-assigned managed identity. + - name: userAssignedManagedIdentity + value: user_assigned_managed_identity + description: Authentication via user-assigned managed identity. + onYourDataApiKeyAuthenticationOptions: + required: + - key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an API key. + x-ms-discriminator-value: api_key + onYourDataConnectionStringAuthenticationOptions: + required: + - connection_string + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + connection_string: + type: string + description: The connection string to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a connection string. + x-ms-discriminator-value: connection_string + onYourDataSystemAssignedManagedIdentityAuthenticationOptions: + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + description: The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity. + x-ms-discriminator-value: system_assigned_managed_identity + onYourDataUserAssignedManagedIdentityAuthenticationOptions: + required: + - managed_identity_resource_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + managed_identity_resource_id: + type: string + description: The resource ID of the user-assigned managed identity to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity. + x-ms-discriminator-value: user_assigned_managed_identity + onYourDataVectorizationSource: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataVectorizationSourceType' + description: An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search. + discriminator: + propertyName: type + mapping: + endpoint: '#/components/schemas/onYourDataEndpointVectorizationSource' + deployment_name: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + onYourDataVectorizationSourceType: + enum: + - endpoint + - deployment_name + type: string + description: "Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\nvector search." + x-ms-enum: + name: OnYourDataVectorizationSourceType + modelAsString: true + values: + - name: endpoint + value: endpoint + description: Represents vectorization performed by public service calls to an Azure OpenAI embedding model. + - name: deploymentName + value: deployment_name + description: "Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\nsearch even in private networks." + onYourDataDeploymentNameVectorizationSource: + required: + - deployment_name + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + deployment_name: + type: string + description: 'Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.' + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non an internal embeddings model deployment name in the same Azure OpenAI resource." + x-ms-discriminator-value: deployment_name + onYourDataEndpointVectorizationSource: + required: + - authentication + - endpoint + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + authentication: + $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + endpoint: + type: string + description: 'Specifies the endpoint to use for vectorization. This endpoint must be in the same Azure OpenAI resource, but On Your Data will use this endpoint via an internal call rather than a public one, which enables vector search even in private networks.' + format: uri + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a public Azure OpenAI endpoint call for embeddings." + x-ms-discriminator-value: endpoint + azureChatExtensionsMessageContext: + type: object + properties: + citations: + type: array + items: + $ref: '#/components/schemas/citation' + description: 'The data source retrieval result, used to generate the assistant message in the response.' + x-ms-identifiers: [ ] + intent: + type: string + description: 'The detected intent from the chat history, used to pass to the next turn to carry over the context.' + description: " A representation of the additional context information available when Azure OpenAI chat extensions are involved\n in the generation of a corresponding chat completions response. This context information is only populated when\n using an Azure OpenAI request configured to use a matching extension." + citation: + required: + - content + type: object + properties: + content: + type: string + description: The content of the citation. + title: + type: string + description: The title of the citation. + url: + type: string + description: The URL of the citation. + filepath: + type: string + description: The file path of the citation. + chunk_id: + type: string + description: The chunk ID of the citation. + description: citation information for a chat completions response message. + chatCompletionMessageToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + $ref: '#/components/schemas/toolCallType' + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: The function that the model called. + toolCallType: + enum: + - function + type: string + description: 'The type of the tool call, in this case `function`.' + x-ms-enum: + name: ToolCallType + modelAsString: true + values: + - value: function + description: The tool call type is function. + chatCompletionRequestMessageTool: + required: + - tool_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + tool_call_id: + type: string + description: Tool call that this message is responding to. + content: + type: string + description: The contents of the message. + nullable: true + nullable: true + chatCompletionRequestMessageFunction: + required: + - function_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + name: + type: string + description: The contents of the message. + content: + type: string + description: The contents of the message. + nullable: true + description: Deprecated. Message that represents a function. + nullable: true + createChatCompletionResponse: + required: + - id + - object + - created + - model + - choices + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsResponseCommon' + - properties: + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionChoiceCommon' + - properties: + message: + $ref: '#/components/schemas/chatCompletionResponseMessage' + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + chatCompletionResponseMessage: + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionResponseMessageRole' + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + function_call: + $ref: '#/components/schemas/chatCompletionFunctionCall' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + description: A chat completion message generated by the model. + chatCompletionResponseMessageRole: + enum: + - assistant + type: string + description: The role of the author of the response message. + chatCompletionToolChoiceOption: + oneOf: + - enum: + - none + - auto + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - $ref: '#/components/schemas/chatCompletionNamedToolChoice' + description: 'Controls which (if any) function is called by the model. `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function.' + chatCompletionNamedToolChoice: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific function. + chatCompletionFunctionCall: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + chatCompletionsResponseCommon: + required: + - id + - object + - created + - model + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. + object: + $ref: '#/components/schemas/chatCompletionResponseObject' + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + format: unixtime + model: + type: string + description: The model used for the chat completion. + usage: + $ref: '#/components/schemas/completionUsage' + system_fingerprint: + type: string + description: Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + chatCompletionResponseObject: + enum: + - chat.completion + type: string + description: The object type. + x-ms-enum: + name: ChatCompletionResponseObject + modelAsString: true + values: + - value: chat.completion + description: The object type is chat completion. + completionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + description: Usage statistics for the completion request. + chatCompletionTool: + required: + - type + - function + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionToolType' + function: + required: + - name + - parameters + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + chatCompletionToolType: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-ms-enum: + name: ChatCompletionToolType + modelAsString: true + values: + - value: function + description: The tool type is function. + chatCompletionChoiceCommon: + type: object + properties: + index: + type: integer + finish_reason: + type: string + createTranslationRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + description: Translation request. + audioResponse: + required: + - text + type: object + properties: + text: + type: string + description: Translated or transcribed text. + description: Translation or transcription response when response_format was json + audioVerboseResponse: + required: + - text + type: object + allOf: + - $ref: '#/components/schemas/audioResponse' + - properties: + task: + enum: + - transcribe + - translate + type: string + description: Type of audio task. + x-ms-enum: + modelAsString: true + language: + type: string + description: Language. + duration: + type: number + description: Duration. + segments: + type: array + items: + $ref: '#/components/schemas/audioSegment' + description: Translation or transcription response when response_format was verbose_json + audioResponseFormat: + title: AudioResponseFormat + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: Defines the format of the output. + x-ms-enum: + modelAsString: true + createTranscriptionRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + description: Transcription request. + audioSegment: + type: object + properties: + id: + type: integer + description: Segment identifier. + seek: + type: number + description: Offset of the segment. + start: + type: number + description: Segment start offset. + end: + type: number + description: Segment end offset. + text: + type: string + description: Segment text. + tokens: + type: array + items: + type: number + description: Tokens of the text. + temperature: + type: number + description: Temperature. + avg_logprob: + type: number + description: Average log probability. + compression_ratio: + type: number + description: Compression ratio. + no_speech_prob: + type: number + description: Probability of 'no speech'. + description: Transcription or translation segment. + imageQuality: + enum: + - standard + - hd + type: string + description: The quality of the image that will be generated. + default: standard + x-ms-enum: + name: Quality + modelAsString: true + values: + - value: standard + description: Standard quality creates images with standard quality. + name: Standard + - value: hd + description: HD quality creates images with finer details and greater consistency across the image. + name: HD + imagesResponseFormat: + enum: + - url + - b64_json + type: string + description: The format in which the generated images are returned. + default: url + x-ms-enum: + name: ImagesResponseFormat + modelAsString: true + values: + - value: url + description: The URL that provides temporary access to download the generated images. + name: Url + - value: b64_json + description: The generated images are returned as base64 encoded string. + name: Base64Json + imageSize: + enum: + - 1792x1024 + - 1024x1792 + - 1024x1024 + type: string + description: The size of the generated images. + default: 1024x1024 + x-ms-enum: + name: Size + modelAsString: true + values: + - value: 1792x1024 + description: The desired size of the generated image is 1792x1024 pixels. + name: Size1792x1024 + - value: 1024x1792 + description: The desired size of the generated image is 1024x1792 pixels. + name: Size1024x1792 + - value: 1024x1024 + description: The desired size of the generated image is 1024x1024 pixels. + name: Size1024x1024 + imageStyle: + enum: + - vivid + - natural + type: string + description: The style of the generated images. + default: vivid + x-ms-enum: + name: Style + modelAsString: true + values: + - value: vivid + description: Vivid creates images that are hyper-realistic and dramatic. + name: Vivid + - value: natural + description: Natural creates images that are more natural and less hyper-realistic. + name: Natural + imageGenerationsRequest: + required: + - prompt + type: object + properties: + prompt: + minLength: 1 + type: string + description: A text description of the desired image(s). The maximum length is 4000 characters. + format: string + example: a corgi in a field + n: + maximum: 1.0 + minimum: 1.0 + type: integer + description: The number of images to generate. + default: 1 + size: + $ref: '#/components/schemas/imageSize' + response_format: + $ref: '#/components/schemas/imagesResponseFormat' + user: + type: string + description: 'A unique identifier representing your end-user, which can help to monitor and detect abuse.' + format: string + example: user123456 + quality: + $ref: '#/components/schemas/imageQuality' + style: + $ref: '#/components/schemas/imageStyle' + generateImagesResponse: + required: + - created + - data + type: object + properties: + created: + type: integer + description: The unix timestamp when the operation was created. + format: unixtime + example: '1676540381' + data: + type: array + items: + $ref: '#/components/schemas/imageResult' + description: 'The result data of the operation, if successful' + imageResult: + type: object + properties: + url: + type: string + description: The image url. + example: https://www.contoso.com + b64_json: + type: string + description: The base64 encoded image + content_filter_results: + $ref: '#/components/schemas/dalleContentFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + prompt_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + description: 'The image url or encoded image if successful, and an error otherwise.' + securitySchemes: + apiKeyHeader: + type: apiKey + name: Ocp-Apim-Subscription-Key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-05-01-preview.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-05-01-preview.yaml new file mode 100644 index 000000000..f2c15c4e7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-05-01-preview.yaml @@ -0,0 +1,8635 @@ +openapi: 3.0.1 +info: + title: Azure OpenAI API + description: 'Azure OpenAI APIs for completions, image, audio and search' + version: '2024-05-01-preview' +servers: + - url: https://REPLACED-BY-POLICY/openai +paths: + '/deployments/{deployment-id}/completions': + post: + summary: 'Creates a completion for the provided prompt, parameters and chosen model.' + description: 'Creates a completion for the provided prompt, parameters and chosen model.' + operationId: Completions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the model which was deployed. + example: davinci + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + type: object + properties: + prompt: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - type: array + items: + type: string + default: '' + example: This is a test. + description: Array size minimum of 1 and maximum of 2048 + description: "The prompt(s) to generate completions for, encoded as a string or array of strings.\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. Maximum allowed size of string list is 2048." + max_tokens: + type: integer + description: 'The token count of your prompt plus max_tokens cannot exceed the model''s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096). Has minimum of 0.' + default: 16 + nullable: true + example: 16 + temperature: + type: number + description: "What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.\nWe generally recommend altering this or top_p but not both." + default: 1 + nullable: true + example: 1 + top_p: + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or temperature but not both." + default: 1 + nullable: true + example: 1 + logit_bias: + type: object + description: 'Defaults to null. Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. As an example, you can pass {"50256" : -100} to prevent the <|endoftext|> token from being generated.' + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse' + n: + type: integer + description: "How many completions to generate for each prompt. Minimum of 1 and maximum of 128 allowed.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.' + default: false + nullable: true + logprobs: + type: integer + description: "Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.\nMinimum of 0 and maximum of 5 allowed." + default: '' + nullable: true + suffix: + type: string + description: The suffix that comes after a completion of inserted text. + nullable: true + echo: + type: boolean + description: Echo back the prompt in addition to the completion + default: false + nullable: true + stop: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: "\n" + - type: array + items: + type: string + example: "\n" + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + completion_config: + type: string + nullable: true + presence_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + best_of: + type: integer + description: "Generates best_of completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\nWhen used with n, best_of controls the number of candidate completions and n specifies how many to return - best_of must be greater than n.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop. Has maximum value of 128." + example: + prompt: "Negate the following sentence.The price for bubblegum increased on thursday.\n\n Negated Sentence:" + max_tokens: 50 + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + object: + type: string + created: + type: integer + model: + type: string + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + properties: + text: + type: string + index: + type: integer + logprobs: + type: object + properties: + tokens: + type: array + items: + type: string + token_logprobs: + type: array + items: + type: number + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + text_offset: + type: array + items: + type: integer + nullable: true + finish_reason: + type: string + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + usage: + required: + - prompt_tokens + - total_tokens + - completion_tokens + type: object + properties: + completion_tokens: + type: number + format: int32 + prompt_tokens: + type: number + format: int32 + total_tokens: + type: number + format: int32 + example: + model: davinci + object: text_completion + id: cmpl-4509KAos68kxOqpE2uYGw81j6m7uo + created: 1637097562 + choices: + - index: 0 + text: The price for bubblegum decreased on thursday. + logprobs: '' + finish_reason: stop + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/embeddings': + post: + summary: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + description: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + operationId: embeddings_create + parameters: + - name: deployment-id + in: path + description: The deployment id of the model which was deployed. + required: true + schema: + enum: + - embedding + type: string + default: embedding + example: ada-search-index-v1 + - name: api-version + in: query + required: true + schema: + enum: + - 2024-05-01-preview + type: string + description: api version + default: 2024-05-01-preview + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + required: + - input + type: object + properties: + input: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - maxItems: 2048 + minItems: 1 + type: array + items: + minLength: 1 + type: string + example: This is a test. + description: "Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length.\nUnless you are embedding code, we suggest replacing newlines (\\n) in your input with a single space, as we have observed inferior results when newlines are present." + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse.' + input_type: + type: string + description: input type of embedding search to use + example: query + encoding_format: + type: string + description: The format to return the embeddings in. Can be either `float` or `base64`. Defaults to `float`. + nullable: true + example: base64 + dimensions: + type: integer + description: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. + nullable: true + example: 1 + example: + input: APIM love OpenAI + responses: + '200': + description: OK + content: + application/json: + schema: + required: + - object + - model + - data + - usage + type: object + properties: + object: + type: string + model: + type: string + data: + type: array + items: + required: + - index + - object + - embedding + type: object + properties: + index: + type: integer + object: + type: string + embedding: + type: array + items: + type: number + usage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + total_tokens: + type: integer + example: + object: string + model: string + data: + - index: 0 + object: string + embedding: + - 0 + usage: + prompt_tokens: 0 + total_tokens: 0 + '/deployments/{deployment-id}/chat/completions': + post: + summary: Creates a completion for the chat message + description: Creates a completion for the chat message + operationId: ChatCompletions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - gpt-4o + - chat + type: string + description: Deployment id of the model which was deployed. + default: gpt-4o + - name: api-version + in: query + required: true + schema: + enum: + - 2024-05-01-preview + type: string + description: api version + default: 2024-05-01-preview + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionRequest' + example: + model: chat + messages: + - role: system + content: You are a helpful assistant that responds in Markdown. Help me with my math homework! + - role: user + content: How to calculate the distance between earth and moon? + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionResponse' + example: + id: string + object: chat.completion + created: 0 + model: string + usage: + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + system_fingerprint: string + prompt_filter_results: + - prompt_index: 0 + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + choices: + - index: 0 + finish_reason: string + message: + role: assistant + content: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + function_call: + name: string + arguments: string + context: + citations: + - content: string + title: string + url: string + filepath: string + chunk_id: string + intent: string + all_retrieved_documents: + - content: string + title: string + url: string + filepath: string + chunk_id: string + search_queries: + - string + data_source_index: 0 + original_search_score: 0 + rerank_score: 0 + filter_reason: score + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + protected_material_text: + filtered: true + detected: true + protected_material_code: + filtered: true + detected: true + citation: + URL: string + license: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + indirect_attack: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/audio/transcriptions': + post: + summary: Transcribes audio into the input language. + description: Transcribes audio into the input language. + operationId: Transcriptions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + format: binary + prompt: + type: string + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + default: 0 + language: + type: string + 'timestamp_granularities[]': + type: array + default: "[\n \"segment\"\n]" + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + example: '' + '/deployments/{deployment-id}/audio/translations': + post: + summary: Transcribes and translates input audio into English text. + description: Transcribes and translates input audio into English text. + operationId: Translations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model which was deployed. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + format: binary + prompt: + type: string + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + default: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + example: '' + '/deployments/{deployment-id}/audio/speech': + post: + summary: Generates audio from the input text. + description: Generates audio from the input text. + operationId: Speech_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - tts-hd + type: string + description: Deployment id of the tts model which was deployed. + default: tts-hd + example: tts-hd + - name: api-version + in: query + required: true + schema: + enum: + - 2024-05-01-preview + type: string + description: api version + default: 2024-05-01-preview + example: 2024-05-01-preview + requestBody: + content: + multipart/form-data: + example: + model: tts-1-hd + input: I'm excited to try text to speech. + voice: nova + schema: + properties: + input: + type: string + voice: + enum: + - alloy + - echo + - fable + - onyx + - nova + - shimmer + type: string + response_format: + enum: + - mp3 + - opus + - aac + - flac + - wav + - pcm + type: string + speed: + type: number + default: 1 + responses: + '200': + description: OK + content: + application/octet-stream: + schema: + type: string + format: binary + examples: + default: + value: + '/deployments/{deployment-id}/images/generations': + post: + summary: Generates a batch of images from a text caption on a given DALLE model deployment + description: Generates a batch of images from a text caption on a given DALLE model deployment + operationId: ImageGenerations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - dall-e-3 + type: string + description: Deployment id of the dalle model which was deployed. + default: dall-e-3 + example: dalle-deployment + - name: api-version + in: query + required: true + schema: + enum: + - 2024-05-01-preview + type: string + description: api version + default: 2024-05-01-preview + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/imageGenerationsRequest' + example: + prompt: a corgi in a field + n: 1 + size: 1024x1024 + response_format: url + user: user123456 + quality: standard + style: vivid + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + example: + created: '1676540381' + data: + - url: https://www.contoso.com + b64_json: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + revised_prompt: string + prompt_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + '400': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + '500': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + custom_blocklists: + filtered: true + details: + - filtered: true + id: string + revised_prompt: string + code: string + message: string + /assistants: + get: + tags: + - Assistants + summary: Returns a list of assistants. + description: Returns a list of assistants. + operationId: List_Assistants + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListAssistantsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listAssistantsResponse' + example: + object: list + data: + - id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + first_id: asst_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: asst_QLoItBbqwyAJEzlTy4y9kOMM + has_more: false + post: + tags: + - Assistants + summary: Create an assistant with a model and instructions. + description: Create an assistant with a model and instructions. + operationId: Create_Assistant + parameters: + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createAssistantRequest' + example: + model: { } + name: string + description: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + metadata: { } + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + '/assistants/{assistant_id}': + get: + tags: + - Assistants + summary: Retrieves an assistant. + description: Retrieves an assistant. + operationId: Get_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + post: + tags: + - Assistant + summary: Modifies an assistant. + description: Modifies an assistant. + operationId: Modify_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyAssistantRequest' + example: + model: { } + name: string + description: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/assistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + delete: + tags: + - Assistants + summary: Delete an assistant. + description: Delete an assistant. + operationId: Delete_Assistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to delete. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteAssistantResponse' + example: + id: string + deleted: true + object: assistant.deleted + /threads: + post: + tags: + - Assistants + summary: Create a thread. + description: Create a thread. + operationId: Create_Thread + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createThreadRequest' + example: + messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + metadata: { } + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + '/threads/{thread_id}': + get: + tags: + - Assistants + summary: Retrieves a thread. + description: Retrieves a thread. + operationId: Get_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + post: + tags: + - Assistants + summary: Modifies a thread. + description: Modifies a thread. + operationId: Modify_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to modify. Only the `metadata` can be modified. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyThreadRequest' + example: + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/threadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + delete: + tags: + - Assistants + summary: Delete a thread. + description: Delete a thread. + operationId: Delete_Thread + parameters: + - name: thread_id + in: path + description: The ID of the thread to delete. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteThreadResponse' + example: + id: string + deleted: true + object: thread.deleted + '/threads/{thread_id}/messages': + get: + tags: + - Assistants + summary: Returns a list of messages for a given thread. + description: Returns a list of messages for a given thread. + operationId: List_Messages + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) the messages belong to.' + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListMessagesOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: run_id + in: query + description: "Filter messages by the run ID that generated them.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listMessagesResponse' + example: + object: list + data: + - id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + first_id: msg_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: msg_QLoItBbqwyAJEzlTy4y9kOMM + has_more: false + post: + tags: + - Assistants + summary: Create a message. + description: Create a message. + operationId: Create_Message + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to create a message for.' + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createMessageRequest' + example: + role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + '/threads/{thread_id}/messages/{message_id}': + get: + tags: + - Assistants + summary: Retrieve a message. + description: Retrieve a message. + operationId: Get_Message + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to which this message belongs.' + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + post: + tags: + - Assistants + summary: Modifies a message. + description: Modifies a message. + operationId: Modify_Message + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this message belongs. + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyMessageRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/messageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + /threads/runs: + post: + tags: + - Assistants + summary: Create a thread and run it in one request. + description: Create a thread and run it in one request. + operationId: Create_Thread_And_Run + parameters: + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createThreadAndRunRequest' + example: + assistant_id: string + thread: + messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + metadata: { } + metadata: { } + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + stream: true + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + '/threads/{thread_id}/runs': + get: + tags: + - Assistants + summary: Returns a list of runs belonging to a thread. + description: Returns a list of runs belonging to a thread. + operationId: List_Runs + parameters: + - name: thread_id + in: path + description: The ID of the thread the run belongs to. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListRunsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listRunsResponse' + example: + object: list + data: + - id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + first_id: run_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: run_QLoItBbqwyAJEzlTy4y9kOMM + has_more: false + post: + tags: + - Assistants + summary: Create a run. + description: Create a run. + operationId: Create_Run + parameters: + - name: thread_id + in: path + description: The ID of the thread to run. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createRunRequest' + example: + assistant_id: string + model: string + instructions: string + additional_instructions: string + additional_messages: + - role: user + content: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tools: + - type: code_interpreter + metadata: { } + temperature: 1 + top_p: 1 + stream: true + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + '/threads/{thread_id}/runs/{run_id}': + get: + tags: + - Assistants + summary: Retrieves a run. + description: Retrieves a run. + operationId: Get_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + post: + tags: + - Assistants + summary: Modifies a run. + description: Modifies a run. + operationId: Modify_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to modify. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/modifyRunRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + '/threads/{thread_id}/runs/{run_id}/submit_tool_outputs': + post: + tags: + - Assistants + summary: "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n" + description: "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n" + operationId: Submit_Tool_Outputs_To_Run + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) to which this run belongs.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run that requires the tool output submission. + required: true + schema: + type: string + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/submitToolOutputsRunRequest' + example: + tool_outputs: + - tool_call_id: string + output: string + stream: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + '/threads/{thread_id}/runs/{run_id}/cancel': + post: + tags: + - Assistants + summary: Cancels a run that is `in_progress`. + description: Cancels a run that is `in_progress`. + operationId: Cancel_Run + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this run belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to cancel. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + response_format: { } + '/threads/{thread_id}/runs/{run_id}/steps': + get: + tags: + - Assistants + summary: Returns a list of run steps belonging to a run. + description: Returns a list of run steps belonging to a run. + operationId: List_Run_Steps + parameters: + - name: thread_id + in: path + description: The ID of the thread the run and run steps belong to. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run the run steps belong to. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListRunStepsOrder + modelAsString: true + values: + - value: asc + description: Order results in ascending order + - value: desc + description: Order results in descending order + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listRunStepsResponse' + example: + object: list + data: + - id: string + object: assistant.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + first_id: step_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: step_QLoItBbqwyAJEzlTy4y9kOMM + has_more: false + '/threads/{thread_id}/runs/{run_id}/steps/{step_id}': + get: + tags: + - Assistants + summary: Retrieves a run step. + description: Retrieves a run step. + operationId: Get_Run_Step + parameters: + - name: thread_id + in: path + description: The ID of the thread to which the run and run step belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to which the run step belongs. + required: true + schema: + type: string + - name: step_id + in: path + description: The ID of the run step to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/runStepObject' + example: + id: string + object: assistant.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + /vector_stores: + get: + tags: + - Vector Stores + summary: Returns a list of vector stores. + description: Returns a list of vector stores. + operationId: List_Vector_Stores + parameters: + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoresOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoresResponse' + example: + object: list + data: + - id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + first_id: vs_abc123 + last_id: vs_abc456 + has_more: false + post: + tags: + - Vector Stores + summary: Create a vector store. + description: Create a vector store. + operationId: Create_Vector_Store + parameters: + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreRequest' + example: + file_ids: + - string + name: string + expires_after: + anchor: last_active_at + days: 0 + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + '/vector_stores/{vector_store_id}': + get: + tags: + - Vector Stores + summary: Retrieves a vector store. + description: Retrieves a vector store. + operationId: Get_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to retrieve. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + post: + tags: + - Vector Stores + summary: Modifies a vector store. + description: Modifies a vector store. + operationId: Modify_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to modify. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/updateVectorStoreRequest' + example: + name: string + expires_after: + anchor: last_active_at + days: 0 + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + delete: + tags: + - Vector Stores + summary: Delete a vector store. + description: Delete a vector store. + operationId: Delete_Vector_Store + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteVectorStoreResponse' + example: + id: string + deleted: true + object: vector_store.deleted + '/vector_stores/{vector_store_id}/files': + get: + tags: + - Vector Stores + summary: Returns a list of vector store files. + description: Returns a list of vector store files. + operationId: List_Vector_Store_Files + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoreFilesOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: 'Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.' + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + x-ms-enum: + name: ListVectorStoreFilesFilter + modelAsString: true + values: + - value: in_progress + - value: completed + - value: failed + - value: cancelled + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: internal_error + message: string + first_id: file-abc123 + last_id: file-abc456 + has_more: false + post: + tags: + - Vector Stores + summary: 'Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).' + description: 'Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).' + operationId: Create_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File.\n" + required: true + schema: + type: string + example: vs_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreFileRequest' + example: + file_id: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: internal_error + message: string + '/vector_stores/{vector_store_id}/files/{file_id}': + get: + tags: + - Vector Stores + summary: Retrieves a vector store file. + description: Retrieves a vector store file. + operationId: Get_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file being retrieved. + required: true + schema: + type: string + example: file-abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: internal_error + message: string + delete: + tags: + - Vector Stores + summary: 'Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.' + description: 'Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.' + operationId: Delete_Vector_Store_File + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + - name: file_id + in: path + description: The ID of the file to delete. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/deleteVectorStoreFileResponse' + example: + id: string + deleted: true + object: vector_store.file.deleted + '/vector_stores/{vector_store_id}/file_batches': + post: + tags: + - Vector Stores + summary: Create a vector store file batch. + description: Create a vector store file batch. + operationId: Create_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File Batch.\n" + required: true + schema: + type: string + example: vs_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createVectorStoreFileBatchRequest' + example: + file_ids: + - string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}': + get: + tags: + - Vector Stores + summary: Retrieves a vector store file batch. + description: Retrieves a vector store file batch. + operationId: Get_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: batch_id + in: path + description: The ID of the file batch being retrieved. + required: true + schema: + type: string + example: vsfb_abc123 + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel': + post: + tags: + - Vector Stores + summary: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + description: Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible. + operationId: Cancel_Vector_Store_File_Batch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch to cancel. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/vectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/files': + get: + tags: + - Vector Stores + summary: Returns a list of vector store files in a batch. + description: Returns a list of vector store files in a batch. + operationId: List_Vector_Store_File_Batch_Files + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch that the files belong to. + required: true + schema: + type: string + - name: api-version + in: query + description: api version + required: true + schema: + type: string + example: 2024-05-01-preview + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + x-ms-enum: + name: ListVectorStoreFilesOrder + modelAsString: true + values: + - value: asc + - value: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: 'Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.' + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + x-ms-enum: + name: ListVectorStoreFilesFilter + modelAsString: true + values: + - value: in_progress + - value: completed + - value: failed + - value: cancelled + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/listVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: internal_error + message: string + first_id: file-abc123 + last_id: file-abc456 + has_more: false +components: + schemas: + errorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/error' + errorBase: + type: object + properties: + code: + type: string + message: + type: string + error: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/innerError' + innerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Inner error with additional details. + innerErrorCode: + enum: + - ResponsibleAIPolicyViolation + type: string + description: Error codes for the inner error object. + x-ms-enum: + name: InnerErrorCode + modelAsString: true + values: + - value: ResponsibleAIPolicyViolation + description: The prompt violated one of more content filter rules. + dalleErrorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/dalleError' + dalleError: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/dalleInnerError' + dalleInnerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + description: Inner error with additional details. + contentFilterResultBase: + required: + - filtered + type: object + properties: + filtered: + type: boolean + contentFilterSeverityResult: + required: + - severity + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + severity: + enum: + - safe + - low + - medium + - high + type: string + x-ms-enum: + name: ContentFilterSeverity + modelAsString: true + values: + - value: safe + description: General content or related content in generic or non-harmful contexts. + - value: low + description: Harmful content at a low intensity and risk level. + - value: medium + description: Harmful content at a medium intensity and risk level. + - value: high + description: Harmful content at a high intensity and risk level. + contentFilterDetectedResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + detected: + type: boolean + contentFilterDetectedWithCitationResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + citation: + type: object + properties: + URL: + type: string + license: + type: string + contentFilterIdResult: + required: + - id + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + id: + type: string + contentFilterResultsBase: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + custom_blocklists: + $ref: '#/components/schemas/contentFilterDetailedResults' + error: + $ref: '#/components/schemas/errorBase' + description: Information about the content filtering results. + contentFilterPromptResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + indirect_attack: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterChoiceResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + protected_material_text: + $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + protected_material_code: + $ref: '#/components/schemas/contentFilterDetectedWithCitationResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterDetailedResults: + required: + - filtered + - details + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + details: + type: array + items: + $ref: '#/components/schemas/contentFilterIdResult' + description: Content filtering results with a detail of content filter ids for the filtered segments. + promptFilterResult: + type: object + properties: + prompt_index: + type: integer + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Content filtering results for a single prompt in the request. + promptFilterResults: + type: array + items: + $ref: '#/components/schemas/promptFilterResult' + description: 'Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.' + dalleContentFilterResults: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + description: Information about the content filtering results. + dalleFilterResults: + type: object + allOf: + - $ref: '#/components/schemas/dalleContentFilterResults' + - properties: + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + custom_blocklists: + $ref: '#/components/schemas/contentFilterDetailedResults' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + chatCompletionsRequestCommon: + type: object + properties: + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or `temperature` but not both." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.' + default: false + nullable: true + stop: + oneOf: + - type: string + nullable: true + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. + default: '' + max_tokens: + type: integer + description: 'The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens).' + default: 4096 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + logit_bias: + type: object + description: 'Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.' + nullable: true + user: + type: string + description: 'A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.' + example: user-1234 + createChatCompletionRequest: + required: + - messages + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsRequestCommon' + - properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessage' + description: 'A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).' + data_sources: + type: array + items: + $ref: '#/components/schemas/azureChatExtensionConfiguration' + description: " The configuration entries for Azure OpenAI chat extensions that use them.\n This additional specification is only compatible with Azure OpenAI." + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: How many chat completion choices to generate for each input message. + default: 1 + nullable: true + example: 1 + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: 'If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.' + default: 0 + nullable: true + example: 1 + logprobs: + type: boolean + description: 'Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of `message`. This option is currently not available on the `gpt-4-vision-preview` model.' + default: false + nullable: true + top_logprobs: + maximum: 5.0 + minimum: 0.0 + type: integer + description: 'An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to `true` if this parameter is used.' + nullable: true + response_format: + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionResponseFormat' + description: An object specifying the format that the model must output. Used to enable JSON mode. + tools: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionTool' + description: 'A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.' + tool_choice: + $ref: '#/components/schemas/chatCompletionToolChoiceOption' + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionFunction' + description: Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for. + function_call: + oneOf: + - enum: + - none + - auto + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: 'Specifying a particular function via `{"name": "my_function"}` forces the model to call that function.' + description: 'Deprecated in favor of `tool_choice`. Controls how the model responds to function calls. "none" means the model does not call a function, and responds to the end-user. "auto" means the model can pick between an end-user or calling a function. Specifying a particular function via `{"name":\ "my_function"}` forces the model to call that function. "none" is the default when no functions are present. "auto" is the default if functions are present.' + chatCompletionResponseFormat: + enum: + - text + - json_object + type: string + description: Setting to `json_object` enables JSON mode. This guarantees that the message the model generates is valid JSON. + default: text + nullable: true + example: json_object + x-ms-enum: + name: ChatCompletionResponseFormat + modelAsString: true + values: + - value: text + description: Response format is a plain text string. + - value: json_object + description: Response format is a JSON object. + chatCompletionFunction: + required: + - name + type: object + properties: + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + description: + type: string + description: The description of what the function does. + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + chatCompletionFunctionParameters: + type: object + description: 'The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.' + chatCompletionRequestMessage: + required: + - role + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionRequestMessageRole' + discriminator: + propertyName: role + mapping: + system: '#/components/schemas/chatCompletionRequestMessageSystem' + user: '#/components/schemas/chatCompletionRequestMessageUser' + assistant: '#/components/schemas/chatCompletionRequestMessageAssistant' + tool: '#/components/schemas/chatCompletionRequestMessageTool' + function: '#/components/schemas/chatCompletionRequestMessageFunction' + chatCompletionRequestMessageRole: + enum: + - system + - user + - assistant + - tool + - function + type: string + description: The role of the messages author. + x-ms-enum: + name: ChatCompletionRequestMessageRole + modelAsString: true + values: + - value: system + description: The message author role is system. + - value: user + description: The message author role is user. + - value: assistant + description: The message author role is assistant. + - value: tool + description: The message author role is tool. + - value: function + description: Deprecated. The message author role is function. + chatCompletionRequestMessageSystem: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + chatCompletionRequestMessageUser: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + oneOf: + - type: string + description: The contents of the message. + - minimum: 1.0 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + description: 'An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4-visual-preview` model.' + nullable: true + chatCompletionRequestMessageContentPart: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPartType' + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/chatCompletionRequestMessageContentPartText' + image_url: '#/components/schemas/chatCompletionRequestMessageContentPartImage' + chatCompletionRequestMessageContentPartType: + enum: + - text + - image_url + type: string + description: The type of the content part. + x-ms-enum: + name: ChatCompletionRequestMessageContentPartType + modelAsString: true + values: + - value: text + description: The content part type is text. + - value: image_url + description: The content part type is image_url. + chatCompletionRequestMessageContentPartText: + required: + - text + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + text: + type: string + description: The text content. + chatCompletionRequestMessageContentPartImage: + required: + - url + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + $ref: '#/components/schemas/imageDetailLevel' + imageDetailLevel: + enum: + - auto + - low + - high + type: string + description: Specifies the detail level of the image. + default: auto + x-ms-enum: + name: ImageDetailLevel + modelAsString: true + values: + - value: auto + description: The image detail level is auto. + - value: low + description: The image detail level is low. + - value: high + description: The image detail level is high. + chatCompletionRequestMessageAssistant: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + azureChatExtensionConfiguration: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/azureChatExtensionType' + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + discriminator: + propertyName: type + mapping: + azure_search: '#/components/schemas/azureSearchChatExtensionConfiguration' + azure_ml_index: '#/components/schemas/azureMachineLearningIndexChatExtensionConfiguration' + azure_cosmos_db: '#/components/schemas/azureCosmosDBChatExtensionConfiguration' + elasticsearch: '#/components/schemas/elasticsearchChatExtensionConfiguration' + pinecone: '#/components/schemas/pineconeChatExtensionConfiguration' + azureChatExtensionType: + enum: + - azure_search + - azure_ml_index + - azure_cosmos_db + - elasticsearch + - pinecone + type: string + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + x-ms-enum: + name: AzureChatExtensionType + modelAsString: true + values: + - name: azureSearch + value: azure_search + description: Represents the use of Azure Search as an Azure OpenAI chat extension. + - name: azureMachineLearningIndex + value: azure_ml_index + description: Represents the use of Azure Machine Learning index as an Azure OpenAI chat extension. + - name: azureCosmosDB + value: azure_cosmos_db + description: Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + - name: elasticsearch + value: elasticsearch + description: Represents the use of Elasticsearch® index as an Azure OpenAI chat extension. + - name: pinecone + value: pinecone + description: Represents the use of Pinecone index as an Azure OpenAI chat extension. + azureSearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureSearchChatExtensionParameters' + description: "A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_search + azureSearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + endpoint: + type: string + description: The absolute endpoint path for the Azure Search resource to use. + format: uri + index_name: + type: string + description: The name of the index to use as available in the referenced Azure Search resource. + fields_mapping: + $ref: '#/components/schemas/azureSearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/azureSearchQueryType' + semantic_configuration: + type: string + description: The additional semantic configuration for the query. + filter: + type: string + description: Search filter. + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters for Azure Search when used as an Azure OpenAI chat extension. + azureSearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + image_vector_fields: + type: array + items: + type: string + description: The names of fields that represent image vector data. + description: Optional settings to control how fields are processed when using a configured Azure Search resource. + azureSearchQueryType: + enum: + - simple + - semantic + - vector + - vector_simple_hybrid + - vector_semantic_hybrid + type: string + description: The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: AzureSearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: semantic + value: semantic + description: Represents the semantic query parser for advanced semantic modeling. + - name: vector + value: vector + description: Represents vector search over computed data. + - name: vectorSimpleHybrid + value: vector_simple_hybrid + description: Represents a combination of the simple query strategy with vector data. + - name: vectorSemanticHybrid + value: vector_semantic_hybrid + description: Represents a combination of semantic search and vector data querying. + azureMachineLearningIndexChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureMachineLearningIndexChatExtensionParameters' + description: "A specific representation of configurable options for Azure Machine Learning vector index when using it as an Azure\nOpenAI chat extension." + x-ms-discriminator-value: azure_ml_index + azureMachineLearningIndexChatExtensionParameters: + required: + - authentication + - name + - project_resource_id + - version + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + project_resource_id: + type: string + description: The resource ID of the Azure Machine Learning project. + name: + type: string + description: The Azure Machine Learning vector index name. + version: + type: string + description: The version of the Azure Machine Learning vector index. + filter: + type: string + description: Search filter. Only supported if the Azure Machine Learning vector index is of type AzureSearch. + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters for the Azure Machine Learning vector index chat extension. + azureCosmosDBChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureCosmosDBChatExtensionParameters' + description: "A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_cosmos_db + azureCosmosDBChatExtensionParameters: + required: + - authentication + - container_name + - database_name + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + database_name: + type: string + description: The MongoDB vCore database name to use with Azure Cosmos DB. + container_name: + type: string + description: The name of the Azure Cosmos DB resource container. + index_name: + type: string + description: The MongoDB vCore index name to use with Azure Cosmos DB. + fields_mapping: + $ref: '#/components/schemas/azureCosmosDBFieldMappingOptions' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\nMongoDB vCore." + azureCosmosDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource. + elasticsearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/elasticsearchChatExtensionParameters' + description: "A specific representation of configurable options for Elasticsearch when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: elasticsearch + elasticsearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + endpoint: + type: string + description: The endpoint of Elasticsearch®. + format: uri + index_name: + type: string + description: The index name of Elasticsearch®. + fields_mapping: + $ref: '#/components/schemas/elasticsearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/elasticsearchQueryType' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + - $ref: '#/components/schemas/onYourDataModelIdVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: 'Parameters to use when configuring Elasticsearch® as an Azure OpenAI chat extension. ' + elasticsearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Elasticsearch® resource. + elasticsearchQueryType: + enum: + - simple + - vector + type: string + description: The type of Elasticsearch® retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: ElasticsearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: vector + value: vector + description: Represents vector search over computed data. + pineconeChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/pineconeChatExtensionParameters' + description: "A specific representation of configurable options for Pinecone when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: pinecone + pineconeChatExtensionParameters: + required: + - authentication + - embedding_dependency + - environment + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + max_search_queries: + type: integer + description: 'The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.' + format: int32 + allow_partial_result: + type: boolean + description: 'If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails.' + default: false + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + environment: + type: string + description: The environment name of Pinecone. + index_name: + type: string + description: The name of the Pinecone database index. + fields_mapping: + $ref: '#/components/schemas/pineconeFieldMappingOptions' + embedding_dependency: + $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + include_contexts: + type: array + items: + $ref: '#/components/schemas/onYourDataContextProperty' + description: 'The included properties of the output context. If not specified, the default value is `citations` and `intent`.' + description: Parameters for configuring Azure OpenAI Pinecone chat extensions. + pineconeFieldMappingOptions: + required: + - content_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + description: Optional settings to control how fields are processed when using a configured Pinecone resource. + onYourDataAuthenticationOptions: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataAuthenticationType' + description: The authentication options for Azure OpenAI On Your Data. + discriminator: + propertyName: type + mapping: + api_key: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + connection_string: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + key_and_key_id: '#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions' + encoded_api_key: '#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions' + access_token: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + system_assigned_managed_identity: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + user_assigned_managed_identity: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + onYourDataContextProperty: + enum: + - citations + - intent + - all_retrieved_documents + type: string + description: The context property. + x-ms-enum: + name: OnYourDataContextProperty + modelAsString: true + values: + - name: citations + value: citations + description: The `citations` property. + - name: intent + value: intent + description: The `intent` property. + - name: allRetrievedDocuments + value: all_retrieved_documents + description: The `all_retrieved_documents` property. + onYourDataAuthenticationType: + enum: + - api_key + - connection_string + - key_and_key_id + - encoded_api_key + - access_token + - system_assigned_managed_identity + - user_assigned_managed_identity + type: string + description: The authentication types supported with Azure OpenAI On Your Data. + x-ms-enum: + name: OnYourDataAuthenticationType + modelAsString: true + values: + - name: apiKey + value: api_key + description: Authentication via API key. + - name: connectionString + value: connection_string + description: Authentication via connection string. + - name: keyAndKeyId + value: key_and_key_id + description: Authentication via key and key ID pair. + - name: encodedApiKey + value: encoded_api_key + description: Authentication via encoded API key. + - name: accessToken + value: access_token + description: Authentication via access token. + - name: systemAssignedManagedIdentity + value: system_assigned_managed_identity + description: Authentication via system-assigned managed identity. + - name: userAssignedManagedIdentity + value: user_assigned_managed_identity + description: Authentication via user-assigned managed identity. + onYourDataApiKeyAuthenticationOptions: + required: + - key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an API key. + x-ms-discriminator-value: api_key + onYourDataConnectionStringAuthenticationOptions: + required: + - connection_string + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + connection_string: + type: string + description: The connection string to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a connection string. + x-ms-discriminator-value: connection_string + onYourDataKeyAndKeyIdAuthenticationOptions: + required: + - key + - key_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The Elasticsearch key to use for authentication. + key_id: + type: string + description: The Elasticsearch key ID to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an Elasticsearch key and key ID pair. + x-ms-discriminator-value: key_and_key_id + onYourDataEncodedApiKeyAuthenticationOptions: + required: + - encoded_api_key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + encoded_api_key: + type: string + description: The Elasticsearch encoded API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an Elasticsearch encoded API key. + x-ms-discriminator-value: encoded_api_key + onYourDataAccessTokenAuthenticationOptions: + required: + - access_token + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + access_token: + type: string + description: The access token to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using access token. + x-ms-discriminator-value: access_token + onYourDataSystemAssignedManagedIdentityAuthenticationOptions: + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + description: The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity. + x-ms-discriminator-value: system_assigned_managed_identity + onYourDataUserAssignedManagedIdentityAuthenticationOptions: + required: + - managed_identity_resource_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + managed_identity_resource_id: + type: string + description: The resource ID of the user-assigned managed identity to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity. + x-ms-discriminator-value: user_assigned_managed_identity + onYourDataVectorizationSource: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataVectorizationSourceType' + description: An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search. + discriminator: + propertyName: type + mapping: + endpoint: '#/components/schemas/onYourDataEndpointVectorizationSource' + deployment_name: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + model_id: '#/components/schemas/onYourDataModelIdVectorizationSource' + onYourDataVectorizationSourceType: + enum: + - endpoint + - deployment_name + - model_id + type: string + description: "Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\nvector search." + x-ms-enum: + name: OnYourDataVectorizationSourceType + modelAsString: true + values: + - name: endpoint + value: endpoint + description: Represents vectorization performed by public service calls to an Azure OpenAI embedding model. + - name: deploymentName + value: deployment_name + description: "Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\nsearch even in private networks." + - name: modelId + value: model_id + description: "Represents a specific embedding model ID as defined in the search service.\nCurrently only supported by Elasticsearch®." + onYourDataEndpointVectorizationSource: + required: + - authentication + - endpoint + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + endpoint: + type: string + description: Specifies the resource endpoint URL from which embeddings should be retrieved. It should be in the format of https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/embeddings. The api-version query parameter is not allowed. + format: uri + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataAccessTokenAuthenticationOptions' + dimensions: + type: integer + description: The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models. + format: int32 + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a public Azure OpenAI endpoint call for embeddings." + x-ms-discriminator-value: endpoint + onYourDataDeploymentNameVectorizationSource: + required: + - deployment_name + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + deployment_name: + type: string + description: 'Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.' + dimensions: + type: integer + description: The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models. + format: int32 + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non an internal embeddings model deployment name in the same Azure OpenAI resource." + x-ms-discriminator-value: deployment_name + onYourDataModelIdVectorizationSource: + required: + - model_id + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + model_id: + type: string + description: Specifies the model ID to use for vectorization. This model ID must be defined in the search service. + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a search service model ID. Currently only supported by Elasticsearch®." + x-ms-discriminator-value: model_id + azureChatExtensionsMessageContext: + type: object + properties: + citations: + type: array + items: + $ref: '#/components/schemas/citation' + description: 'The data source retrieval result, used to generate the assistant message in the response.' + x-ms-identifiers: [ ] + intent: + type: string + description: 'The detected intent from the chat history, used to pass to the next turn to carry over the context.' + all_retrieved_documents: + type: array + items: + $ref: '#/components/schemas/retrievedDocument' + description: All the retrieved documents. + description: " A representation of the additional context information available when Azure OpenAI chat extensions are involved\n in the generation of a corresponding chat completions response. This context information is only populated when\n using an Azure OpenAI request configured to use a matching extension." + citation: + required: + - content + type: object + properties: + content: + type: string + description: The content of the citation. + title: + type: string + description: The title of the citation. + url: + type: string + description: The URL of the citation. + filepath: + type: string + description: The file path of the citation. + chunk_id: + type: string + description: The chunk ID of the citation. + description: citation information for a chat completions response message. + retrievedDocument: + required: + - search_queries + - data_source_index + type: object + allOf: + - $ref: '#/components/schemas/citation' + - properties: + search_queries: + type: array + items: + type: string + description: The search queries used to retrieve the document. + data_source_index: + type: integer + description: The index of the data source. + format: int32 + original_search_score: + type: number + description: The original search score of the retrieved document. + format: double + rerank_score: + type: number + description: The rerank score of the retrieved document. + format: double + filter_reason: + $ref: '#/components/schemas/filterReason' + description: The retrieved document. + filterReason: + enum: + - score + - rerank + type: string + description: The filtering reason of the retrieved document. + x-ms-enum: + name: FilterReason + modelAsString: true + values: + - name: score + value: score + description: The document is filtered by original search score threshold defined by `strictness` configure. + - name: rerank + value: rerank + description: 'The document is not filtered by original search score threshold, but is filtered by rerank score and `top_n_documents` configure.' + chatCompletionMessageToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + $ref: '#/components/schemas/toolCallType' + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: The function that the model called. + toolCallType: + enum: + - function + type: string + description: 'The type of the tool call, in this case `function`.' + x-ms-enum: + name: ToolCallType + modelAsString: true + values: + - value: function + description: The tool call type is function. + chatCompletionRequestMessageTool: + required: + - tool_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + tool_call_id: + type: string + description: Tool call that this message is responding to. + content: + type: string + description: The contents of the message. + nullable: true + nullable: true + chatCompletionRequestMessageFunction: + required: + - function_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + name: + type: string + description: The contents of the message. + content: + type: string + description: The contents of the message. + nullable: true + description: Deprecated. Message that represents a function. + nullable: true + createChatCompletionResponse: + required: + - id + - object + - created + - model + - choices + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsResponseCommon' + - properties: + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionChoiceCommon' + - properties: + message: + $ref: '#/components/schemas/chatCompletionResponseMessage' + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + logprobs: + $ref: '#/components/schemas/chatCompletionChoiceLogProbs' + chatCompletionChoiceLogProbs: + required: + - content + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + chatCompletionTokenLogprob: + required: + - token + - logprob + - bytes + - top_logprobs + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + top_logprobs: + type: array + items: + required: + - token + - logprob + - bytes + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + description: 'List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned.' + chatCompletionResponseMessage: + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionResponseMessageRole' + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + function_call: + $ref: '#/components/schemas/chatCompletionFunctionCall' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + description: A chat completion message generated by the model. + chatCompletionResponseMessageRole: + enum: + - assistant + type: string + description: The role of the author of the response message. + chatCompletionToolChoiceOption: + oneOf: + - enum: + - none + - auto + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - $ref: '#/components/schemas/chatCompletionNamedToolChoice' + description: 'Controls which (if any) function is called by the model. `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function.' + chatCompletionNamedToolChoice: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific function. + chatCompletionFunctionCall: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + chatCompletionsResponseCommon: + required: + - id + - object + - created + - model + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. + object: + $ref: '#/components/schemas/chatCompletionResponseObject' + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + format: unixtime + model: + type: string + description: The model used for the chat completion. + usage: + $ref: '#/components/schemas/completionUsage' + system_fingerprint: + type: string + description: Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + chatCompletionResponseObject: + enum: + - chat.completion + type: string + description: The object type. + x-ms-enum: + name: ChatCompletionResponseObject + modelAsString: true + values: + - value: chat.completion + description: The object type is chat completion. + completionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + description: Usage statistics for the completion request. + chatCompletionTool: + required: + - type + - function + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionToolType' + function: + required: + - name + - parameters + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + FunctionParameters: + type: object + description: "The parameters the functions accepts, described as a JSON Schema object. See the guide](/docs/guides/text-generation/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. \n\nOmitting `parameters` defines a function with an empty parameter list." + functionObject: + required: + - name + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/FunctionParameters' + chatCompletionToolType: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-ms-enum: + name: ChatCompletionToolType + modelAsString: true + values: + - value: function + description: The tool type is function. + chatCompletionChoiceCommon: + type: object + properties: + index: + type: integer + finish_reason: + type: string + createTranslationRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + description: Translation request. + audioResponse: + required: + - text + type: object + properties: + text: + type: string + description: Translated or transcribed text. + description: Translation or transcription response when response_format was json + audioVerboseResponse: + required: + - text + type: object + allOf: + - $ref: '#/components/schemas/audioResponse' + - properties: + task: + enum: + - transcribe + - translate + type: string + description: Type of audio task. + x-ms-enum: + modelAsString: true + language: + type: string + description: Language. + duration: + type: number + description: Duration. + segments: + type: array + items: + $ref: '#/components/schemas/audioSegment' + words: + type: array + items: + $ref: '#/components/schemas/audioWord' + description: Translation or transcription response when response_format was verbose_json + audioResponseFormat: + title: AudioResponseFormat + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: Defines the format of the output. + x-ms-enum: + modelAsString: true + createTranscriptionRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + 'timestamp_granularities[]': + type: array + items: + enum: + - word + - segment + type: string + description: "The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.\n" + default: + - segment + description: Transcription request. + audioSegment: + type: object + properties: + id: + type: integer + description: Segment identifier. + seek: + type: number + description: Offset of the segment. + start: + type: number + description: Segment start offset. + end: + type: number + description: Segment end offset. + text: + type: string + description: Segment text. + tokens: + type: array + items: + type: number + description: Tokens of the text. + temperature: + type: number + description: Temperature. + avg_logprob: + type: number + description: Average log probability. + compression_ratio: + type: number + description: Compression ratio. + no_speech_prob: + type: number + description: Probability of 'no speech'. + description: Transcription or translation segment. + audioWord: + type: object + properties: + word: + type: string + description: Word + start: + type: number + description: Word start offset. + end: + type: number + description: Word end offset. + description: Transcription or translation word. + createSpeechRequest: + required: + - input + - voice + type: object + properties: + input: + maxLength: 4096 + type: string + description: The text to synthesize audio for. The maximum length is 4096 characters. + voice: + enum: + - alloy + - echo + - fable + - onyx + - nova + - shimmer + type: string + description: The voice to use for speech synthesis. + response_format: + enum: + - mp3 + - opus + - aac + - flac + - wav + - pcm + type: string + description: The format to synthesize the audio in. + speed: + maximum: 4.0 + minimum: 0.25 + type: number + description: The speed of the synthesize audio. Select a value from `0.25` to `4.0`. `1.0` is the default. + default: 1 + description: Speech request. + imageQuality: + enum: + - standard + - hd + type: string + description: The quality of the image that will be generated. + default: standard + x-ms-enum: + name: Quality + modelAsString: true + values: + - value: standard + description: Standard quality creates images with standard quality. + name: Standard + - value: hd + description: HD quality creates images with finer details and greater consistency across the image. + name: HD + imagesResponseFormat: + enum: + - url + - b64_json + type: string + description: The format in which the generated images are returned. + default: url + x-ms-enum: + name: ImagesResponseFormat + modelAsString: true + values: + - value: url + description: The URL that provides temporary access to download the generated images. + name: Url + - value: b64_json + description: The generated images are returned as base64 encoded string. + name: Base64Json + imageSize: + enum: + - 256x256 + - 512x512 + - 1792x1024 + - 1024x1792 + - 1024x1024 + type: string + description: The size of the generated images. + default: 1024x1024 + x-ms-enum: + name: Size + modelAsString: true + values: + - value: 256x256 + description: The desired size of the generated image is 256x256 pixels. Only supported for dall-e-2. + name: Size256x256 + - value: 512x512 + description: The desired size of the generated image is 512x512 pixels. Only supported for dall-e-2. + name: Size512x512 + - value: 1792x1024 + description: The desired size of the generated image is 1792x1024 pixels. Only supported for dall-e-3. + name: Size1792x1024 + - value: 1024x1792 + description: The desired size of the generated image is 1024x1792 pixels. Only supported for dall-e-3. + name: Size1024x1792 + - value: 1024x1024 + description: The desired size of the generated image is 1024x1024 pixels. + name: Size1024x1024 + imageStyle: + enum: + - vivid + - natural + type: string + description: The style of the generated images. + default: vivid + x-ms-enum: + name: Style + modelAsString: true + values: + - value: vivid + description: Vivid creates images that are hyper-realistic and dramatic. + name: Vivid + - value: natural + description: Natural creates images that are more natural and less hyper-realistic. + name: Natural + imageGenerationsRequest: + required: + - prompt + type: object + properties: + prompt: + minLength: 1 + type: string + description: A text description of the desired image(s). The maximum length is 4000 characters. + format: string + example: a corgi in a field + n: + maximum: 1.0 + minimum: 1.0 + type: integer + description: The number of images to generate. + default: 1 + size: + $ref: '#/components/schemas/imageSize' + response_format: + $ref: '#/components/schemas/imagesResponseFormat' + user: + type: string + description: 'A unique identifier representing your end-user, which can help to monitor and detect abuse.' + format: string + example: user123456 + quality: + $ref: '#/components/schemas/imageQuality' + style: + $ref: '#/components/schemas/imageStyle' + generateImagesResponse: + required: + - created + - data + type: object + properties: + created: + type: integer + description: The unix timestamp when the operation was created. + format: unixtime + example: '1676540381' + data: + type: array + items: + $ref: '#/components/schemas/imageResult' + description: 'The result data of the operation, if successful' + imageResult: + type: object + properties: + url: + type: string + description: The image url. + example: https://www.contoso.com + b64_json: + type: string + description: The base64 encoded image + content_filter_results: + $ref: '#/components/schemas/dalleContentFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + prompt_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + description: 'The image url or encoded image if successful, and an error otherwise.' + line: + required: + - text + - spans + type: object + properties: + text: + type: string + spans: + type: array + items: + $ref: '#/components/schemas/span' + description: An array of spans that represent detected objects and its bounding box information. + description: 'A content line object consisting of an adjacent sequence of content elements, such as words and selection marks.' + span: + required: + - text + - offset + - length + - polygon + type: object + properties: + text: + type: string + description: The text content of the span that represents the detected object. + offset: + type: integer + description: 'The character offset within the text where the span begins. This offset is defined as the position of the first character of the span, counting from the start of the text as Unicode codepoints.' + length: + type: integer + description: 'The length of the span in characters, measured in Unicode codepoints.' + polygon: + type: array + items: + type: object + properties: + x: + type: number + description: The x-coordinate of the point. + y: + type: number + description: The y-coordinate of the point. + description: An array of objects representing points in the polygon that encloses the detected object. + description: A span object that represents a detected object and its bounding box information. + runCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: 'Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.).' + nullable: true + runStepCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run step. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run step. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`. + nullable: true + assistantsApiResponseFormatOption: + oneOf: + - enum: + - none + - auto + type: string + description: "`auto` is the default value\n" + x-ms-enum: + name: AssistantsApiResponseFormatOption + modelAsString: true + values: + - value: none + - value: auto + - $ref: '#/components/schemas/assistantsApiResponseFormat' + description: "Specifies the format that the model must output. Compatible with GPT-4 Turbo and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which guarantees the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.\n" + assistantsApiResponseFormat: + type: object + properties: + type: + enum: + - text + - json_object + type: string + description: Must be one of `text` or `json_object`. + default: text + example: json_object + x-ms-enum: + name: AssistantsApiResponseFormat + modelAsString: true + values: + - value: text + - value: json_object + description: "An object describing the expected output of the model. If `json_object` only `function` type `tools` are allowed to be passed to the Run. If `text` the model can return text or any value needed.\n" + assistantObject: + title: Assistant + required: + - id + - object + - created_at + - name + - description + - model + - instructions + - tools + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - assistant + type: string + description: 'The object type, which is always `assistant`.' + x-ms-enum: + name: AssistantObjectType + modelAsString: true + values: + - value: assistant + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the assistant was created. + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + model: + type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + description: Represents an `assistant` that can call the model and use tools. + createAssistantRequest: + required: + - model + type: object + properties: + model: + anyOf: + - type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + description: "A helper to create a vector store with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + modifyAssistantRequest: + type: object + properties: + model: + anyOf: + - type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n" + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 32768 + type: string + description: "The system instructions that the assistant uses. The maximum length is 32768 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "Overrides the list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "Overrides the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + deleteAssistantResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - assistant.deleted + type: string + x-ms-enum: + name: DeleteAssistantResponseState + modelAsString: true + values: + - value: assistant.deleted + listAssistantsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/assistantObject' + first_id: + type: string + example: asst_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: + type: string + example: asst_QLoItBbqwyAJEzlTy4y9kOMM + has_more: + type: boolean + example: false + assistantToolsCode: + title: Code interpreter tool + required: + - type + type: object + properties: + type: + enum: + - code_interpreter + type: string + description: 'The type of tool being defined: `code_interpreter`' + x-ms-enum: + name: assistantToolsCodeType + modelAsString: true + values: + - value: code_interpreter + assistantToolsFileSearch: + title: FileSearch tool + required: + - type + type: object + properties: + type: + enum: + - file_search + type: string + description: 'The type of tool being defined: `file_search`' + x-ms-enum: + name: assistantToolsFileSearchType + modelAsString: true + values: + - value: file_search + assistantToolsFunction: + title: Function tool + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of tool being defined: `function`' + x-ms-enum: + name: assistantToolsFunction + modelAsString: true + values: + - value: function + function: + required: + - name + - parameters + - description + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + description: The function definition. + truncationObject: + title: Thread Truncation Controls + required: + - type + type: object + properties: + type: + enum: + - auto + - last_messages + type: string + description: 'The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`.' + x-ms-enum: + name: TruncationType + modelAsString: true + values: + - value: auto + - value: last_messages + last_messages: + minimum: 1.0 + type: integer + description: The number of most recent messages from the thread when constructing the context for the run. + nullable: true + description: Controls for how a thread will be truncated prior to the run. Use this to control the intial context window of the run. + assistantsApiToolChoiceOption: + oneOf: + - enum: + - none + - auto + type: string + description: "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n" + x-ms-enum: + name: AssistantsApiToolChoiceOption + modelAsString: true + values: + - value: none + - value: auto + - $ref: '#/components/schemas/assistantsNamedToolChoice' + description: "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tools and instead generates a message.\n`auto` is the default value and means the model can pick between generating a message or calling a tool.\nSpecifying a particular tool like `{\"type\": \"file_search\"}` or `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n" + assistantsNamedToolChoice: + required: + - type + type: object + properties: + type: + enum: + - function + - code_interpreter + - file_search + type: string + description: 'The type of the tool. If type is `function`, the function name must be set' + x-ms-enum: + name: AssistantsNamedToolChoiceType + modelAsString: true + values: + - value: function + - value: code_interpreter + - value: file_search + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific tool. + runObject: + title: A run on a thread + required: + - id + - object + - created_at + - thread_id + - assistant_id + - status + - required_action + - last_error + - expires_at + - started_at + - cancelled_at + - failed_at + - completed_at + - model + - instructions + - tools + - metadata + - usage + - incomplete_details + - max_prompt_tokens + - max_completion_tokens + - truncation_strategy + - tool_choice + - response_format + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.run + type: string + description: 'The object type, which is always `thread.run`.' + x-ms-enum: + name: runObjectType + modelAsString: true + values: + - value: thread.run + description: The run object type which is always thread.run + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was created. + thread_id: + type: string + description: 'The ID of the [thread](/docs/api-reference/threads) that was executed on as a part of this run.' + assistant_id: + type: string + description: The ID of the assistant used for execution of this run. + status: + enum: + - queued + - in_progress + - requires_action + - cancelling + - cancelled + - failed + - completed + - expired + type: string + description: 'The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or `expired`.' + x-ms-enum: + name: RunObjectStatus + modelAsString: true + values: + - value: queued + description: The queued state + - value: in_progress + description: The in_progress state + - value: requires_action + description: The required_action state + - value: cancelling + description: The cancelling state + - value: cancelled + description: The cancelled state + - value: failed + description: The failed state + - value: completed + description: The completed state + - value: expired + description: The expired state + required_action: + required: + - type + - submit_tool_outputs + type: object + properties: + type: + enum: + - submit_tool_outputs + type: string + description: 'For now, this is always `submit_tool_outputs`.' + submit_tool_outputs: + required: + - tool_calls + type: object + properties: + tool_calls: + type: array + items: + $ref: '#/components/schemas/runToolCallObject' + description: A list of the relevant tool calls. + description: Details on the tool outputs needed for this run to continue. + description: Details on the action required to continue the run. Will be `null` if no action is required. + nullable: true + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + type: string + description: One of `server_error` or `rate_limit_exceeded`. + x-ms-enum: + name: LastErrorCode + modelAsString: true + values: + - value: server_error + description: The server failed to respond to request due to server error + - value: rate_limit_exceeded + description: The server failed to respond to request due to rate limit exceeded + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this run. Will be `null` if there are no errors. + nullable: true + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the run will expire. + started_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was started. + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was completed. + nullable: true + incomplete_details: + type: object + properties: + reason: + enum: + - max_completion_tokens + - max_prompt_tokens + type: string + description: The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run. + x-ms-enum: + name: IncompleteDetailsReason + modelAsString: true + values: + - value: max_completion_tokens + - value: max_prompt_tokens + description: Details on why the run is incomplete. Will be `null` if the run is not incomplete. + nullable: true + model: + type: string + description: The model that the assistant used for this run. + instructions: + type: string + description: The instructions that the assistant used for this run. + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: The list of tools that the assistant used for this run. + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + usage: + $ref: '#/components/schemas/runCompletionUsage' + temperature: + type: number + description: 'The sampling temperature used for this run. If not set, defaults to 1.' + nullable: true + top_p: + type: number + description: 'The nucleus sampling value used for this run. If not set, defaults to 1.' + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens specified to have been used over the course of the run.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens specified to have been used over the course of the run.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + description: 'Represents an execution run on a [thread](/docs/api-reference/threads).' + createRunRequest: + required: + - thread_id + - assistant_id + type: object + properties: + assistant_id: + type: string + description: The ID of the assistant to use to execute this run. + model: + type: string + description: 'The ID of the Model to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.' + nullable: true + instructions: + type: string + description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + nullable: true + additional_instructions: + type: string + description: Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions. + nullable: true + additional_messages: + type: array + items: + $ref: '#/components/schemas/createMessageRequest' + description: Adds additional messages to the thread before creating the run. + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + listRunsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/runObject' + first_id: + type: string + example: run_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: + type: string + example: run_QLoItBbqwyAJEzlTy4y9kOMM + has_more: + type: boolean + example: false + modifyRunRequest: + type: object + properties: + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + submitToolOutputsRunRequest: + required: + - tool_outputs + type: object + properties: + tool_outputs: + type: array + items: + type: object + properties: + tool_call_id: + type: string + description: The ID of the tool call in the `required_action` object within the run object the output is being submitted for. + output: + type: string + description: The output of the tool call to be submitted to continue the run. + description: A list of tools for which the outputs are being submitted. + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + additionalProperties: false + runToolCallObject: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: 'The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](/docs/api-reference/runs/submitToolOutputs) endpoint.' + type: + enum: + - function + type: string + description: 'The type of tool call the output is required for. For now, this is always `function`.' + x-ms-enum: + name: RunToolCallObjectType + modelAsString: true + values: + - value: function + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments that the model expects you to pass to the function. + description: The function definition. + description: Tool call objects + createThreadAndRunRequest: + required: + - thread_id + - assistant_id + type: object + properties: + assistant_id: + type: string + description: The ID of the assistant to use to execute this run. + thread: + $ref: '#/components/schemas/createThreadRequest' + model: + type: string + description: 'The ID of the [Model](/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.' + nullable: true + instructions: + type: string + description: Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis. + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + - $ref: '#/components/schemas/assistantToolsFunction' + description: Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis. + nullable: true + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + $ref: '#/components/schemas/truncationObject' + tool_choice: + $ref: '#/components/schemas/assistantsApiToolChoiceOption' + response_format: + $ref: '#/components/schemas/assistantsApiResponseFormatOption' + additionalProperties: false + threadObject: + title: Thread + required: + - id + - object + - created_at + - tool_resources + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread + type: string + description: 'The object type, which is always `thread`.' + x-ms-enum: + name: ThreadObjectType + modelAsString: true + values: + - value: thread + description: The type of thread object which is always `thread` + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the thread was created. + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: 'Represents a thread that contains [messages](/docs/api-reference/messages).' + createThreadRequest: + type: object + properties: + messages: + type: array + items: + $ref: '#/components/schemas/createMessageRequest' + description: A list of messages to start the thread with. + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + description: "A helper to create a vector store with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + modifyThreadRequest: + type: object + properties: + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of File IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + deleteThreadResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.deleted + type: string + x-ms-enum: + name: DeleteThreadResponseObjectState + modelAsString: true + values: + - value: thread.deleted + description: The delete thread response object state which is `thread.deleted` + listThreadsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/threadObject' + first_id: + type: string + example: asst_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: + type: string + example: asst_QLoItBbqwyAJEzlTy4y9kOMM + has_more: + type: boolean + example: false + messageObject: + title: The message object + required: + - id + - object + - created_at + - thread_id + - status + - incomplete_details + - completed_at + - incomplete_at + - role + - content + - assistant_id + - run_id + - attachments + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.message + type: string + description: 'The object type, which is always `thread.message`.' + x-ms-enum: + name: MessageObjectType + modelAsString: true + values: + - value: thread.message + description: The message object type which is `thread.message` + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was created. + thread_id: + type: string + description: 'The [thread](/docs/api-reference/threads) ID that this message belongs to.' + status: + enum: + - in_progress + - incomplete + - completed + type: string + description: 'The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.' + x-ms-enum: + name: MessageObjectStatus + modelAsString: true + values: + - value: in_progress + - value: incomplete + - value: completed + incomplete_details: + required: + - reason + type: object + properties: + reason: + enum: + - content_filter + - max_tokens + - run_cancelled + - run_expired + - run_failed + type: string + description: The reason the message is incomplete. + x-ms-enum: + name: MessageObjectIncompleteReason + modelAsString: true + values: + - value: content_filter + - value: max_tokens + - value: run_cancelled + - value: run_expired + - value: run_failed + description: 'On an incomplete message, details about why the message is incomplete.' + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was completed. + nullable: true + incomplete_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was marked as incomplete. + nullable: true + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + x-ms-enum: + name: MessageObjectRole + modelAsString: true + values: + - value: user + - value: assistant + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageContentImageFileObject' + - $ref: '#/components/schemas/messageContentTextObject' + description: The content of the message in array of text and/or images. + assistant_id: + type: string + description: 'If applicable, the ID of the assistant that authored this message.' + nullable: true + run_id: + type: string + description: 'If applicable, the ID of the [run](/docs/api-reference/runs) associated with the authoring of this message.' + nullable: true + attachments: + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + description: The tools to add this file to. + description: 'A list of files attached to the message, and the tools they were added to.' + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: 'Represents a message within a [thread](/docs/api-reference/threads).' + messageDeltaObject: + title: Message delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: 'The identifier of the message, which can be referenced in API endpoints.' + object: + enum: + - thread.message.delta + type: string + description: 'The object type, which is always `thread.message.delta`.' + x-ms-enum: + name: MessageDeltaObjectType + modelAsString: true + values: + - value: thread.message.delta + delta: + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + x-ms-enum: + name: MessageDeltaObjectDeltaRole + modelAsString: true + values: + - value: user + - value: assistant + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageDeltaContentImageFileObject' + - $ref: '#/components/schemas/messageDeltaContentTextObject' + description: The content of the message in array of text and/or images. + description: The delta containing the fields that have changed on the Message. + description: "Represents a message delta i.e. any changed fields on a message during streaming.\n" + createMessageRequest: + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: "The role of the entity that is creating the message. Allowed values include:\n- `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.\n- `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.\n" + x-ms-enum: + name: CreateMessageRequestRole + modelAsString: true + values: + - value: user + - value: assistant + content: + maxLength: 256000 + minLength: 1 + type: string + description: The content of the message. + attachments: + required: + - file_id + - tools + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/assistantToolsCode' + - $ref: '#/components/schemas/assistantToolsFileSearch' + x-oaiExpandable: true + description: The tools to add this file to. + description: 'A list of files attached to the message, and the tools they should be added to.' + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + modifyMessageRequest: + type: object + properties: + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + deleteMessageResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.message.deleted + type: string + x-ms-enum: + name: DeleteMessageResponseObject + modelAsString: true + values: + - value: thread.message.deleted + description: The delete message response object state + listMessagesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/messageObject' + first_id: + type: string + example: msg_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: + type: string + example: msg_QLoItBbqwyAJEzlTy4y9kOMM + has_more: + type: boolean + example: false + messageContentImageFileObject: + title: Image file + required: + - type + - image_file + type: object + properties: + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-ms-enum: + name: MessageContentImageFileObjectType + modelAsString: true + values: + - value: image_file + description: The message content image file type + image_file: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The file ID of the image in the message content. + description: References an image file in the content of a message. + messageContentTextObject: + title: Text + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: Always `text`. + x-ms-enum: + name: messageContentTextObjectType + modelAsString: true + values: + - value: text + description: The message content text Object type + text: + required: + - value + - annotations + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/messageContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + messageContentTextAnnotationsFileCitationObject: + title: File citation + required: + - type + - text + - file_citation + - start_index + - end_index + type: object + properties: + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-ms-enum: + name: FileCitationObjectType + modelAsString: true + values: + - value: file_citation + description: The file citation object type + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + required: + - file_id + - quote + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + quote: + type: string + description: The specific quote in the file. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "retrieval" tool to search files. + messageContentTextAnnotationsFilePathObject: + title: File path + required: + - type + - text + - file_path + - start_index + - end_index + type: object + properties: + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-ms-enum: + name: FilePathObjectType + modelAsString: true + values: + - value: file_path + description: The file path object type + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + messageDeltaContentImageFileObject: + title: Image file + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-ms-enum: + name: MessageDeltaContentImageFileObjectType + modelAsString: true + values: + - value: image_file + image_file: + type: object + properties: + file_id: + type: string + description: The File ID of the image in the message content. + description: References an image File in the content of a message. + messageDeltaContentTextObject: + title: Text + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - text + type: string + description: Always `text`. + x-ms-enum: + name: MessageDeltaContentTextObjectType + modelAsString: true + values: + - value: text + text: + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/messageDeltaContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/messageDeltaContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + messageDeltaContentTextAnnotationsFileCitationObject: + title: File citation + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-ms-enum: + name: MessageDeltaContentTextAnnotationsFileCitationObjectType + modelAsString: true + values: + - value: file_citation + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + quote: + type: string + description: The specific quote in the file. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the "file_search" tool to search files. + messageDeltaContentTextAnnotationsFilePathObject: + title: File path + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-ms-enum: + name: MessageDeltaContentTextAnnotationsFilePathObjectType + modelAsString: true + values: + - value: file_path + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file. + runStepObject: + title: Run steps + required: + - id + - object + - created_at + - assistant_id + - thread_id + - run_id + - type + - status + - step_details + - last_error + - expired_at + - cancelled_at + - failed_at + - completed_at + - metadata + type: object + properties: + id: + type: string + description: 'The identifier of the run step, which can be referenced in API endpoints.' + object: + enum: + - assistant.run.step + type: string + description: 'The object type, which is always `assistant.run.step``.' + x-ms-enum: + name: RunStepObjectType + modelAsString: true + values: + - value: assistant.run.step + description: 'The object type, which is always `assistant.run.step`' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was created. + assistant_id: + type: string + description: The ID of the assistant associated with the run step. + thread_id: + type: string + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + run_id: + type: string + description: 'The ID of the [run](/docs/api-reference/runs) that this run step is a part of.' + type: + enum: + - message_creation + - tool_calls + type: string + description: 'The type of run step, which can be either `message_creation` or `tool_calls`.' + x-ms-enum: + name: RunStepObjectType + modelAsString: true + values: + - value: message_creation + description: The message_creation run step + - value: tool_calls + description: The tool_calls run step + status: + enum: + - in_progress + - cancelled + - failed + - completed + - expired + type: string + description: 'The status of the run, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`.' + x-ms-enum: + name: RunStepObjectStatus + modelAsString: true + values: + - value: in_progress + description: The in_progress run status + - value: cancelled + description: The cancelled run status + - value: failed + description: The cancelled run status + - value: completed + description: The cancelled run status + - value: expired + description: The cancelled run status + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsObject' + description: The details of the run step. + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + type: string + description: One of `server_error` or `rate_limit_exceeded`. + x-ms-enum: + name: LastErrorCode + modelAsString: true + values: + - value: server_error + description: The server_error + - value: rate_limit_exceeded + description: The rate_limit_exceeded status + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this run step. Will be `null` if there are no errors. + nullable: true + expired_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired. + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step completed. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: "Represents a step in execution of a run.\n" + runStepDeltaObject: + title: Run step delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: 'The identifier of the run step, which can be referenced in API endpoints.' + object: + enum: + - thread.run.step.delta + type: string + description: 'The object type, which is always `thread.run.step.delta`.' + x-ms-enum: + name: RunStepDeltaObjectType + modelAsString: true + values: + - value: thread.run.step.delta + delta: + type: object + properties: + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsObject' + description: The details of the run step. + x-oaiExpandable: true + description: The delta containing the fields that have changed on the run step. + description: "Represents a run step delta i.e. any changed fields on a run step during streaming.\n" + listRunStepsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/runStepObject' + first_id: + type: string + example: step_hLBK7PXBv5Lr2NQT7KLY0ag1 + last_id: + type: string + example: step_QLoItBbqwyAJEzlTy4y9kOMM + has_more: + type: boolean + example: false + runStepDetailsMessageCreationObject: + title: Message creation + required: + - type + - message_creation + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation``. + x-ms-enum: + name: RunStepDetailsMessageCreationObjectType + modelAsString: true + values: + - value: message_creation + message_creation: + required: + - message_id + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + runStepDeltaStepDetailsMessageCreationObject: + title: Message creation + required: + - type + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation`. + x-ms-enum: + name: RunStepDeltaStepDetailsMessageCreationObjectType + modelAsString: true + values: + - value: message_creation + message_creation: + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + runStepDetailsToolCallsObject: + title: Tool calls + required: + - type + - tool_calls + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-ms-enum: + name: RunStepDetailsToolCallsObjectType + modelAsString: true + values: + - value: tool_calls + tool_calls: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsFunctionObject' + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `retrieval` or `function`.\n" + description: Details of the tool call. + runStepDeltaStepDetailsToolCallsObject: + title: Tool calls + required: + - type + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsObjectType + modelAsString: true + values: + - value: tool_calls + tool_calls: + type: array + items: + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsFunctionObject' + x-oaiExpandable: true + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search` or `function`.\n" + description: Details of the tool call. + runStepDetailsToolCallsCodeObject: + title: Code interpreter tool call + required: + - id + - type + - code_interpreter + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsCodeObjectType + modelAsString: true + values: + - value: code_interpreter + code_interpreter: + required: + - input + - outputs + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/runStepDetailsToolCallsCodeOutputImageObject' + description: 'The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.' + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + runStepDeltaStepDetailsToolCallsCodeObject: + title: Code interpreter tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: The type of tool call. This is always going to be `code_interpreter` for this type of tool call. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeObjectType + modelAsString: true + values: + - value: code_interpreter + code_interpreter: + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputImageObject' + description: 'The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.' + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + runStepDetailsToolCallsCodeOutputLogsObject: + title: Code interpreter log output + required: + - type + - logs + type: object + properties: + type: + enum: + - logs + type: string + description: Always `logs`. + x-ms-enum: + name: RunStepDetailsToolCallsCodeOutputLogsObjectType + modelAsString: true + values: + - value: logs + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + runStepDeltaStepDetailsToolCallsCodeOutputLogsObject: + title: Code interpreter log output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - logs + type: string + description: Always `logs`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeOutputLogsObjectType + modelAsString: true + values: + - value: logs + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + runStepDetailsToolCallsCodeOutputImageObject: + title: Code interpreter image output + required: + - type + - image + type: object + properties: + type: + enum: + - image + type: string + description: Always `image`. + x-ms-enum: + name: RunStepDetailsToolCallsCodeOutputImageObjectType + modelAsString: true + values: + - value: image + image: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The file ID of the image. + runStepDeltaStepDetailsToolCallsCodeOutputImageObject: + title: Code interpreter image output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - image + type: string + description: Always `image`. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsCodeOutputImageObject + modelAsString: true + values: + - value: image + image: + type: object + properties: + file_id: + type: string + description: The file ID of the image. + runStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - id + - type + - file_search + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: The type of tool call. This is always going to be `file_search` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFileSearchObjectType + modelAsString: true + values: + - value: file_search + file_search: + type: object + description: 'For now, this is always going to be an empty object.' + runStepDeltaStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - index + - type + - file_search + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: The type of tool call. This is always going to be `retrieval` for this type of tool call. + x-ms-enum: + name: RunStepDeltaStepDetailsToolCallsFileSearchObjectType + modelAsString: true + values: + - value: file_search + file_search: + type: object + description: 'For now, this is always going to be an empty object.' + runStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: The type of tool call. This is always going to be `function` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFunctionObjectType + modelAsString: true + values: + - value: function + function: + required: + - name + - arguments + - output + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: 'The output of the function. This will be `null` if the outputs have not been [submitted](/docs/api-reference/runs/submitToolOutputs) yet.' + nullable: true + description: The definition of the function that was called. + runStepDeltaStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: The type of tool call. This is always going to be `function` for this type of tool call. + x-ms-enum: + name: RunStepDetailsToolCallsFunctionObjectType + modelAsString: true + values: + - value: function + function: + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: The output of the function. This will be `null` if the outputs have not been submitted yet. + nullable: true + description: The definition of the function that was called. + vectorStoreExpirationAfter: + title: Vector store expiration policy + required: + - anchor + - days + type: object + properties: + anchor: + enum: + - last_active_at + type: string + description: 'Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`.' + x-ms-enum: + name: VectorStoreExpirationAfterAnchor + modelAsString: true + values: + - value: last_active_at + description: The anchor timestamp after which the expiration policy applies. + days: + maximum: 365.0 + minimum: 1.0 + type: integer + description: The number of days after the anchor time that the vector store will expire. + description: The expiration policy for a vector store. + vectorStoreObject: + title: Vector store + required: + - id + - object + - usage_bytes + - created_at + - status + - last_active_at + - name + - bytes + - file_counts + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store + type: string + description: 'The object type, which is always `vector_store`.' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store was created. + name: + type: string + description: The name of the vector store. + usage_bytes: + type: integer + description: The total number of bytes used by the files in the vector store. + file_counts: + required: + - in_progress + - completed + - failed + - cancelled + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been successfully processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that were cancelled. + total: + type: integer + description: The total number of files. + status: + enum: + - expired + - in_progress + - completed + type: string + description: 'The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use.' + x-ms-enum: + name: VectorStoreObjectStatus + modelAsString: true + values: + - value: expired + - value: in_progress + - value: completed + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store will expire. + nullable: true + last_active_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store was last active. + nullable: true + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + description: A vector store is a collection of processed files can be used by the `file_search` tool. + createVectorStoreRequest: + type: object + properties: + file_ids: + maxItems: 500 + type: array + items: + type: string + description: A list of file IDs that the vector store should use. Useful for tools like `file_search` that can access files. + name: + type: string + description: The name of the vector store. + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + updateVectorStoreRequest: + type: object + properties: + name: + type: string + description: The name of the vector store. + nullable: true + expires_after: + $ref: '#/components/schemas/vectorStoreExpirationAfter' + metadata: + type: object + description: "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + nullable: true + additionalProperties: false + listVectorStoresResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/vectorStoreObject' + first_id: + type: string + example: vs_abc123 + last_id: + type: string + example: vs_abc456 + has_more: + type: boolean + example: false + deleteVectorStoreResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.deleted + type: string + x-ms-enum: + name: DeleteVectorStoreResponseObject + modelAsString: true + values: + - value: vector_store.deleted + description: The delete vector store response object state + vectorStoreFileObject: + title: Vector store files + required: + - id + - object + - usage_bytes + - created_at + - vector_store_id + - status + - last_error + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.file + type: string + description: 'The object type, which is always `vector_store.file`.' + x-ms-enum: + name: VectorStoreFileObjectType + modelAsString: true + values: + - value: vector_store.file + usage_bytes: + type: integer + description: The total vector store usage in bytes. Note that this may be different from the original file size. + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store file was created. + vector_store_id: + type: string + description: The ID of the vector store that the file is attached to. + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: 'The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.' + x-ms-enum: + name: VectorStoreFileObjectStatus + modelAsString: true + values: + - value: in_progress + - value: completed + - value: cancelled + - value: failed + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - internal_error + - file_not_found + - parsing_error + - unhandled_mime_type + type: string + description: One of `server_error` or `rate_limit_exceeded`. + x-ms-enum: + name: VectorStoreFileObjectLastErrorCode + modelAsString: true + values: + - value: internal_error + - value: file_not_found + - value: parsing_error + - value: unhandled_mime_type + message: + type: string + description: A human-readable description of the error. + description: The last error associated with this vector store file. Will be `null` if there are no errors. + nullable: true + description: A list of files attached to a vector store. + createVectorStoreFileRequest: + required: + - file_id + type: object + properties: + file_id: + type: string + description: A File ID that the vector store should use. Useful for tools like `file_search` that can access files. + additionalProperties: false + listVectorStoreFilesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/vectorStoreFileObject' + first_id: + type: string + example: file-abc123 + last_id: + type: string + example: file-abc456 + has_more: + type: boolean + example: false + deleteVectorStoreFileResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.file.deleted + type: string + x-ms-enum: + name: DeleteVectorStoreFileResponseObject + modelAsString: true + values: + - value: vector_store.file.deleted + vectorStoreFileBatchObject: + title: Vector store file batch + required: + - id + - object + - created_at + - vector_store_id + - status + - file_counts + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.files_batch + type: string + description: 'The object type, which is always `vector_store.file_batch`.' + x-ms-enum: + name: VectorStoreFileBatchObjectType + modelAsString: true + values: + - value: vector_store.files_batch + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the vector store files batch was created. + vector_store_id: + type: string + description: The ID of the vector store that the File is attached to. + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: 'The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.' + x-ms-enum: + name: VectorStoreFileBatchObjectStatus + modelAsString: true + values: + - value: in_progress + - value: completed + - value: cancelled + - value: failed + file_counts: + required: + - in_progress + - completed + - cancelled + - failed + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that where cancelled. + total: + type: integer + description: The total number of files. + description: A batch of files attached to a vector store. + createVectorStoreFileBatchRequest: + required: + - file_ids + type: object + properties: + file_ids: + maxItems: 500 + minItems: 1 + type: array + items: + type: string + description: A list of File IDs that the vector store should use. Useful for tools like `file_search` that can access files. + additionalProperties: false + assistantStreamEvent: + oneOf: + - $ref: '#/components/schemas/threadStreamEvent' + - $ref: '#/components/schemas/runStreamEvent' + - $ref: '#/components/schemas/runStepStreamEvent' + - $ref: '#/components/schemas/messageStreamEvent' + - $ref: '#/components/schemas/errorEvent' + - $ref: '#/components/schemas/doneEvent' + description: "Represents an event emitted when streaming a Run.\n\nEach event in a server-sent events stream has an `event` and `data` property:\n\n```\nevent: thread.created\ndata: {\"id\": \"thread_123\", \"object\": \"thread\", ...}\n```\n\nWe emit events whenever a new object is created, transitions to a new state, or is being\nstreamed in parts (deltas). For example, we emit `thread.run.created` when a new run\nis created, `thread.run.completed` when a run completes, and so on. When an Assistant chooses\nto create a message during a run, we emit a `thread.message.created event`, a\n`thread.message.in_progress` event, many `thread.message.delta` events, and finally a\n`thread.message.completed` event.\n\nWe may add additional events over time, so we recommend handling unknown events gracefully\nin your code. See the [Assistants API quickstart](/docs/assistants/overview) to learn how to\nintegrate the Assistants API with streaming.\n" + threadStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.created + type: string + x-ms-enum: + name: ThreadStreamEventEnum + modelAsString: true + values: + - value: thread.created + description: The thread created event + data: + $ref: '#/components/schemas/threadObject' + description: Occurs when a new thread is created. + runStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.created + type: string + x-ms-enum: + name: RunStreamEventCreated + modelAsString: true + values: + - value: thread.run.created + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a new run is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.queued + type: string + x-ms-enum: + name: RunStreamEventQueued + modelAsString: true + values: + - value: thread.run.queued + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `queued` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.in_progress + type: string + x-ms-enum: + name: RunStreamEventInProgress + modelAsString: true + values: + - value: thread.run.in_progress + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to an `in_progress` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.requires_action + type: string + x-ms-enum: + name: RunStreamEventRequiresAction + modelAsString: true + values: + - value: thread.run.requires_action + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `requires_action` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.completed + type: string + x-ms-enum: + name: RunStreamEventCompleted + modelAsString: true + values: + - value: thread.run.completed + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.failed + type: string + x-ms-enum: + name: RunStreamEventFailed + modelAsString: true + values: + - value: thread.run.failed + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run fails. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelling + type: string + x-ms-enum: + name: RunStreamEventCancelling + modelAsString: true + values: + - value: thread.run.cancelling + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run moves to a `cancelling` status. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelled + type: string + x-ms-enum: + name: RunStreamEventCancelled + modelAsString: true + values: + - value: thread.run.cancelled + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run is cancelled. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.expired + type: string + x-ms-enum: + name: RunStreamEventExpired + modelAsString: true + values: + - value: thread.run.expired + data: + $ref: '#/components/schemas/runObject' + description: Occurs when a run expires. + runStepStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.created + type: string + x-ms-enum: + name: RunStepStreamEventCreated + modelAsString: true + values: + - value: thread.run.step.created + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.in_progress + type: string + x-ms-enum: + name: RunStepStreamEventInProgress + modelAsString: true + values: + - value: thread.run.step.in_progress + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step moves to an `in_progress` state. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.delta + type: string + x-ms-enum: + name: RunStepStreamEventDelta + modelAsString: true + values: + - value: thread.run.step.delta + data: + $ref: '#/components/schemas/runStepDeltaObject' + description: Occurs when parts of a run step are being streamed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.completed + type: string + x-ms-enum: + name: RunStepStreamEventCompleted + modelAsString: true + values: + - value: thread.run.step.completed + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.failed + type: string + x-ms-enum: + name: RunStepStreamEventFailed + modelAsString: true + values: + - value: thread.run.step.failed + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step fails. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.cancelled + type: string + x-ms-enum: + name: RunStepStreamEventCancelled + modelAsString: true + values: + - value: thread.run.step.cancelled + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step is cancelled. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.expired + type: string + x-ms-enum: + name: RunStepStreamEventExpired + modelAsString: true + values: + - value: thread.run.step.expired + data: + $ref: '#/components/schemas/runStepObject' + description: Occurs when a run step expires. + messageStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.created + type: string + x-ms-enum: + name: MessageStreamEventCreated + modelAsString: true + values: + - value: thread.message.created + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message is created. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.in_progress + type: string + x-ms-enum: + name: MessageStreamEventInProgress + modelAsString: true + values: + - value: thread.message.in_progress + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message moves to an `in_progress` state. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.delta + type: string + x-ms-enum: + name: MessageStreamEventDelta + modelAsString: true + values: + - value: thread.message.delta + data: + $ref: '#/components/schemas/messageDeltaObject' + description: Occurs when parts of a message are being streamed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.completed + type: string + x-ms-enum: + name: MessageStreamEventCompleted + modelAsString: true + values: + - value: thread.message.completed + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message is completed. + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.incomplete + type: string + x-ms-enum: + name: MessageStreamEventIncomplete + modelAsString: true + values: + - value: thread.message.incomplete + data: + $ref: '#/components/schemas/messageObject' + description: Occurs when a message ends before it is completed. + errorEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - error + type: string + x-ms-enum: + name: ErrorEventEnum + modelAsString: true + values: + - value: error + data: + $ref: '#/components/schemas/error' + description: Occurs when an error occurs. This can happen due to an internal server error or a timeout. + doneEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - done + type: string + x-ms-enum: + name: DoneEventEnum + modelAsString: true + values: + - value: done + data: + enum: + - '[DONE]' + type: string + x-ms-enum: + name: DoneEventDataEnum + modelAsString: true + values: + - value: '[DONE]' + description: Occurs when a stream ends. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-06-01.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-06-01.yaml new file mode 100644 index 000000000..204c0068c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-06-01.yaml @@ -0,0 +1,2409 @@ +openapi: 3.0.1 +info: + title: Azure OpenAI API + description: Azure OpenAI APIs for completions and search + version: '2024-06-01' +servers: + - url: https://REPLACE-BY-POLICY.azure-api.net/openai +paths: + '/deployments/{deployment-id}/completions': + post: + summary: 'Creates a completion for the provided prompt, parameters and chosen model.' + description: 'Creates a completion for the provided prompt, parameters and chosen model.' + operationId: Completions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the model which was deployed. + example: davinci + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-06-01T00:00:00Z' + requestBody: + content: + application/json: + schema: + type: object + properties: + prompt: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - type: array + items: + type: string + default: '' + example: This is a test. + description: Array size minimum of 1 and maximum of 2048 + description: "The prompt(s) to generate completions for, encoded as a string or array of strings.\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. Maximum allowed size of string list is 2048." + max_tokens: + type: integer + description: 'The token count of your prompt plus max_tokens cannot exceed the model''s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096). Has minimum of 0.' + default: 16 + nullable: true + example: 16 + temperature: + type: number + description: "What sampling temperature to use. Higher values means the model will take more risks. Try 0.9 for more creative applications, and 0 (argmax sampling) for ones with a well-defined answer.\nWe generally recommend altering this or top_p but not both." + default: 1 + nullable: true + example: 1 + top_p: + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or temperature but not both." + default: 1 + nullable: true + example: 1 + logit_bias: + type: object + description: 'Defaults to null. Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this tokenizer tool (which works for both GPT-2 and GPT-3) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. As an example, you can pass {"50256" : -100} to prevent the <|endoftext|> token from being generated.' + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse' + n: + type: integer + description: "How many completions to generate for each prompt. Minimum of 1 and maximum of 128 allowed.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'Whether to stream back partial progress. If set, tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a data: [DONE] message.' + default: false + nullable: true + logprobs: + type: integer + description: "Include the log probabilities on the logprobs most likely tokens, as well the chosen tokens. For example, if logprobs is 5, the API will return a list of the 5 most likely tokens. The API will always return the logprob of the sampled token, so there may be up to logprobs+1 elements in the response.\nMinimum of 0 and maximum of 5 allowed." + default: + nullable: true + suffix: + type: string + description: The suffix that comes after a completion of inserted text. + nullable: true + echo: + type: boolean + description: Echo back the prompt in addition to the completion + default: false + nullable: true + stop: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: '' + - type: array + items: + type: string + example: '' + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + completion_config: + type: string + nullable: true + presence_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + best_of: + type: integer + description: "Generates best_of completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\nWhen used with n, best_of controls the number of candidate completions and n specifies how many to return - best_of must be greater than n.\nNote: Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for max_tokens and stop. Has maximum value of 128." + example: + prompt: "Negate the following sentence.The price for bubblegum increased on thursday.\n Negated Sentence:" + max_tokens: 50 + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + object: + type: string + created: + type: integer + model: + type: string + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + properties: + text: + type: string + index: + type: integer + logprobs: + type: object + properties: + tokens: + type: array + items: + type: string + token_logprobs: + type: array + items: + type: number + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + text_offset: + type: array + items: + type: integer + nullable: true + finish_reason: + type: string + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + usage: + required: + - prompt_tokens + - total_tokens + - completion_tokens + type: object + properties: + completion_tokens: + type: number + format: int32 + prompt_tokens: + type: number + format: int32 + total_tokens: + type: number + format: int32 + example: + model: davinci + object: text_completion + id: cmpl-4509KAos68kxOqpE2uYGw81j6m7uo + created: 1637097562 + choices: + - index: 0 + text: The price for bubblegum decreased on thursday. + logprobs: + finish_reason: stop + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/embeddings': + post: + summary: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + description: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + operationId: embeddings_create + parameters: + - name: deployment-id + in: path + description: The deployment id of the model which was deployed. + required: true + schema: + enum: + - embedding + type: string + default: embedding + example: ada-search-index-v1 + - name: api-version + in: query + required: true + schema: + enum: + - '2024-06-01' + type: string + description: api version + default: '2024-06-01' + example: '2024-06-01' + requestBody: + content: + application/json: + schema: + required: + - input + type: object + properties: + input: + oneOf: + - type: string + default: '' + nullable: true + example: This is a test. + - maxItems: 2048 + minItems: 1 + type: array + items: + minLength: 1 + type: string + example: This is a test. + description: "Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length.\nUnless you are embedding code, we suggest replacing newlines (\\n) in your input with a single space, as we have observed inferior results when newlines are present." + user: + type: string + description: 'A unique identifier representing your end-user, which can help monitoring and detecting abuse.' + input_type: + type: string + description: input type of embedding search to use + example: query + encoding_format: + type: string + description: The format to return the embeddings in. Can be either `float` or `base64`. Defaults to `float`. + nullable: true + example: base64 + dimensions: + type: integer + description: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. + nullable: true + example: 1 + example: + input: APIM love OpenAI + responses: + '200': + description: OK + content: + application/json: + schema: + required: + - object + - model + - data + - usage + type: object + properties: + object: + type: string + model: + type: string + data: + type: array + items: + required: + - index + - object + - embedding + type: object + properties: + index: + type: integer + object: + type: string + embedding: + type: array + items: + type: number + usage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + total_tokens: + type: integer + example: + object: string + model: string + data: + - index: 0 + object: string + embedding: + - 0 + usage: + prompt_tokens: 0 + total_tokens: 0 + '/deployments/{deployment-id}/chat/completions': + post: + summary: Creates a completion for the chat message + description: Creates a completion for the chat message + operationId: ChatCompletions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - chat + - gpt-4o + type: string + description: Deployment id of the model which was deployed. + default: chat + - name: api-version + in: query + required: true + schema: + enum: + - '2024-06-01' + type: string + description: api version + default: '2024-06-01' + example: '2024-06-01' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionRequest' + example: + model: chat + messages: + - role: system + content: You are a helpful assistant that responds in Markdown. Help me with my math homework! + - role: user + content: How to calculate the distance between earth and moon? + responses: + '200': + description: OK + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/createChatCompletionResponse' + example: + id: string + object: chat.completion + created: 0 + model: string + usage: + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + system_fingerprint: string + prompt_filter_results: + - prompt_index: 0 + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + choices: + - index: 0 + finish_reason: string + message: + role: assistant + content: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + function_call: + name: string + arguments: string + context: + citations: + - content: string + title: string + url: string + filepath: string + chunk_id: string + intent: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + protected_material_text: + filtered: true + detected: true + protected_material_code: + filtered: true + detected: true + citation: + URL: string + license: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + '400': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '500': + description: Service unavailable + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/errorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + error: + code: string + message: string + jailbreak: + filtered: true + detected: true + code: string + message: string + '/deployments/{deployment-id}/audio/transcriptions': + post: + summary: Transcribes audio into the input language. + description: Transcribes audio into the input language. + operationId: Transcriptions_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-06-01' + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + format: binary + prompt: + type: string + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + default: 0 + language: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/audio/translations': + post: + summary: Transcribes and translates input audio into English text. + description: Transcribes and translates input audio into English text. + operationId: Translations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + type: string + description: Deployment id of the whisper model which was deployed. + example: whisper + - name: api-version + in: query + required: true + schema: + type: string + description: api version + example: '2024-06-01' + requestBody: + content: + multipart/form-data: + schema: + properties: + file: + type: string + format: binary + prompt: + type: string + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + default: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + example: + text: string + text/plain: + schema: + type: string + description: 'Transcribed text in the output format (when response_format was one of text, vtt or srt).' + examples: + default: + value: + '/deployments/{deployment-id}/images/generations': + post: + summary: Generates a batch of images from a text caption on a given DALLE model deployment + description: Generates a batch of images from a text caption on a given DALLE model deployment + operationId: ImageGenerations_Create + parameters: + - name: deployment-id + in: path + required: true + schema: + enum: + - dall-e-3 + type: string + description: Deployment id of the dalle model which was deployed. + default: dall-e-3 + example: dalle-deployment + - name: api-version + in: query + required: true + schema: + enum: + - '2024-06-01' + type: string + description: api version + default: '2024-06-01' + example: '2024-06-01' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/imageGenerationsRequest' + example: + prompt: a corgi in a field + n: 1 + size: 1024x1024 + response_format: url + user: user123456 + quality: standard + style: vivid + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + example: + created: '1676540381' + data: + - url: https://www.contoso.com + b64_json: string + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + revised_prompt: string + prompt_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + '400': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + revised_prompt: string + code: string + message: string + '500': + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + example: + error: + param: string + type: string + inner_error: + code: ResponsibleAIPolicyViolation + content_filter_results: + sexual: + filtered: true + severity: safe + violence: + filtered: true + severity: safe + hate: + filtered: true + severity: safe + self_harm: + filtered: true + severity: safe + profanity: + filtered: true + detected: true + jailbreak: + filtered: true + detected: true + revised_prompt: string + code: string + message: string +components: + schemas: + errorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/error' + errorBase: + type: object + properties: + code: + type: string + message: + type: string + error: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/innerError' + innerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Inner error with additional details. + innerErrorCode: + enum: + - ResponsibleAIPolicyViolation + type: string + description: Error codes for the inner error object. + x-ms-enum: + name: InnerErrorCode + modelAsString: true + values: + - value: ResponsibleAIPolicyViolation + description: The prompt violated one of more content filter rules. + dalleErrorResponse: + type: object + properties: + error: + $ref: '#/components/schemas/dalleError' + dalleError: + type: object + allOf: + - $ref: '#/components/schemas/errorBase' + properties: + param: + type: string + type: + type: string + inner_error: + $ref: '#/components/schemas/dalleInnerError' + dalleInnerError: + type: object + properties: + code: + $ref: '#/components/schemas/innerErrorCode' + content_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + description: Inner error with additional details. + contentFilterResultBase: + required: + - filtered + type: object + properties: + filtered: + type: boolean + contentFilterSeverityResult: + required: + - severity + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + severity: + enum: + - safe + - low + - medium + - high + type: string + x-ms-enum: + name: ContentFilterSeverity + modelAsString: true + values: + - value: safe + description: General content or related content in generic or non-harmful contexts. + - value: low + description: Harmful content at a low intensity and risk level. + - value: medium + description: Harmful content at a medium intensity and risk level. + - value: high + description: Harmful content at a high intensity and risk level. + contentFilterDetectedResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultBase' + - properties: + detected: + type: boolean + contentFilterDetectedWithCitationResult: + required: + - detected + - filtered + type: object + allOf: + - $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + citation: + type: object + properties: + URL: + type: string + license: + type: string + contentFilterResultsBase: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + error: + $ref: '#/components/schemas/errorBase' + description: Information about the content filtering results. + contentFilterPromptResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + contentFilterChoiceResults: + type: object + allOf: + - $ref: '#/components/schemas/contentFilterResultsBase' + - properties: + protected_material_text: + $ref: '#/components/schemas/contentFilterDetectedResult' + - properties: + protected_material_code: + $ref: '#/components/schemas/contentFilterDetectedWithCitationResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + promptFilterResult: + type: object + properties: + prompt_index: + type: integer + content_filter_results: + $ref: '#/components/schemas/contentFilterPromptResults' + description: Content filtering results for a single prompt in the request. + promptFilterResults: + type: array + items: + $ref: '#/components/schemas/promptFilterResult' + description: 'Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.' + dalleContentFilterResults: + type: object + properties: + sexual: + $ref: '#/components/schemas/contentFilterSeverityResult' + violence: + $ref: '#/components/schemas/contentFilterSeverityResult' + hate: + $ref: '#/components/schemas/contentFilterSeverityResult' + self_harm: + $ref: '#/components/schemas/contentFilterSeverityResult' + description: Information about the content filtering results. + dalleFilterResults: + type: object + allOf: + - $ref: '#/components/schemas/dalleContentFilterResults' + - properties: + profanity: + $ref: '#/components/schemas/contentFilterDetectedResult' + jailbreak: + $ref: '#/components/schemas/contentFilterDetectedResult' + description: 'Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.' + chatCompletionsRequestCommon: + type: object + properties: + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or `temperature` but not both." + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: 'If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.' + default: false + nullable: true + stop: + oneOf: + - type: string + nullable: true + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + description: Array minimum size of 1 and maximum of 4 + description: Up to 4 sequences where the API will stop generating further tokens. + default: + max_tokens: + type: integer + description: 'The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens).' + default: 4096 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model''s likelihood to talk about new topics.' + default: 0 + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: 'Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model''s likelihood to repeat the same line verbatim.' + default: 0 + logit_bias: + type: object + description: 'Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.' + nullable: true + user: + type: string + description: 'A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.' + example: user-1234 + createChatCompletionRequest: + required: + - messages + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsRequestCommon' + - properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessage' + description: 'A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).' + data_sources: + type: array + items: + $ref: '#/components/schemas/azureChatExtensionConfiguration' + description: " The configuration entries for Azure OpenAI chat extensions that use them.\n This additional specification is only compatible with Azure OpenAI." + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: How many chat completion choices to generate for each input message. + default: 1 + nullable: true + example: 1 + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: 'If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.' + default: 0 + nullable: true + example: 1 + logprobs: + type: boolean + description: 'Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of `message`. This option is currently not available on the `gpt-4-vision-preview` model.' + default: false + nullable: true + top_logprobs: + maximum: 5.0 + minimum: 0.0 + type: integer + description: 'An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to `true` if this parameter is used.' + nullable: true + response_format: + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionResponseFormat' + description: An object specifying the format that the model must output. Used to enable JSON mode. + tools: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionTool' + description: 'A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.' + tool_choice: + $ref: '#/components/schemas/chatCompletionToolChoiceOption' + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/chatCompletionFunction' + description: Deprecated in favor of `tools`. A list of functions the model may generate JSON inputs for. + function_call: + oneOf: + - enum: + - none + - auto + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: 'Specifying a particular function via `{"name": "my_function"}` forces the model to call that function.' + description: 'Deprecated in favor of `tool_choice`. Controls how the model responds to function calls. "none" means the model does not call a function, and responds to the end-user. "auto" means the model can pick between an end-user or calling a function. Specifying a particular function via `{"name":\ "my_function"}` forces the model to call that function. "none" is the default when no functions are present. "auto" is the default if functions are present.' + chatCompletionResponseFormat: + enum: + - text + - json_object + type: string + description: Setting to `json_object` enables JSON mode. This guarantees that the message the model generates is valid JSON. + default: text + nullable: true + example: json_object + x-ms-enum: + name: ChatCompletionResponseFormat + modelAsString: true + values: + - value: text + description: Response format is a plain text string. + - value: json_object + description: Response format is a JSON object. + chatCompletionFunction: + required: + - name + type: object + properties: + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + description: + type: string + description: The description of what the function does. + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + chatCompletionFunctionParameters: + type: object + description: 'The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/gpt/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.' + chatCompletionRequestMessage: + required: + - role + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionRequestMessageRole' + discriminator: + propertyName: role + mapping: + system: '#/components/schemas/chatCompletionRequestMessageSystem' + user: '#/components/schemas/chatCompletionRequestMessageUser' + assistant: '#/components/schemas/chatCompletionRequestMessageAssistant' + tool: '#/components/schemas/chatCompletionRequestMessageTool' + function: '#/components/schemas/chatCompletionRequestMessageFunction' + chatCompletionRequestMessageRole: + enum: + - system + - user + - assistant + - tool + - function + type: string + description: The role of the messages author. + x-ms-enum: + name: ChatCompletionRequestMessageRole + modelAsString: true + values: + - value: system + description: The message author role is system. + - value: user + description: The message author role is user. + - value: assistant + description: The message author role is assistant. + - value: tool + description: The message author role is tool. + - value: function + description: Deprecated. The message author role is function. + chatCompletionRequestMessageSystem: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + chatCompletionRequestMessageUser: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + oneOf: + - type: string + description: The contents of the message. + - minimum: 1.0 + type: array + items: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + description: 'An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4-visual-preview` model.' + nullable: true + chatCompletionRequestMessageContentPart: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionRequestMessageContentPartType' + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/chatCompletionRequestMessageContentPartText' + image_url: '#/components/schemas/chatCompletionRequestMessageContentPartImage' + chatCompletionRequestMessageContentPartType: + enum: + - text + - image_url + type: string + description: The type of the content part. + x-ms-enum: + name: ChatCompletionRequestMessageContentPartType + modelAsString: true + values: + - value: text + description: The content part type is text. + - value: image_url + description: The content part type is image_url. + chatCompletionRequestMessageContentPartText: + required: + - text + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + text: + type: string + description: The text content. + chatCompletionRequestMessageContentPartImage: + required: + - url + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessageContentPart' + - type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + $ref: '#/components/schemas/imageDetailLevel' + imageDetailLevel: + enum: + - auto + - low + - high + type: string + description: Specifies the detail level of the image. + default: auto + x-ms-enum: + name: ImageDetailLevel + modelAsString: true + values: + - value: auto + description: The image detail level is auto. + - value: low + description: The image detail level is low. + - value: high + description: The image detail level is high. + chatCompletionRequestMessageAssistant: + required: + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + azureChatExtensionConfiguration: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/azureChatExtensionType' + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + discriminator: + propertyName: type + mapping: + azure_search: '#/components/schemas/azureSearchChatExtensionConfiguration' + azure_cosmos_db: '#/components/schemas/azureCosmosDBChatExtensionConfiguration' + azureChatExtensionType: + enum: + - azure_search + - azure_cosmos_db + type: string + description: " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI." + x-ms-enum: + name: AzureChatExtensionType + modelAsString: true + values: + - name: azureSearch + value: azure_search + description: Represents the use of Azure Search as an Azure OpenAI chat extension. + - name: azureCosmosDB + value: azure_cosmos_db + description: Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + azureSearchChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureSearchChatExtensionParameters' + description: "A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_search + azureSearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + - $ref: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + endpoint: + type: string + description: The absolute endpoint path for the Azure Search resource to use. + format: uri + index_name: + type: string + description: The name of the index to use as available in the referenced Azure Search resource. + fields_mapping: + $ref: '#/components/schemas/azureSearchIndexFieldMappingOptions' + query_type: + $ref: '#/components/schemas/azureSearchQueryType' + semantic_configuration: + type: string + description: The additional semantic configuration for the query. + filter: + type: string + description: Search filter. + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + description: Parameters for Azure Search when used as an Azure OpenAI chat extension. + azureSearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Search resource. + azureSearchQueryType: + enum: + - simple + - semantic + - vector + - vector_simple_hybrid + - vector_semantic_hybrid + type: string + description: The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension. + x-ms-enum: + name: azureSearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: 'Represents the default, simple query parser.' + - name: semantic + value: semantic + description: Represents the semantic query parser for advanced semantic modeling. + - name: vector + value: vector + description: Represents vector search over computed data. + - name: vectorSimpleHybrid + value: vector_simple_hybrid + description: Represents a combination of the simple query strategy with vector data. + - name: vectorSemanticHybrid + value: vector_semantic_hybrid + description: Represents a combination of semantic search and vector data querying. + azureCosmosDBChatExtensionConfiguration: + required: + - parameters + allOf: + - $ref: '#/components/schemas/azureChatExtensionConfiguration' + - properties: + parameters: + $ref: '#/components/schemas/azureCosmosDBChatExtensionParameters' + description: "A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\nextension." + x-ms-discriminator-value: azure_cosmos_db + azureCosmosDBChatExtensionParameters: + required: + - authentication + - container_name + - database_name + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5.0 + minimum: 1.0 + type: integer + description: 'The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.' + format: int32 + role_information: + type: string + description: 'Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant''s personality and tell it how to format responses. There''s a 100 token limit for it, and it counts against the overall token limit.' + database_name: + type: string + description: The MongoDB vCore database name to use with Azure Cosmos DB. + container_name: + type: string + description: The name of the Azure Cosmos DB resource container. + index_name: + type: string + description: The MongoDB vCore index name to use with Azure Cosmos DB. + fields_mapping: + $ref: '#/components/schemas/azureCosmosDBFieldMappingOptions' + embedding_dependency: + oneOf: + - $ref: '#/components/schemas/onYourDataEndpointVectorizationSource' + - $ref: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + description: "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\nMongoDB vCore." + azureCosmosDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + items: + type: string + description: The names of index fields that should be treated as content. + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + items: + type: string + description: The names of fields that represent vector data. + description: Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource. + onYourDataAuthenticationOptions: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataAuthenticationType' + description: The authentication options for Azure OpenAI On Your Data. + discriminator: + propertyName: type + mapping: + api_key: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + connection_string: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + system_assigned_managed_identity: '#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions' + user_assigned_managed_identity: '#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions' + onYourDataAuthenticationType: + enum: + - api_key + - connection_string + - system_assigned_managed_identity + - user_assigned_managed_identity + type: string + description: The authentication types supported with Azure OpenAI On Your Data. + x-ms-enum: + name: OnYourDataAuthenticationType + modelAsString: true + values: + - name: apiKey + value: api_key + description: Authentication via API key. + - name: connectionString + value: connection_string + description: Authentication via connection string. + - name: systemAssignedManagedIdentity + value: system_assigned_managed_identity + description: Authentication via system-assigned managed identity. + - name: userAssignedManagedIdentity + value: user_assigned_managed_identity + description: Authentication via user-assigned managed identity. + onYourDataApiKeyAuthenticationOptions: + required: + - key + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + key: + type: string + description: The API key to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using an API key. + x-ms-discriminator-value: api_key + onYourDataConnectionStringAuthenticationOptions: + required: + - connection_string + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + connection_string: + type: string + description: The connection string to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a connection string. + x-ms-discriminator-value: connection_string + onYourDataSystemAssignedManagedIdentityAuthenticationOptions: + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + description: The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity. + x-ms-discriminator-value: system_assigned_managed_identity + onYourDataUserAssignedManagedIdentityAuthenticationOptions: + required: + - managed_identity_resource_id + allOf: + - $ref: '#/components/schemas/onYourDataAuthenticationOptions' + - properties: + managed_identity_resource_id: + type: string + description: The resource ID of the user-assigned managed identity to use for authentication. + description: The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity. + x-ms-discriminator-value: user_assigned_managed_identity + onYourDataVectorizationSource: + required: + - type + type: object + properties: + type: + $ref: '#/components/schemas/onYourDataVectorizationSourceType' + description: An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search. + discriminator: + propertyName: type + mapping: + endpoint: '#/components/schemas/onYourDataEndpointVectorizationSource' + deployment_name: '#/components/schemas/onYourDataDeploymentNameVectorizationSource' + onYourDataVectorizationSourceType: + enum: + - endpoint + - deployment_name + type: string + description: "Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\nvector search." + x-ms-enum: + name: OnYourDataVectorizationSourceType + modelAsString: true + values: + - name: endpoint + value: endpoint + description: Represents vectorization performed by public service calls to an Azure OpenAI embedding model. + - name: deploymentName + value: deployment_name + description: "Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\nsearch even in private networks." + onYourDataDeploymentNameVectorizationSource: + required: + - deployment_name + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + deployment_name: + type: string + description: 'Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks.' + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non an internal embeddings model deployment name in the same Azure OpenAI resource." + x-ms-discriminator-value: deployment_name + onYourDataEndpointVectorizationSource: + required: + - authentication + - endpoint + allOf: + - $ref: '#/components/schemas/onYourDataVectorizationSource' + - properties: + authentication: + $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + endpoint: + type: string + description: 'Specifies the endpoint to use for vectorization. This endpoint must be in the same Azure OpenAI resource, but On Your Data will use this endpoint via an internal call rather than a public one, which enables vector search even in private networks.' + format: uri + description: "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a public Azure OpenAI endpoint call for embeddings." + x-ms-discriminator-value: endpoint + azureChatExtensionsMessageContext: + type: object + properties: + citations: + type: array + items: + $ref: '#/components/schemas/citation' + description: 'The data source retrieval result, used to generate the assistant message in the response.' + x-ms-identifiers: [ ] + intent: + type: string + description: 'The detected intent from the chat history, used to pass to the next turn to carry over the context.' + description: " A representation of the additional context information available when Azure OpenAI chat extensions are involved\n in the generation of a corresponding chat completions response. This context information is only populated when\n using an Azure OpenAI request configured to use a matching extension." + citation: + required: + - content + type: object + properties: + content: + type: string + description: The content of the citation. + title: + type: string + description: The title of the citation. + url: + type: string + description: The URL of the citation. + filepath: + type: string + description: The file path of the citation. + chunk_id: + type: string + description: The chunk ID of the citation. + description: citation information for a chat completions response message. + chatCompletionMessageToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + $ref: '#/components/schemas/toolCallType' + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: The function that the model called. + toolCallType: + enum: + - function + type: string + description: 'The type of the tool call, in this case `function`.' + x-ms-enum: + name: ToolCallType + modelAsString: true + values: + - value: function + description: The tool call type is function. + chatCompletionRequestMessageTool: + required: + - tool_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + tool_call_id: + type: string + description: Tool call that this message is responding to. + content: + type: string + description: The contents of the message. + nullable: true + nullable: true + chatCompletionRequestMessageFunction: + required: + - function_call_id + - content + allOf: + - $ref: '#/components/schemas/chatCompletionRequestMessage' + - type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + name: + type: string + description: The contents of the message. + content: + type: string + description: The contents of the message. + nullable: true + description: Deprecated. Message that represents a function. + nullable: true + createChatCompletionResponse: + required: + - id + - object + - created + - model + - choices + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionsResponseCommon' + - properties: + prompt_filter_results: + $ref: '#/components/schemas/promptFilterResults' + choices: + type: array + items: + type: object + allOf: + - $ref: '#/components/schemas/chatCompletionChoiceCommon' + - properties: + message: + $ref: '#/components/schemas/chatCompletionResponseMessage' + content_filter_results: + $ref: '#/components/schemas/contentFilterChoiceResults' + logprobs: + $ref: '#/components/schemas/chatCompletionChoiceLogProbs' + chatCompletionChoiceLogProbs: + required: + - content + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/chatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + chatCompletionTokenLogprob: + required: + - token + - logprob + - bytes + - top_logprobs + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + top_logprobs: + type: array + items: + required: + - token + - logprob + - bytes + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: The log probability of this token. + bytes: + type: array + items: + type: integer + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + nullable: true + description: 'List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned.' + chatCompletionResponseMessage: + type: object + properties: + role: + $ref: '#/components/schemas/chatCompletionResponseMessageRole' + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/chatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + function_call: + $ref: '#/components/schemas/chatCompletionFunctionCall' + context: + $ref: '#/components/schemas/azureChatExtensionsMessageContext' + description: A chat completion message generated by the model. + chatCompletionResponseMessageRole: + enum: + - assistant + type: string + description: The role of the author of the response message. + chatCompletionToolChoiceOption: + oneOf: + - enum: + - none + - auto + - required + type: string + description: '`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.' + - $ref: '#/components/schemas/chatCompletionNamedToolChoice' + description: 'Controls which (if any) function is called by the model. `none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function. Specifying a particular function via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that function.' + chatCompletionNamedToolChoice: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: Specifies a tool the model should use. Use to force the model to call a specific function. + chatCompletionFunctionCall: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: 'The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.' + description: 'Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.' + chatCompletionsResponseCommon: + required: + - id + - object + - created + - model + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. + object: + $ref: '#/components/schemas/chatCompletionResponseObject' + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + format: unixtime + model: + type: string + description: The model used for the chat completion. + usage: + $ref: '#/components/schemas/completionUsage' + system_fingerprint: + type: string + description: Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + chatCompletionResponseObject: + enum: + - chat.completion + type: string + description: The object type. + x-ms-enum: + name: ChatCompletionResponseObject + modelAsString: true + values: + - value: chat.completion + description: The object type is chat completion. + completionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + description: Usage statistics for the completion request. + chatCompletionTool: + required: + - type + - function + type: object + properties: + type: + $ref: '#/components/schemas/chatCompletionToolType' + function: + required: + - name + - parameters + type: object + properties: + description: + type: string + description: 'A description of what the function does, used by the model to choose when and how to call the function.' + name: + type: string + description: 'The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + parameters: + $ref: '#/components/schemas/chatCompletionFunctionParameters' + chatCompletionToolType: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-ms-enum: + name: ChatCompletionToolType + modelAsString: true + values: + - value: function + description: The tool type is function. + chatCompletionChoiceCommon: + type: object + properties: + index: + type: integer + finish_reason: + type: string + createTranslationRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + description: Translation request. + audioResponse: + required: + - text + type: object + properties: + text: + type: string + description: Translated or transcribed text. + description: Translation or transcription response when response_format was json + audioVerboseResponse: + required: + - text + type: object + allOf: + - $ref: '#/components/schemas/audioResponse' + - properties: + task: + enum: + - transcribe + - translate + type: string + description: Type of audio task. + x-ms-enum: + modelAsString: true + language: + type: string + description: Language. + duration: + type: number + description: Duration. + segments: + type: array + items: + $ref: '#/components/schemas/audioSegment' + description: Translation or transcription response when response_format was verbose_json + audioResponseFormat: + title: AudioResponseFormat + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: Defines the format of the output. + x-ms-enum: + modelAsString: true + createTranscriptionRequest: + required: + - file + type: object + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: '#/components/schemas/audioResponseFormat' + temperature: + type: number + description: 'The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit.' + default: 0 + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + description: Transcription request. + audioSegment: + type: object + properties: + id: + type: integer + description: Segment identifier. + seek: + type: number + description: Offset of the segment. + start: + type: number + description: Segment start offset. + end: + type: number + description: Segment end offset. + text: + type: string + description: Segment text. + tokens: + type: array + items: + type: number + description: Tokens of the text. + temperature: + type: number + description: Temperature. + avg_logprob: + type: number + description: Average log probability. + compression_ratio: + type: number + description: Compression ratio. + no_speech_prob: + type: number + description: Probability of 'no speech'. + description: Transcription or translation segment. + imageQuality: + enum: + - standard + - hd + type: string + description: The quality of the image that will be generated. + default: standard + x-ms-enum: + name: Quality + modelAsString: true + values: + - value: standard + description: Standard quality creates images with standard quality. + name: Standard + - value: hd + description: HD quality creates images with finer details and greater consistency across the image. + name: HD + imagesResponseFormat: + enum: + - url + - b64_json + type: string + description: The format in which the generated images are returned. + default: url + x-ms-enum: + name: ImagesResponseFormat + modelAsString: true + values: + - value: url + description: The URL that provides temporary access to download the generated images. + name: Url + - value: b64_json + description: The generated images are returned as base64 encoded string. + name: Base64Json + imageSize: + enum: + - 1792x1024 + - 1024x1792 + - 1024x1024 + type: string + description: The size of the generated images. + default: 1024x1024 + x-ms-enum: + name: Size + modelAsString: true + values: + - value: 1792x1024 + description: The desired size of the generated image is 1792x1024 pixels. + name: Size1792x1024 + - value: 1024x1792 + description: The desired size of the generated image is 1024x1792 pixels. + name: Size1024x1792 + - value: 1024x1024 + description: The desired size of the generated image is 1024x1024 pixels. + name: Size1024x1024 + imageStyle: + enum: + - vivid + - natural + type: string + description: The style of the generated images. + default: vivid + x-ms-enum: + name: Style + modelAsString: true + values: + - value: vivid + description: Vivid creates images that are hyper-realistic and dramatic. + name: Vivid + - value: natural + description: Natural creates images that are more natural and less hyper-realistic. + name: Natural + imageGenerationsRequest: + required: + - prompt + type: object + properties: + prompt: + minLength: 1 + type: string + description: A text description of the desired image(s). The maximum length is 4000 characters. + format: string + example: a corgi in a field + n: + maximum: 1.0 + minimum: 1.0 + type: integer + description: The number of images to generate. + default: 1 + size: + $ref: '#/components/schemas/imageSize' + response_format: + $ref: '#/components/schemas/imagesResponseFormat' + user: + type: string + description: 'A unique identifier representing your end-user, which can help to monitor and detect abuse.' + format: string + example: user123456 + quality: + $ref: '#/components/schemas/imageQuality' + style: + $ref: '#/components/schemas/imageStyle' + generateImagesResponse: + required: + - created + - data + type: object + properties: + created: + type: integer + description: The unix timestamp when the operation was created. + format: unixtime + example: '1676540381' + data: + type: array + items: + $ref: '#/components/schemas/imageResult' + description: 'The result data of the operation, if successful' + imageResult: + type: object + properties: + url: + type: string + description: The image url. + example: https://www.contoso.com + b64_json: + type: string + description: The base64 encoded image + content_filter_results: + $ref: '#/components/schemas/dalleContentFilterResults' + revised_prompt: + type: string + description: 'The prompt that was used to generate the image, if there was any revision to the prompt.' + prompt_filter_results: + $ref: '#/components/schemas/dalleFilterResults' + description: 'The image url or encoded image if successful, and an error otherwise.' + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-10-21.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-10-21.yaml new file mode 100644 index 000000000..d478dad36 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-api-spec-2024-10-21.yaml @@ -0,0 +1,2622 @@ +openapi: 3.0.0 +info: + title: Azure OpenAI Service API + description: Azure OpenAI APIs for completions and search + version: '2024-10-21' +servers: + - url: https://REPLACEBYPOLICY/openai + variables: + endpoint: + default: your-resource-name.openai.azure.com +security: + - bearer: + - api.read + - apiKey: [] +paths: + /deployments/{deployment-id}/completions: + post: + summary: Creates a completion for the provided prompt, parameters and chosen model. + operationId: Completions_Create + parameters: + - in: path + name: deployment-id + required: true + schema: + type: string + example: davinci + description: Deployment id of the model which was deployed. + - in: query + name: api-version + required: true + schema: + type: string + example: 2024-10-21 + description: api version + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/createCompletionRequest" + example: + prompt: |- + Negate the following sentence.The price for bubblegum increased on thursday. + + Negated Sentence: + max_tokens: 50 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/createCompletionResponse" + example: + model: davinci + object: text_completion + id: cmpl-4509KAos68kxOqpE2uYGw81j6m7uo + created: 1637097562 + choices: + - index: 0 + text: The price for bubblegum decreased on thursday. + logprobs: null + finish_reason: stop + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + default: + description: Service unavailable + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + /deployments/{deployment-id}/embeddings: + post: + summary: Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms. + operationId: embeddings_create + parameters: + - in: path + name: deployment-id + required: true + schema: + enum: + - embedding + type: string + example: ada-search-index-v1 + default: embedding + description: The deployment id of the model which was deployed. + - in: query + name: api-version + required: true + schema: + type: string + enum: + - '2024-10-21' + example: '2024-10-21' + default: '2024-10-21' + description: api version + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + properties: + input: + description: |- + Input text to get embeddings for, encoded as a string. To get embeddings for multiple inputs in a single request, pass an array of strings. Each input must not exceed 2048 tokens in length. + Unless you are embedding code, we suggest replacing newlines (\n) in your input with a single space, as we have observed inferior results when newlines are present. + oneOf: + - type: string + default: "" + example: This is a test. + nullable: true + - type: array + minItems: 1 + maxItems: 2048 + items: + type: string + minLength: 1 + example: This is a test. + nullable: false + user: + description: A unique identifier representing your end-user, which can help monitoring and detecting abuse. + type: string + nullable: false + input_type: + description: input type of embedding search to use + type: string + example: query + encoding_format: + description: The format to return the embeddings in. Can be either `float` or `base64`. Defaults to `float`. + type: string + example: base64 + nullable: true + dimensions: + description: The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models. + type: integer + example: 1 + nullable: true + required: + - input + example: + input: APIM love OpenAI + responses: + "200": + description: OK + content: + application/json: + schema: + type: object + properties: + object: + type: string + model: + type: string + data: + type: array + items: + type: object + properties: + index: + type: integer + object: + type: string + embedding: + type: array + items: + type: number + required: + - index + - object + - embedding + usage: + type: object + properties: + prompt_tokens: + type: integer + total_tokens: + type: integer + required: + - prompt_tokens + - total_tokens + required: + - object + - model + - data + - usage + x-ms-examples: + Create a embeddings.: + $ref: ./examples/embeddings.yaml + /deployments/{deployment-id}/chat/completions: + post: + summary: Creates a completion for the chat message + operationId: ChatCompletions_Create + parameters: + - in: path + name: deployment-id + required: true + schema: + type: string + default: chat + description: Deployment id of the model which was deployed. + enum: + - chat + - gpt-4o + - in: query + name: api-version + required: true + schema: + enum: + - '2024-10-21' + type: string + default: '2024-10-21' + example: '2024-10-21' + description: api version + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/createChatCompletionRequest" + example: + model: chat + messages: + - role: system + content: You are a helpful assistant that responds in Markdown. Help me with my math homework! + - role: user + content: How to calculate the distance between earth and moon? + responses: + "200": + description: OK + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/createChatCompletionResponse" + - $ref: "#/components/schemas/createChatCompletionStreamResponse" + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + default: + description: Service unavailable + content: + application/json: + schema: + $ref: "#/components/schemas/errorResponse" + headers: + apim-request-id: + description: Request ID for troubleshooting purposes + schema: + type: string + x-ms-examples: + Create a chat completion.: + $ref: ./examples/chat_completions.yaml + Creates a completion based on Azure Search data and system-assigned managed identity.: + $ref: ./examples/chat_completions_azure_search_minimum.yaml + Creates a completion based on Azure Search vector data, previous assistant message and user-assigned managed identity.: + $ref: ./examples/chat_completions_azure_search_advanced.yaml + Creates a completion for the provided Azure Cosmos DB.: + $ref: ./examples/chat_completions_cosmos_db.yaml + /deployments/{deployment-id}/audio/transcriptions: + post: + summary: Transcribes audio into the input language. + operationId: Transcriptions_Create + parameters: + - in: path + name: deployment-id + required: true + schema: + type: string + example: whisper + description: Deployment id of the whisper model. + - in: query + name: api-version + required: true + schema: + type: string + example: '2024-10-21' + description: api version + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/createTranscriptionRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + text/plain: + schema: + type: string + description: Transcribed text in the output format (when response_format was one of text, vtt or srt). + x-ms-examples: + Create an audio transcription with json response format.: + $ref: ./examples/audio_transcription_object.yaml + Create an audio transcription with text response format.: + $ref: ./examples/audio_transcription_text.yaml + /deployments/{deployment-id}/audio/translations: + post: + summary: Transcribes and translates input audio into English text. + operationId: Translations_Create + parameters: + - in: path + name: deployment-id + required: true + schema: + type: string + example: whisper + description: Deployment id of the whisper model which was deployed. + - in: query + name: api-version + required: true + schema: + type: string + example: '2024-10-21' + description: api version + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/createTranslationRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/audioResponse' + - $ref: '#/components/schemas/audioVerboseResponse' + text/plain: + schema: + type: string + description: Transcribed text in the output format (when response_format was one of text, vtt or srt). + x-ms-examples: + Create an audio translation with json response format.: + $ref: ./examples/audio_translation_object.yaml + Create an audio translation with text response format.: + $ref: ./examples/audio_translation_text.yaml + /deployments/{deployment-id}/images/generations: + post: + summary: Generates a batch of images from a text caption on a given DALLE model deployment + operationId: ImageGenerations_Create + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/imageGenerationsRequest' + parameters: + - in: path + name: deployment-id + required: true + schema: + type: string + example: dalle-deployment + description: Deployment id of the dalle model which was deployed. + - in: query + name: api-version + required: true + schema: + type: string + example: '2024-10-21' + description: api version + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/generateImagesResponse' + default: + description: An error occurred. + content: + application/json: + schema: + $ref: '#/components/schemas/dalleErrorResponse' + x-ms-examples: + Create an image.: + $ref: ./examples/image_generation.yaml +components: + schemas: + errorResponse: + type: object + properties: + error: + $ref: "#/components/schemas/error" + errorBase: + type: object + properties: + code: + type: string + message: + type: string + error: + type: object + allOf: + - $ref: "#/components/schemas/errorBase" + properties: + param: + type: string + type: + type: string + inner_error: + $ref: "#/components/schemas/innerError" + innerError: + description: Inner error with additional details. + type: object + properties: + code: + $ref: "#/components/schemas/innerErrorCode" + content_filter_results: + $ref: "#/components/schemas/contentFilterPromptResults" + innerErrorCode: + description: Error codes for the inner error object. + enum: + - ResponsibleAIPolicyViolation + type: string + x-ms-enum: + name: InnerErrorCode + modelAsString: true + values: + - value: ResponsibleAIPolicyViolation + description: The prompt violated one of more content filter rules. + dalleErrorResponse: + type: object + properties: + error: + $ref: "#/components/schemas/dalleError" + dalleError: + type: object + allOf: + - $ref: "#/components/schemas/errorBase" + properties: + param: + type: string + type: + type: string + inner_error: + $ref: "#/components/schemas/dalleInnerError" + dalleInnerError: + description: Inner error with additional details. + type: object + properties: + code: + $ref: "#/components/schemas/innerErrorCode" + content_filter_results: + $ref: "#/components/schemas/dalleFilterResults" + revised_prompt: + type: string + description: The prompt that was used to generate the image, if there was any revision to the prompt. + contentFilterResultBase: + type: object + properties: + filtered: + type: boolean + required: + - filtered + contentFilterSeverityResult: + type: object + allOf: + - $ref: "#/components/schemas/contentFilterResultBase" + - properties: + severity: + type: string + enum: + - safe + - low + - medium + - high + x-ms-enum: + name: ContentFilterSeverity + modelAsString: true + values: + - value: safe + description: General content or related content in generic or non-harmful contexts. + - value: low + description: Harmful content at a low intensity and risk level. + - value: medium + description: Harmful content at a medium intensity and risk level. + - value: high + description: Harmful content at a high intensity and risk level. + required: + - severity + - filtered + contentFilterDetectedResult: + type: object + allOf: + - $ref: "#/components/schemas/contentFilterResultBase" + - properties: + detected: + type: boolean + required: + - detected + - filtered + contentFilterDetectedWithCitationResult: + type: object + allOf: + - $ref: "#/components/schemas/contentFilterDetectedResult" + - properties: + citation: + type: object + properties: + URL: + type: string + license: + type: string + required: + - detected + - filtered + contentFilterResultsBase: + type: object + description: Information about the content filtering results. + properties: + sexual: + $ref: "#/components/schemas/contentFilterSeverityResult" + violence: + $ref: "#/components/schemas/contentFilterSeverityResult" + hate: + $ref: "#/components/schemas/contentFilterSeverityResult" + self_harm: + $ref: "#/components/schemas/contentFilterSeverityResult" + profanity: + $ref: "#/components/schemas/contentFilterDetectedResult" + error: + $ref: "#/components/schemas/errorBase" + contentFilterPromptResults: + type: object + description: Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id. + allOf: + - $ref: "#/components/schemas/contentFilterResultsBase" + - properties: + jailbreak: + $ref: "#/components/schemas/contentFilterDetectedResult" + contentFilterChoiceResults: + type: object + description: Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id. + allOf: + - $ref: "#/components/schemas/contentFilterResultsBase" + - properties: + protected_material_text: + $ref: "#/components/schemas/contentFilterDetectedResult" + - properties: + protected_material_code: + $ref: "#/components/schemas/contentFilterDetectedWithCitationResult" + promptFilterResult: + type: object + description: Content filtering results for a single prompt in the request. + properties: + prompt_index: + type: integer + content_filter_results: + $ref: "#/components/schemas/contentFilterPromptResults" + promptFilterResults: + type: array + description: Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders. + items: + $ref: "#/components/schemas/promptFilterResult" + dalleContentFilterResults: + type: object + description: Information about the content filtering results. + properties: + sexual: + $ref: "#/components/schemas/contentFilterSeverityResult" + violence: + $ref: "#/components/schemas/contentFilterSeverityResult" + hate: + $ref: "#/components/schemas/contentFilterSeverityResult" + self_harm: + $ref: "#/components/schemas/contentFilterSeverityResult" + dalleFilterResults: + type: object + description: Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id. + allOf: + - $ref: "#/components/schemas/dalleContentFilterResults" + - properties: + profanity: + $ref: "#/components/schemas/contentFilterDetectedResult" + jailbreak: + $ref: "#/components/schemas/contentFilterDetectedResult" + chatCompletionsRequestCommon: + type: object + properties: + temperature: + description: |- + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + We generally recommend altering this or `top_p` but not both. + type: number + minimum: 0 + maximum: 2 + default: 1 + example: 1 + nullable: true + top_p: + description: |- + An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. + We generally recommend altering this or `temperature` but not both. + type: number + minimum: 0 + maximum: 1 + default: 1 + example: 1 + nullable: true + stream: + description: "If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message." + type: boolean + nullable: true + default: false + stop: + description: Up to 4 sequences where the API will stop generating further tokens. + oneOf: + - type: string + nullable: true + - type: array + items: + type: string + nullable: false + minItems: 1 + maxItems: 4 + description: Array minimum size of 1 and maximum of 4 + default: null + max_tokens: + description: | + The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens). + + This value is now deprecated in favor of `max_completion_tokens`, and is not compatible with o1 series models. + type: integer + default: 4096 + max_completion_tokens: + description: | + An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. + + type: integer + nullable: true + presence_penalty: + description: Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + type: number + default: 0 + minimum: -2 + maximum: 2 + frequency_penalty: + description: Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + type: number + default: 0 + minimum: -2 + maximum: 2 + logit_bias: + description: Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. + type: object + nullable: true + user: + description: A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse. + type: string + example: user-1234 + nullable: false + createCompletionRequest: + type: object + properties: + prompt: + description: &completions_prompt_description | + The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays. + + Note that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document. + default: "<|endoftext|>" + nullable: true + oneOf: + - type: string + default: "" + example: "This is a test." + - type: array + items: + type: string + default: "" + example: "This is a test." + best_of: + type: integer + default: 1 + minimum: 0 + maximum: 20 + nullable: true + description: &completions_best_of_description | + Generates `best_of` completions server-side and returns the "best" (the one with the highest log probability per token). Results cannot be streamed. + + When used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`. + + **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`. + echo: + type: boolean + default: false + nullable: true + description: &completions_echo_description > + Echo back the prompt in addition to the completion + frequency_penalty: + type: number + default: 0 + minimum: -2 + maximum: 2 + nullable: true + description: &completions_frequency_penalty_description | + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + logit_bias: &completions_logit_bias + type: object + x-oaiTypeLabel: map + default: null + nullable: true + additionalProperties: + type: integer + description: &completions_logit_bias_description | + Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. + + As an example, you can pass `{"50256": -100}` to prevent the <|endoftext|> token from being generated. + logprobs: &completions_logprobs_configuration + type: integer + minimum: 0 + maximum: 5 + default: null + nullable: true + description: &completions_logprobs_description | + Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response. + + The maximum value for `logprobs` is 5. + max_tokens: + type: integer + minimum: 0 + default: 16 + example: 16 + nullable: true + description: &completions_max_tokens_description | + The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion. + + The token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. + n: + type: integer + minimum: 1 + maximum: 128 + default: 1 + example: 1 + nullable: true + description: &completions_completions_description | + How many completions to generate for each prompt. + + **Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`. + presence_penalty: + type: number + default: 0 + minimum: -2 + maximum: 2 + nullable: true + description: &completions_presence_penalty_description | + Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + seed: &completions_seed_param + type: integer + minimum: -9223372036854775808 + maximum: 9223372036854775807 + nullable: true + description: | + If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. + + Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + stop: + description: &completions_stop_description > + Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence. + default: null + nullable: true + oneOf: + - type: string + default: <|endoftext|> + example: "\n" + nullable: true + - type: array + minItems: 1 + maxItems: 4 + items: + type: string + example: '["\n"]' + stream: + description: > + Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + type: boolean + nullable: true + default: false + suffix: + description: | + The suffix that comes after a completion of inserted text. + + This parameter is only supported for `gpt-3.5-turbo-instruct`. + default: null + nullable: true + type: string + example: "test." + stream_options: + $ref: "#/components/schemas/chatCompletionStreamOptions" + temperature: + type: number + minimum: 0 + maximum: 2 + default: 1 + example: 1 + nullable: true + description: &completions_temperature_description | + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + + We generally recommend altering this or `top_p` but not both. + top_p: + type: number + minimum: 0 + maximum: 1 + default: 1 + example: 1 + nullable: true + description: &completions_top_p_description | + An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. + + We generally recommend altering this or `temperature` but not both. + user: &end_user_param_configuration + type: string + example: user-1234 + description: | + A unique identifier representing your end-user, which can help to monitor and detect abuse. + required: + - prompt + + createCompletionResponse: + type: object + description: | + Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint). + properties: + id: + type: string + description: A unique identifier for the completion. + choices: + type: array + description: The list of completion choices the model generated for the input prompt. + items: + type: object + required: + - finish_reason + - index + - logprobs + - text + properties: + finish_reason: + type: string + description: &completion_finish_reason_description | + The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence, + `length` if the maximum number of tokens specified in the request was reached, + or `content_filter` if content was omitted due to a flag from our content filters. + enum: ["stop", "length", "content_filter"] + index: + type: integer + logprobs: + type: object + nullable: true + properties: + text_offset: + type: array + items: + type: integer + token_logprobs: + type: array + items: + type: number + tokens: + type: array + items: + type: string + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + text: + type: string + content_filter_results: + $ref: "#/components/schemas/contentFilterChoiceResults" + created: + type: integer + description: The Unix timestamp (in seconds) of when the completion was created. + model: + type: string + description: The model used for completion. + prompt_filter_results: + $ref: "#/components/schemas/promptFilterResults" + system_fingerprint: + type: string + description: | + This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + object: + type: string + description: The object type, which is always "text_completion" + enum: [text_completion] + usage: + $ref: "#/components/schemas/completionUsage" + required: + - id + - object + - created + - model + - choices + + createChatCompletionRequest: + type: object + allOf: + - $ref: "#/components/schemas/chatCompletionsRequestCommon" + - properties: + messages: + description: A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb). + type: array + minItems: 1 + items: + $ref: "#/components/schemas/chatCompletionRequestMessage" + data_sources: + type: array + description: |2- + The configuration entries for Azure OpenAI chat extensions that use them. + This additional specification is only compatible with Azure OpenAI. + items: + $ref: "#/components/schemas/azureChatExtensionConfiguration" + frequency_penalty: + type: number + default: 0 + minimum: -2 + maximum: 2 + nullable: true + description: *completions_frequency_penalty_description + logit_bias: + type: object + default: null + nullable: true + additionalProperties: + type: integer + description: | + Modify the likelihood of specified tokens appearing in the completion. + + Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token. + logprobs: + description: Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of `message`. + type: boolean + default: false + nullable: true + top_logprobs: + description: An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to `true` if this parameter is used. + type: integer + minimum: 0 + maximum: 20 + nullable: true + max_tokens: + description: | + The maximum number of [tokens](/tokenizer) that can be generated in the chat completion. + + The total length of input tokens and generated tokens is limited by the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. + type: integer + nullable: true + n: + type: integer + minimum: 1 + maximum: 128 + default: 1 + example: 1 + nullable: true + description: How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + parallel_tool_calls: + $ref: "#/components/schemas/ParallelToolCalls" + presence_penalty: + type: number + default: 0 + minimum: -2 + maximum: 2 + nullable: true + description: *completions_presence_penalty_description + response_format: + description: | + An object specifying the format that the model must output. Compatible with [GPT-4o](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4o mini](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4 Turbo](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models) and all [GPT-3.5](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-35) Turbo models newer than `gpt-3.5-turbo-1106`. + + Setting to `{ "type": "json_schema", "json_schema": {...} }` enables Structured Outputs which guarantees the model will match your supplied JSON schema. + + Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON. + + **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly "stuck" request. Also note that the message content may be partially cut off if `finish_reason="length"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length. + oneOf: + - $ref: "#/components/schemas/ResponseFormatText" + - $ref: "#/components/schemas/ResponseFormatJsonObject" + - $ref: "#/components/schemas/ResponseFormatJsonSchema" + x-oaiExpandable: true + seed: + type: integer + minimum: -9223372036854775808 + maximum: 9223372036854775807 + nullable: true + description: | + This feature is in Beta. + If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result. + Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend. + stop: + description: | + Up to 4 sequences where the API will stop generating further tokens. + default: null + oneOf: + - type: string + nullable: true + - type: array + minItems: 1 + maxItems: 4 + items: + type: string + stream: + description: > + If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) + as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions). + type: boolean + nullable: true + default: false + stream_options: + $ref: "#/components/schemas/chatCompletionStreamOptions" + temperature: + type: number + minimum: 0 + maximum: 2 + default: 1 + example: 1 + nullable: true + description: *completions_temperature_description + top_p: + type: number + minimum: 0 + maximum: 1 + default: 1 + example: 1 + nullable: true + description: *completions_top_p_description + tools: + type: array + description: > + A list of tools the model may call. Currently, only functions are supported as a tool. + Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported. + items: + $ref: "#/components/schemas/chatCompletionTool" + tool_choice: + $ref: "#/components/schemas/chatCompletionToolChoiceOption" + function_call: + deprecated: true + description: | + Deprecated in favor of `tool_choice`. + + Controls which (if any) function is called by the model. + `none` means the model will not call a function and instead generates a message. + `auto` means the model can pick between generating a message or calling a function. + Specifying a particular function via `{"name": "my_function"}` forces the model to call that function. + + `none` is the default when no functions are present. `auto` is the default if functions are present. + oneOf: + - type: string + description: > + `none` means the model will not call a function and instead generates a message. + `auto` means the model can pick between generating a message or calling a function. + enum: [none, auto] + - $ref: "#/components/schemas/chatCompletionFunctionCallOption" + x-oaiExpandable: true + functions: + deprecated: true + description: | + Deprecated in favor of `tools`. + + A list of functions the model may generate JSON inputs for. + type: array + minItems: 1 + maxItems: 128 + items: + $ref: "#/components/schemas/chatCompletionFunctions" + user: *end_user_param_configuration + + required: + - messages + + chatCompletionFunctions: + type: object + deprecated: true + properties: + description: + type: string + description: A description of what the function does, used by the model to choose when and how to call the function. + name: + type: string + description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. + parameters: + $ref: "#/components/schemas/FunctionParameters" + required: + - name + + chatCompletionFunctionCallOption: + type: object + description: > + Specifying a particular function via `{"name": "my_function"}` forces the model to call that function. + properties: + name: + type: string + description: The name of the function to call. + required: + - name + + + chatCompletionRequestMessage: + oneOf: + - $ref: "#/components/schemas/chatCompletionRequestSystemMessage" + - $ref: "#/components/schemas/chatCompletionRequestUserMessage" + - $ref: "#/components/schemas/chatCompletionRequestAssistantMessage" + - $ref: "#/components/schemas/chatCompletionRequestToolMessage" + - $ref: "#/components/schemas/chatCompletionRequestFunctionMessage" + + chatCompletionRequestSystemMessage: + type: object + title: System message + properties: + content: + description: The contents of the system message. + oneOf: + - type: string + description: The contents of the system message. + title: Text content + - type: array + description: An array of content parts with a defined type. For system messages, only type `text` is supported. + title: Array of content parts + items: + $ref: "#/components/schemas/chatCompletionRequestSystemMessageContentPart" + minItems: 1 + role: + type: string + enum: ["system"] + description: The role of the messages author, in this case `system`. + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + required: + - content + - role + + chatCompletionRequestUserMessage: + type: object + title: User message + properties: + content: + description: | + The contents of the user message. + oneOf: + - type: string + description: The text contents of the message. + title: Text content + - type: array + description: An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4o` model. + title: Array of content parts + items: + $ref: "#/components/schemas/chatCompletionRequestUserMessageContentPart" + minItems: 1 + x-oaiExpandable: true + role: + type: string + enum: ["user"] + description: The role of the messages author, in this case `user`. + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + required: + - content + - role + + chatCompletionRequestAssistantMessage: + type: object + title: Assistant message + properties: + content: + nullable: true + oneOf: + - type: string + description: The contents of the assistant message. + title: Text content + - type: array + description: An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`. + title: Array of content parts + items: + $ref: "#/components/schemas/chatCompletionRequestAssistantMessageContentPart" + minItems: 1 + description: | + The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified. + refusal: + nullable: true + type: string + description: The refusal message by the assistant. + role: + type: string + enum: ["assistant"] + description: The role of the messages author, in this case `assistant`. + name: + type: string + description: An optional name for the participant. Provides the model information to differentiate between participants of the same role. + tool_calls: + $ref: "#/components/schemas/chatCompletionMessageToolCalls" + function_call: + type: object + deprecated: true + description: "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model." + nullable: true + properties: + arguments: + type: string + description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + name: + type: string + description: The name of the function to call. + required: + - arguments + - name + required: + - role + + chatCompletionRequestToolMessage: + type: object + title: Tool message + properties: + role: + type: string + enum: ["tool"] + description: The role of the messages author, in this case `tool`. + content: + oneOf: + - type: string + description: The contents of the tool message. + title: Text content + - type: array + description: An array of content parts with a defined type. For tool messages, only type `text` is supported. + title: Array of content parts + items: + $ref: "#/components/schemas/chatCompletionRequestToolMessageContentPart" + minItems: 1 + description: The contents of the tool message. + tool_call_id: + type: string + description: Tool call that this message is responding to. + required: + - role + - content + - tool_call_id + + chatCompletionRequestFunctionMessage: + type: object + title: Function message + deprecated: true + properties: + role: + type: string + enum: ["function"] + description: The role of the messages author, in this case `function`. + content: + nullable: true + type: string + description: The contents of the function message. + name: + type: string + description: The name of the function to call. + required: + - role + - content + - name + + chatCompletionRequestSystemMessageContentPart: + oneOf: + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartText" + + chatCompletionRequestUserMessageContentPart: + oneOf: + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartText" + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartImage" + + chatCompletionRequestAssistantMessageContentPart: + oneOf: + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartText" + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartRefusal" + + chatCompletionRequestToolMessageContentPart: + oneOf: + - $ref: "#/components/schemas/chatCompletionRequestMessageContentPartText" + + chatCompletionRequestMessageContentPartText: + type: object + title: Text content part + properties: + type: + type: string + enum: ["text"] + description: The type of the content part. + text: + type: string + description: The text content. + required: + - type + - text + + chatCompletionRequestMessageContentPartImage: + type: object + title: Image content part + properties: + type: + type: string + enum: ["image_url"] + description: The type of the content part. + image_url: + type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + type: string + description: Specifies the detail level of the image. Learn more in the [Vision guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/gpt-with-vision?tabs=rest%2Csystem-assigned%2Cresource#detail-parameter-settings-in-image-processing-low-high-auto). + enum: ["auto", "low", "high"] + default: "auto" + required: + - url + required: + - type + - image_url + + chatCompletionRequestMessageContentPartRefusal: + type: object + title: Refusal content part + properties: + type: + type: string + enum: ["refusal"] + description: The type of the content part. + refusal: + type: string + description: The refusal message generated by the model. + required: + - type + - refusal + azureChatExtensionConfiguration: + required: + - type + type: object + properties: + type: + $ref: "#/components/schemas/azureChatExtensionType" + description: |2- + A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat + completions request that should use Azure OpenAI chat extensions to augment the response behavior. + The use of this configuration is compatible only with Azure OpenAI. + discriminator: + propertyName: type + mapping: + azure_search: "#/components/schemas/azureSearchChatExtensionConfiguration" + azure_cosmos_db: "#/components/schemas/azureCosmosDBChatExtensionConfiguration" + azureChatExtensionType: + type: string + description: |2- + A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat + completions request that should use Azure OpenAI chat extensions to augment the response behavior. + The use of this configuration is compatible only with Azure OpenAI. + enum: + - azure_search + - azure_cosmos_db + x-ms-enum: + name: AzureChatExtensionType + modelAsString: true + values: + - name: azureSearch + value: azure_search + description: Represents the use of Azure Search as an Azure OpenAI chat extension. + - name: azureCosmosDB + value: azure_cosmos_db + description: Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension. + azureSearchChatExtensionConfiguration: + required: + - parameters + description: |- + A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat + extension. + allOf: + - $ref: "#/components/schemas/azureChatExtensionConfiguration" + - properties: + parameters: + $ref: "#/components/schemas/azureSearchChatExtensionParameters" + x-ms-discriminator-value: azure_search + azureSearchChatExtensionParameters: + required: + - authentication + - endpoint + - index_name + type: object + properties: + authentication: + oneOf: + - $ref: "#/components/schemas/onYourDataApiKeyAuthenticationOptions" + - $ref: "#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions" + - $ref: "#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions" + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5 + minimum: 1 + type: integer + description: The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer. + format: int32 + role_information: + type: string + description: Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit. + endpoint: + type: string + description: The absolute endpoint path for the Azure Search resource to use. + format: uri + index_name: + type: string + description: The name of the index to use as available in the referenced Azure Search resource. + fields_mapping: + $ref: "#/components/schemas/azureSearchIndexFieldMappingOptions" + query_type: + $ref: "#/components/schemas/azureSearchQueryType" + semantic_configuration: + type: string + description: The additional semantic configuration for the query. + filter: + type: string + description: Search filter. + embedding_dependency: + oneOf: + - $ref: "#/components/schemas/onYourDataEndpointVectorizationSource" + - $ref: "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + description: Parameters for Azure Search when used as an Azure OpenAI chat extension. + azureSearchIndexFieldMappingOptions: + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + description: The names of index fields that should be treated as content. + items: + type: string + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + description: The names of fields that represent vector data. + items: + type: string + description: Optional settings to control how fields are processed when using a configured Azure Search resource. + azureSearchQueryType: + type: string + description: The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension. + enum: + - simple + - semantic + - vector + - vector_simple_hybrid + - vector_semantic_hybrid + x-ms-enum: + name: AzureSearchQueryType + modelAsString: true + values: + - name: simple + value: simple + description: Represents the default, simple query parser. + - name: semantic + value: semantic + description: Represents the semantic query parser for advanced semantic modeling. + - name: vector + value: vector + description: Represents vector search over computed data. + - name: vectorSimpleHybrid + value: vector_simple_hybrid + description: Represents a combination of the simple query strategy with vector data. + - name: vectorSemanticHybrid + value: vector_semantic_hybrid + description: Represents a combination of semantic search and vector data querying. + azureCosmosDBChatExtensionConfiguration: + required: + - parameters + description: |- + A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat + extension. + allOf: + - $ref: "#/components/schemas/azureChatExtensionConfiguration" + - properties: + parameters: + $ref: "#/components/schemas/azureCosmosDBChatExtensionParameters" + x-ms-discriminator-value: azure_cosmos_db + azureCosmosDBChatExtensionParameters: + required: + - authentication + - container_name + - database_name + - embedding_dependency + - fields_mapping + - index_name + type: object + properties: + authentication: + $ref: '#/components/schemas/onYourDataConnectionStringAuthenticationOptions' + top_n_documents: + type: integer + description: The configured top number of documents to feature for the configured query. + format: int32 + in_scope: + type: boolean + description: Whether queries should be restricted to use of indexed data. + strictness: + maximum: 5 + minimum: 1 + type: integer + description: The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer. + format: int32 + role_information: + type: string + description: Give the model instructions about how it should behave and any context it should reference when generating a response. You can describe the assistant's personality and tell it how to format responses. There's a 100 token limit for it, and it counts against the overall token limit. + database_name: + type: string + description: The MongoDB vCore database name to use with Azure Cosmos DB. + container_name: + type: string + description: The name of the Azure Cosmos DB resource container. + index_name: + type: string + description: The MongoDB vCore index name to use with Azure Cosmos DB. + fields_mapping: + $ref: "#/components/schemas/azureCosmosDBFieldMappingOptions" + embedding_dependency: + oneOf: + - $ref: "#/components/schemas/onYourDataEndpointVectorizationSource" + - $ref: "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + description: |- + Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for + MongoDB vCore. + azureCosmosDBFieldMappingOptions: + required: + - content_fields + - vector_fields + type: object + properties: + title_field: + type: string + description: The name of the index field to use as a title. + url_field: + type: string + description: The name of the index field to use as a URL. + filepath_field: + type: string + description: The name of the index field to use as a filepath. + content_fields: + type: array + description: The names of index fields that should be treated as content. + items: + type: string + content_fields_separator: + type: string + description: The separator pattern that content fields should use. + vector_fields: + type: array + description: The names of fields that represent vector data. + items: + type: string + description: Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource. + onYourDataAuthenticationOptions: + required: + - type + type: object + properties: + type: + $ref: "#/components/schemas/onYourDataAuthenticationType" + description: The authentication options for Azure OpenAI On Your Data. + discriminator: + propertyName: type + mapping: + api_key: "#/components/schemas/onYourDataApiKeyAuthenticationOptions" + connection_string: "#/components/schemas/onYourDataConnectionStringAuthenticationOptions" + system_assigned_managed_identity: "#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions" + user_assigned_managed_identity: "#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions" + onYourDataAuthenticationType: + type: string + description: The authentication types supported with Azure OpenAI On Your Data. + enum: + - api_key + - connection_string + - system_assigned_managed_identity + - user_assigned_managed_identity + x-ms-enum: + name: OnYourDataAuthenticationType + modelAsString: true + values: + - name: apiKey + value: api_key + description: Authentication via API key. + - name: connectionString + value: connection_string + description: Authentication via connection string. + - name: systemAssignedManagedIdentity + value: system_assigned_managed_identity + description: Authentication via system-assigned managed identity. + - name: userAssignedManagedIdentity + value: user_assigned_managed_identity + description: Authentication via user-assigned managed identity. + onYourDataApiKeyAuthenticationOptions: + required: + - key + description: The authentication options for Azure OpenAI On Your Data when using an API key. + allOf: + - $ref: "#/components/schemas/onYourDataAuthenticationOptions" + - properties: + key: + type: string + description: The API key to use for authentication. + x-ms-discriminator-value: api_key + onYourDataConnectionStringAuthenticationOptions: + required: + - connection_string + description: The authentication options for Azure OpenAI On Your Data when using a connection string. + allOf: + - $ref: "#/components/schemas/onYourDataAuthenticationOptions" + - properties: + connection_string: + type: string + description: The connection string to use for authentication. + x-ms-discriminator-value: connection_string + onYourDataSystemAssignedManagedIdentityAuthenticationOptions: + description: The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity. + allOf: + - $ref: "#/components/schemas/onYourDataAuthenticationOptions" + x-ms-discriminator-value: system_assigned_managed_identity + onYourDataUserAssignedManagedIdentityAuthenticationOptions: + required: + - managed_identity_resource_id + description: The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity. + allOf: + - $ref: "#/components/schemas/onYourDataAuthenticationOptions" + - properties: + managed_identity_resource_id: + type: string + description: The resource ID of the user-assigned managed identity to use for authentication. + x-ms-discriminator-value: user_assigned_managed_identity + onYourDataVectorizationSource: + required: + - type + type: object + properties: + type: + $ref: "#/components/schemas/onYourDataVectorizationSourceType" + description: An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search. + discriminator: + propertyName: type + mapping: + endpoint: "#/components/schemas/onYourDataEndpointVectorizationSource" + deployment_name: "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + onYourDataVectorizationSourceType: + type: string + description: |- + Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with + vector search. + enum: + - endpoint + - deployment_name + x-ms-enum: + name: OnYourDataVectorizationSourceType + modelAsString: true + values: + - name: endpoint + value: endpoint + description: Represents vectorization performed by public service calls to an Azure OpenAI embedding model. + - name: deploymentName + value: deployment_name + description: |- + Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but + On Your Data will use this model deployment via an internal call rather than a public one, which enables vector + search even in private networks. + onYourDataDeploymentNameVectorizationSource: + required: + - deployment_name + description: |- + The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based + on an internal embeddings model deployment name in the same Azure OpenAI resource. + allOf: + - $ref: "#/components/schemas/onYourDataVectorizationSource" + - properties: + deployment_name: + type: string + description: Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks. + x-ms-discriminator-value: deployment_name + onYourDataEndpointVectorizationSource: + required: + - authentication + - endpoint + description: |- + The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based + on a public Azure OpenAI endpoint call for embeddings. + allOf: + - $ref: "#/components/schemas/onYourDataVectorizationSource" + - properties: + authentication: + $ref: '#/components/schemas/onYourDataApiKeyAuthenticationOptions' + endpoint: + type: string + description: Specifies the endpoint to use for vectorization. This endpoint must be in the same Azure OpenAI resource, but On Your Data will use this endpoint via an internal call rather than a public one, which enables vector search even in private networks. + format: uri + x-ms-discriminator-value: endpoint + azureChatExtensionsMessageContext: + type: object + properties: + citations: + type: array + description: The data source retrieval result, used to generate the assistant message in the response. + items: + $ref: "#/components/schemas/citation" + x-ms-identifiers: [] + intent: + type: string + description: The detected intent from the chat history, used to pass to the next turn to carry over the context. + description: |2- + A representation of the additional context information available when Azure OpenAI chat extensions are involved + in the generation of a corresponding chat completions response. This context information is only populated when + using an Azure OpenAI request configured to use a matching extension. + citation: + required: + - content + type: object + properties: + content: + type: string + description: The content of the citation. + title: + type: string + description: The title of the citation. + url: + type: string + description: The URL of the citation. + filepath: + type: string + description: The file path of the citation. + chunk_id: + type: string + description: The chunk ID of the citation. + description: citation information for a chat completions response message. + chatCompletionMessageToolCall: + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + $ref: "#/components/schemas/toolCallType" + function: + type: object + description: The function that the model called. + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + required: + - name + - arguments + required: + - id + - type + - function + toolCallType: + type: string + enum: + - function + description: The type of the tool call, in this case `function`. + x-ms-enum: + name: ToolCallType + modelAsString: true + values: + - value: function + description: The tool call type is function. + chatCompletionRequestMessageTool: + allOf: + - $ref: "#/components/schemas/chatCompletionRequestMessage" + - type: object + nullable: true + properties: + tool_call_id: + type: string + description: Tool call that this message is responding to. + content: + type: string + description: The contents of the message. + nullable: true + required: + - tool_call_id + - content + chatCompletionRequestMessageFunction: + allOf: + - $ref: "#/components/schemas/chatCompletionRequestMessage" + - type: object + description: Deprecated. Message that represents a function. + nullable: true + properties: + role: + type: string + enum: + - function + description: The role of the messages author, in this case `function`. + name: + type: string + description: The contents of the message. + content: + type: string + description: The contents of the message. + nullable: true + required: + - function_call_id + - content + createChatCompletionResponse: + type: object + description: Represents a chat completion response returned by model, based on the provided input. + properties: + id: + type: string + description: A unique identifier for the chat completion. + prompt_filter_results: + $ref: "#/components/schemas/promptFilterResults" + choices: + type: array + description: A list of chat completion choices. Can be more than one if `n` is greater than 1. + items: + type: object + required: + - finish_reason + - index + - message + - logprobs + properties: + finish_reason: + type: string + description: &chat_completion_finish_reason_description | + The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence, + `length` if the maximum number of tokens specified in the request was reached, + `content_filter` if content was omitted due to a flag from our content filters, + `tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function. + enum: + [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call", + ] + index: + type: integer + description: The index of the choice in the list of choices. + message: + $ref: "#/components/schemas/chatCompletionResponseMessage" + content_filter_results: + $ref: "#/components/schemas/contentFilterChoiceResults" + logprobs: &chat_completion_response_logprobs + description: Log probability information for the choice. + type: object + nullable: true + properties: + content: + description: A list of message content tokens with log probability information. + type: array + items: + $ref: "#/components/schemas/chatCompletionTokenLogprob" + nullable: true + refusal: + description: A list of message refusal tokens with log probability information. + type: array + items: + $ref: "#/components/schemas/chatCompletionTokenLogprob" + nullable: true + required: + - content + - refusal + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. + model: + type: string + description: The model used for the chat completion. + system_fingerprint: + type: string + description: | + This fingerprint represents the backend configuration that the model runs with. + + Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + object: + type: string + description: The object type, which is always `chat.completion`. + enum: [chat.completion] + usage: + $ref: "#/components/schemas/completionUsage" + required: + - choices + - created + - id + - model + - object + + createChatCompletionStreamResponse: + type: object + description: Represents a streamed chunk of a chat completion response returned by model, based on the provided input. + properties: + id: + type: string + description: A unique identifier for the chat completion. Each chunk has the same ID. + choices: + type: array + description: | + A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. + items: + type: object + required: + - delta + - finish_reason + - index + properties: + delta: + $ref: "#/components/schemas/chatCompletionStreamResponseDelta" + logprobs: *chat_completion_response_logprobs + finish_reason: + type: string + description: *chat_completion_finish_reason_description + enum: + [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call", + ] + nullable: true + index: + type: integer + description: The index of the choice in the list of choices. + created: + type: integer + description: The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp. + model: + type: string + description: The model to generate the completion. + system_fingerprint: + type: string + description: | + This fingerprint represents the backend configuration that the model runs with. + Can be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism. + object: + type: string + description: The object type, which is always `chat.completion.chunk`. + enum: [chat.completion.chunk] + required: + - prompt_tokens + - completion_tokens + - total_tokens + required: + - choices + - created + - id + - model + - object + + chatCompletionStreamResponseDelta: + type: object + description: A chat completion delta generated by streamed model responses. + properties: + content: + type: string + description: The contents of the chunk message. + nullable: true + function_call: + deprecated: true + type: object + description: "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model." + properties: + arguments: + type: string + description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + name: + type: string + description: The name of the function to call. + tool_calls: + type: array + items: + $ref: "#/components/schemas/chatCompletionMessageToolCallChunk" + role: + type: string + enum: ["system", "user", "assistant", "tool"] + description: The role of the author of this message. + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + + chatCompletionMessageToolCallChunk: + type: object + properties: + index: + type: integer + id: + type: string + description: The ID of the tool call. + type: + type: string + enum: ["function"] + description: The type of the tool. Currently, only `function` is supported. + function: + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + required: + - index + + chatCompletionStreamOptions: + description: | + Options for streaming response. Only set this when you set `stream: true`. + type: object + nullable: true + default: null + properties: + include_usage: + type: boolean + description: | + If set, an additional chunk will be streamed before the `data: [DONE]` message. The `usage` field on this chunk shows the token usage statistics for the entire request, and the `choices` field will always be an empty array. All other chunks will also include a `usage` field, but with a null value. + + chatCompletionChoiceLogProbs: + description: Log probability information for the choice. + type: object + nullable: true + properties: + content: + description: A list of message content tokens with log probability information. + type: array + items: + $ref: "#/components/schemas/chatCompletionTokenLogprob" + nullable: true + refusal: + description: A list of message refusal tokens with log probability information. + type: array + items: + $ref: "#/components/schemas/chatCompletionTokenLogprob" + nullable: true + required: + - content + chatCompletionTokenLogprob: + type: object + properties: + token: + description: The token. + type: string + logprob: + description: The log probability of this token. + type: number + bytes: + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + type: array + items: + type: integer + nullable: true + top_logprobs: + description: List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned. + type: array + items: + type: object + properties: + token: + description: The token. + type: string + logprob: + description: The log probability of this token. + type: number + bytes: + description: A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token. + type: array + items: + type: integer + nullable: true + required: + - token + - logprob + - bytes + required: + - token + - logprob + - bytes + - top_logprobs + + chatCompletionResponseMessage: + type: object + description: A chat completion message generated by the model. + properties: + role: + $ref: "#/components/schemas/chatCompletionResponseMessageRole" + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + content: + type: string + description: The contents of the message. + nullable: true + tool_calls: + type: array + description: The tool calls generated by the model, such as function calls. + items: + $ref: "#/components/schemas/chatCompletionMessageToolCall" + function_call: + $ref: "#/components/schemas/chatCompletionFunctionCall" + context: + $ref: "#/components/schemas/azureChatExtensionsMessageContext" + required: + - role + - content + - refusal + chatCompletionResponseMessageRole: + type: string + enum: + - assistant + description: The role of the author of the response message. + chatCompletionToolChoiceOption: + description: | + Controls which (if any) tool is called by the model. + `none` means the model will not call any tool and instead generates a message. + `auto` means the model can pick between generating a message or calling one or more tools. + `required` means the model must call one or more tools. + Specifying a particular tool via `{"type": "function", "function": {"name": "my_function"}}` forces the model to call that tool. + `none` is the default when no tools are present. `auto` is the default if tools are present. + oneOf: + - type: string + description: > + `none` means the model will not call any tool and instead generates a message. + `auto` means the model can pick between generating a message or calling one or more tools. + `required` means the model must call one or more tools. + enum: + - none + - auto + - required + - $ref: "#/components/schemas/chatCompletionNamedToolChoice" + chatCompletionNamedToolChoice: + type: object + description: Specifies a tool the model should use. Use to force the model to call a specific function. + properties: + type: + type: string + enum: + - "function" + description: The type of the tool. Currently, only `function` is supported. + function: + type: object + properties: + name: + type: string + description: The name of the function to call. + required: + - name + required: + - type + - function + + ParallelToolCalls: + description: Whether to enable parallel function calling during tool use. + type: boolean + default: true + + chatCompletionMessageToolCalls: + type: array + description: The tool calls generated by the model, such as function calls. + items: + $ref: "#/components/schemas/chatCompletionMessageToolCall" + + chatCompletionFunctionCall: + type: object + description: Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model. + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + required: + - name + - arguments + completionUsage: + type: object + description: Usage statistics for the completion request. + properties: + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + completion_tokens_details: + type: object + description: Breakdown of tokens used in a completion. + properties: + reasoning_tokens: + type: integer + description: Tokens generated by the model for reasoning. + required: + - prompt_tokens + - completion_tokens + - total_tokens + chatCompletionTool: + type: object + properties: + type: + type: string + enum: ["function"] + description: The type of the tool. Currently, only `function` is supported. + function: + $ref: "#/components/schemas/FunctionObject" + required: + - type + - function + + FunctionParameters: + type: object + description: "The parameters the functions accepts, described as a JSON Schema object. See the guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. \n\nOmitting `parameters` defines a function with an empty parameter list." + additionalProperties: true + + FunctionObject: + type: object + properties: + description: + type: string + description: A description of what the function does, used by the model to choose when and how to call the function. + name: + type: string + description: The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. + parameters: + $ref: "#/components/schemas/FunctionParameters" + strict: + type: boolean + nullable: true + default: false + description: Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](docs/guides/function-calling). + required: + - name + + ResponseFormatText: + type: object + properties: + type: + type: string + description: "The type of response format being defined: `text`" + enum: ["text"] + required: + - type + + ResponseFormatJsonObject: + type: object + properties: + type: + type: string + description: "The type of response format being defined: `json_object`" + enum: ["json_object"] + required: + - type + + ResponseFormatJsonSchemaSchema: + type: object + description: "The schema for the response format, described as a JSON Schema object." + additionalProperties: true + + ResponseFormatJsonSchema: + type: object + properties: + type: + type: string + description: "The type of response format being defined: `json_schema`" + enum: ["json_schema"] + json_schema: + type: object + properties: + description: + type: string + description: A description of what the response format is for, used by the model to determine how to respond in the format. + name: + type: string + description: The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. + schema: + $ref: "#/components/schemas/ResponseFormatJsonSchemaSchema" + strict: + type: boolean + nullable: true + default: false + description: Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`. + required: + - type + - name + required: + - type + - json_schema + + chatCompletionChoiceCommon: + type: object + properties: + index: + type: integer + finish_reason: + type: string + createTranslationRequest: + type: object + description: Translation request. + properties: + file: + type: string + description: The audio file to translate. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English. + response_format: + $ref: "#/components/schemas/audioResponseFormat" + temperature: + type: number + default: 0 + description: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit. + required: + - file + audioResponse: + description: Translation or transcription response when response_format was json + type: object + properties: + text: + type: string + description: Translated or transcribed text. + required: + - text + audioVerboseResponse: + description: Translation or transcription response when response_format was verbose_json + type: object + allOf: + - $ref: "#/components/schemas/audioResponse" + - properties: + task: + type: string + description: Type of audio task. + enum: + - transcribe + - translate + x-ms-enum: + modelAsString: true + language: + type: string + description: Language. + duration: + type: number + description: Duration. + segments: + type: array + items: + $ref: "#/components/schemas/audioSegment" + required: + - text + audioResponseFormat: + title: AudioResponseFormat + description: Defines the format of the output. + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + x-ms-enum: + modelAsString: true + createTranscriptionRequest: + type: object + description: Transcription request. + properties: + file: + type: string + description: The audio file object to transcribe. + format: binary + prompt: + type: string + description: An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language. + response_format: + $ref: "#/components/schemas/audioResponseFormat" + temperature: + type: number + default: 0 + description: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit. + language: + type: string + description: The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency. + required: + - file + audioSegment: + type: object + description: Transcription or translation segment. + properties: + id: + type: integer + description: Segment identifier. + seek: + type: number + description: Offset of the segment. + start: + type: number + description: Segment start offset. + end: + type: number + description: Segment end offset. + text: + type: string + description: Segment text. + tokens: + type: array + items: + type: number + nullable: false + description: Tokens of the text. + temperature: + type: number + description: Temperature. + avg_logprob: + type: number + description: Average log probability. + compression_ratio: + type: number + description: Compression ratio. + no_speech_prob: + type: number + description: Probability of 'no speech'. + imageQuality: + description: The quality of the image that will be generated. + type: string + enum: + - standard + - hd + default: standard + x-ms-enum: + name: Quality + modelAsString: true + values: + - value: standard + description: Standard quality creates images with standard quality. + name: Standard + - value: hd + description: HD quality creates images with finer details and greater consistency across the image. + name: HD + imagesResponseFormat: + description: The format in which the generated images are returned. + type: string + enum: + - url + - b64_json + default: url + x-ms-enum: + name: ImagesResponseFormat + modelAsString: true + values: + - value: url + description: The URL that provides temporary access to download the generated images. + name: Url + - value: b64_json + description: The generated images are returned as base64 encoded string. + name: Base64Json + imageSize: + description: The size of the generated images. + type: string + enum: + - 1792x1024 + - 1024x1792 + - 1024x1024 + default: 1024x1024 + x-ms-enum: + name: Size + modelAsString: true + values: + - value: 1792x1024 + description: The desired size of the generated image is 1792x1024 pixels. + name: Size1792x1024 + - value: 1024x1792 + description: The desired size of the generated image is 1024x1792 pixels. + name: Size1024x1792 + - value: 1024x1024 + description: The desired size of the generated image is 1024x1024 pixels. + name: Size1024x1024 + imageStyle: + description: The style of the generated images. + type: string + enum: + - vivid + - natural + default: vivid + x-ms-enum: + name: Style + modelAsString: true + values: + - value: vivid + description: Vivid creates images that are hyper-realistic and dramatic. + name: Vivid + - value: natural + description: Natural creates images that are more natural and less hyper-realistic. + name: Natural + imageGenerationsRequest: + type: object + properties: + prompt: + description: A text description of the desired image(s). The maximum length is 4000 characters. + type: string + format: string + example: a corgi in a field + minLength: 1 + "n": + description: The number of images to generate. + type: integer + minimum: 1 + maximum: 1 + default: 1 + size: + $ref: "#/components/schemas/imageSize" + response_format: + $ref: "#/components/schemas/imagesResponseFormat" + user: + description: A unique identifier representing your end-user, which can help to monitor and detect abuse. + type: string + format: string + example: user123456 + quality: + $ref: "#/components/schemas/imageQuality" + style: + $ref: "#/components/schemas/imageStyle" + required: + - prompt + generateImagesResponse: + type: object + properties: + created: + type: integer + format: unixtime + description: The unix timestamp when the operation was created. + example: "1676540381" + data: + type: array + description: The result data of the operation, if successful + items: + $ref: "#/components/schemas/imageResult" + required: + - created + - data + imageResult: + type: object + description: The image url or encoded image if successful, and an error otherwise. + properties: + url: + type: string + description: The image url. + example: https://www.contoso.com + b64_json: + type: string + description: The base64 encoded image + content_filter_results: + $ref: "#/components/schemas/dalleContentFilterResults" + revised_prompt: + type: string + description: The prompt that was used to generate the image, if there was any revision to the prompt. + prompt_filter_results: + $ref: "#/components/schemas/dalleFilterResults" + securitySchemes: + bearer: + type: oauth2 + flows: + implicit: + authorizationUrl: https://login.microsoftonline.com/common/oauth2/v2.0/authorize + scopes: {} + x-tokenInfoFunc: api.middleware.auth.bearer_auth + x-scopeValidateFunc: api.middleware.auth.validate_scopes + apiKey: + type: apiKey + name: api-key + in: header \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-realtime-api-ws.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-realtime-api-ws.json new file mode 100644 index 000000000..046a2ddfc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/openai-api/oai-realtime-api-ws.json @@ -0,0 +1,48 @@ +{ + "swagger": "2.0", + "info": { + "title": "OAI-Realtime-API", + "version": "1.0" + }, + "host": "apim-d6vjlv67krcvy.azure-api.net", + "basePath": "/openai/realtime", + "schemes": [ + "ws", + "wss" + ], + "securityDefinitions": { + "apiKeyHeader": { + "type": "apiKey", + "name": "api-key", + "in": "header" + }, + "apiKeyQuery": { + "type": "apiKey", + "name": "api-key", + "in": "query" + } + }, + "security": [ + { + "apiKeyHeader": [] + }, + { + "apiKeyQuery": [] + } + ], + "paths": { + "": { + "get": { + "description": "WebSocket opening handshake", + "operationId": "onHandshake", + "summary": "onHandshake", + "responses": { + "200": { + "description": null + } + } + } + } + }, + "tags": [] +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-model-inference-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-model-inference-api-policy.xml new file mode 100644 index 000000000..f826a3e13 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-model-inference-api-policy.xml @@ -0,0 +1,19 @@ + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-index-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-index-api-policy.xml new file mode 100644 index 000000000..ab61365d0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-index-api-policy.xml @@ -0,0 +1,19 @@ + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-service-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-service-api-policy.xml new file mode 100644 index 000000000..ab61365d0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/ai-search-service-api-policy.xml @@ -0,0 +1,19 @@ + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/azure-open-ai-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/azure-open-ai-api-policy.xml new file mode 100644 index 000000000..af9c08f04 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/azure-open-ai-api-policy.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + @{ + var body = context.Request.Body.As(preserveContent: true); + string requestedModel = (string)context.Variables["requestedModel"]; + + // Only inject model if it doesn't already exist in the body + if (body["model"] == null) + { + body["model"] = requestedModel; + } + + return body.ToString(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/doc-intelligence-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/doc-intelligence-api-policy.xml new file mode 100644 index 000000000..304c16d93 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/doc-intelligence-api-policy.xml @@ -0,0 +1,87 @@ + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @{ + var originalLocation = context.Response.Headers.GetValueOrDefault("operation-location",""); + if (!string.IsNullOrEmpty(originalLocation)) { + var uri = new Uri(originalLocation); + return originalLocation.Replace( + uri.Host, + context.Request.OriginalUrl.Host + ); + } + return originalLocation; + } + + + + @((string)context.Variables["responseBody"]) + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth-custom.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth-custom.xml new file mode 100644 index 000000000..f0eec92bf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth-custom.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + @((string)context.Variables["audience"]) + + + + @($"https://sts.windows.net/{context.Variables["tenant-id"]}/") + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth.xml new file mode 100644 index 000000000..c13e9badf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-aad-auth.xml @@ -0,0 +1,26 @@ + + + + + + + + + + {{audience}} + + + https://sts.windows.net/{{tenant-id}}/ + + + + {{client-id}} + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-compatibility.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-compatibility.xml new file mode 100644 index 000000000..886156ac6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-compatibility.xml @@ -0,0 +1,20 @@ + + + + https://ai.azure.com + + + GET + POST + PUT + DELETE + OPTIONS + + +
*
+
+ +
*
+
+
+
\ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-deployments.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-deployments.xml new file mode 100644 index 000000000..8acadde3b --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-foundry-deployments.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + application/json + + @{ + return JsonConvert.SerializeObject(new { + error = "Invalid or missing AOAIAccountName. No matching account found in the configuration.", + requestedAccount = (string)context.Variables.GetValueOrDefault("AOAIAccountName", "default") + }); + } + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-usage.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-usage.xml new file mode 100644 index 000000000..a21d3e85f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-ai-usage.xml @@ -0,0 +1,32 @@ + + + + + @{ + return new JObject( + new JProperty("id", (string)context.Variables.GetValueOrDefault("idPrefix", "ai") + "-" + Guid.NewGuid().ToString()), + new JProperty("timestamp", DateTime.UtcNow.ToString()), + new JProperty("appId", (string)context.Variables.GetValueOrDefault("appId", "NA")), + new JProperty("subscriptionId", context.Subscription?.Id?.ToString() ?? "Portal-Admin"), + new JProperty("productName", context.Product?.Name?.ToString() ?? "Portal-Admin"), + new JProperty("targetService", (string)context.Variables.GetValueOrDefault("targetService", "NA")), + new JProperty("model", (string)context.Variables.GetValueOrDefault("model", "NA")), + new JProperty("gatewayName", context.Deployment?.ServiceName ?? "NA"), + new JProperty("gatewayRegion", context.Deployment?.Region ?? "NA"), + new JProperty("aiGatewayId", context.Deployment?.Gateway?.Id ?? "NA"), + new JProperty("RequestIp", context.Request?.IpAddress ?? "NA"), + new JProperty("operationName", context.Operation?.Name ?? "NA"), + new JProperty("sessionId", (string)context.Variables.GetValueOrDefault("sessionId", "NA")), + new JProperty("endUserId", (string)context.Variables.GetValueOrDefault("endUserId", "NA")), + new JProperty("backendId", (string)context.Variables.GetValueOrDefault("backendId", "NA")), + new JProperty("routeLocation", (string)context.Variables.GetValueOrDefault("routeLocation", "NA")), + new JProperty("routeName", (string)context.Variables.GetValueOrDefault("routeName", "NA")), + new JProperty("deploymentName", (string)context.Variables.GetValueOrDefault("deploymentName", "NA")), + new JProperty("promptTokens", (int)context.Variables.GetValueOrDefault("promptTokens", 1)), + new JProperty("responseTokens", (int)context.Variables.GetValueOrDefault("responseTokens", 0)), + new JProperty("totalTokens", (int)context.Variables.GetValueOrDefault("totalTokens", 1)) + ).ToString(); + } + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-central-cache-manager.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-central-cache-manager.xml new file mode 100644 index 000000000..f410f6f15 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-central-cache-manager.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + () ?? 300; + }" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @($"Unified AI - Config loaded ({context.Variables["cache-operation"]}), models: {((JObject)context.Variables["config-models"]).Count}, api-types: {((JObject)context.Variables["config-api-types"]).Count}") + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-entra-auth.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-entra-auth.xml new file mode 100644 index 000000000..f0eec92bf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-entra-auth.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + @((string)context.Variables["audience"]) + + + + @($"https://sts.windows.net/{context.Variables["tenant-id"]}/") + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-get-available-models.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-get-available-models.xml new file mode 100644 index 000000000..382ba57fa --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-get-available-models.xml @@ -0,0 +1,72 @@ + + + + (StringComparer.OrdinalIgnoreCase); + JArray uniqueModels = new JArray(); + foreach (var model in allModels) + { + string modelName = model["name"]?.ToString() ?? ""; + if (!seenNames.Contains(modelName)) + { + seenNames.Add(modelName); + uniqueModels.Add(model); + } + } + + // Get allowedModels filter (comma-separated string) + string allowedModelsStr = context.Variables.ContainsKey("allowedModels") + ? (string)context.Variables["allowedModels"] + : ""; + + JArray filteredModels; + if (string.IsNullOrWhiteSpace(allowedModelsStr)) + { + // No filter - return all unique models + filteredModels = uniqueModels; + } + else + { + // Parse allowed models and filter (case-insensitive) + var allowedSet = new HashSet( + allowedModelsStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(m => m.Trim()), + StringComparer.OrdinalIgnoreCase + ); + + filteredModels = new JArray(); + foreach (var model in uniqueModels) + { + string modelName = model["name"]?.ToString() ?? ""; + if (allowedSet.Contains(modelName)) + { + filteredModels.Add(model); + } + } + } + + return filteredModels.ToString(); + }" /> + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-llm-usage.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-llm-usage.xml new file mode 100644 index 000000000..4f7ba0cf7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-llm-usage.xml @@ -0,0 +1,63 @@ + + + availableRoutesIndexes = new List(); + + for (int i = 0; i < routes.Count; i++) + { + JObject route = (JObject)routes[i]; + + if (!route.Value("isThrottling")) + { + int routePriority = route.Value("priority"); + + if (routePriority < selectedPriority) + { + selectedPriority = routePriority; + availableRoutesIndexes.Clear(); + availableRoutesIndexes.Add(i); + } + else if (routePriority == selectedPriority) + { + availableRoutesIndexes.Add(i); + } + } + } + + if (availableRoutesIndexes.Count == 1) + { + return availableRoutesIndexes[0]; + } + + if (availableRoutesIndexes.Count > 0) + { + //Returns a random route from the list if we have more than one available with the same priority + return availableRoutesIndexes[new Random().Next(0, availableRoutesIndexes.Count)]; + } + else + { + //If there are no available routes, the request will be sent to the first one + return 0; + } + }" /> + ("backend-id"))" /> + ("location"))" /> + ("name"))" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-metadata-config.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-metadata-config.xml new file mode 100644 index 000000000..ea9a01b6f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-metadata-config.xml @@ -0,0 +1,107 @@ + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage-streaming.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage-streaming.xml new file mode 100644 index 000000000..49a09c9cf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage-streaming.xml @@ -0,0 +1,68 @@ + + + + + + availableRoutesIndexes = new List(); + + for (int i = 0; i < routes.Count; i++) + { + JObject route = (JObject)routes[i]; + + if (!route.Value("isThrottling")) + { + int routePriority = route.Value("priority"); + + if (routePriority < selectedPriority) + { + selectedPriority = routePriority; + availableRoutesIndexes.Clear(); + availableRoutesIndexes.Add(i); + } + else if (routePriority == selectedPriority) + { + availableRoutesIndexes.Add(i); + } + } + } + + if (availableRoutesIndexes.Count == 1) + { + return availableRoutesIndexes[0]; + } + + if (availableRoutesIndexes.Count > 0) + { + //Returns a random route from the list if we have more than one available with the same priority + return availableRoutesIndexes[new Random().Next(0, availableRoutesIndexes.Count)]; + } + else + { + //If there are no available routes, the request will be sent to the first one + return 0; + } + }" /> + ("backend-id"))" /> + ("location"))" /> + ("name"))" /> + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage.xml new file mode 100644 index 000000000..e50e6d54f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-openai-usage.xml @@ -0,0 +1,47 @@ + + + + + + + + + + @{ + //Avoid reading response body as it can only be ready once, instead, before calling this fragement, a variable call responseBody will be set in the outbound policy + //var responseBody = context.Response.Body?.As(true); //Avoid this one + var responseBody = (JObject)context.Variables["responseBody"]; //It is set in the outbound policy before calling the fragment + return new JObject( + new JProperty("id", responseBody?["id"]?.ToString() ?? Guid.NewGuid().ToString()), + new JProperty("timestamp", DateTime.UtcNow.ToString()), + new JProperty("appId", (string)context.Variables.GetValueOrDefault("appId", "NA")), + new JProperty("subscriptionId", context.Subscription?.Id?.ToString() ?? "Portal-Admin"), + new JProperty("productName", context.Product?.Name?.ToString() ?? "Portal-Admin"), + new JProperty("targetService", responseBody?["object"]?.ToString() ?? "NA"), + new JProperty("model", responseBody?["model"]?.ToString() ?? "NA"), + new JProperty("gatewayName", context.Deployment?.ServiceName ?? "NA"), + new JProperty("gatewayRegion", context.Deployment?.Region ?? "NA"), + new JProperty("aiGatewayId", context.Deployment?.Gateway?.Id ?? "NA"), + new JProperty("RequestIp", context.Request?.IpAddress ?? "NA"), + new JProperty("operationName", context.Operation?.Name ?? "NA"), + new JProperty("sessionId", (string)context.Variables.GetValueOrDefault("sessionId", "NA")), + new JProperty("endUserId", (string)context.Variables.GetValueOrDefault("endUserId", "NA")), + new JProperty("backendId", (string)context.Variables.GetValueOrDefault("backendId", "NA")), + new JProperty("routeLocation", (string)context.Variables.GetValueOrDefault("routeLocation", "NA")), + new JProperty("routeName", (string)context.Variables.GetValueOrDefault("routeName", "NA")), + new JProperty("deploymentName", (string)context.Variables.GetValueOrDefault("deploymentName", "NA")), + new JProperty("promptTokens", responseBody?["usage"]?["prompt_tokens"]?.Value() ?? 1), + new JProperty("responseTokens", responseBody?["usage"]?["completion_tokens"]?.Value() ?? 0), + new JProperty("totalTokens", responseBody?["usage"]?["total_tokens"]?.Value() ?? 1), + new JProperty("audioTokens", responseBody?["usage"]?["prompt_tokens_details"]?["audio_tokens"]?.Value() ?? 0), + new JProperty("cachedTokens", responseBody?["usage"]?["prompt_tokens_details"]?["cached_tokens"]?.Value() ?? 0) + ).ToString(); + } + + + @{ + return ((JObject)context.Variables["responseBody"]).ToString(); + } + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-path-builder.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-path-builder.xml new file mode 100644 index 000000000..4fe276689 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-path-builder.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + @(context.Request.Body.As()) + + + + + + + + ("api-base-path", String.Empty); + var responseId = context.Variables.GetValueOrDefault("response-id", String.Empty); + + if (String.IsNullOrEmpty(basePath)) { + return String.Empty; + } + + // Responses API + if (apiType.StartsWith("responses", StringComparison.OrdinalIgnoreCase)) { + var includeResponseId = !String.IsNullOrEmpty(responseId) && (context.Request.Method == "GET" || context.Request.Method == "DELETE"); + return includeResponseId ? $"{basePath}/{responseId}" : basePath; + } + + // Inference API: {api-base-path}/chat/completions + return $"{basePath}/chat/completions"; + }" /> + + + + + @(context.Variables.GetValueOrDefault("selected-api-version")) + + + + + + + ("targetPoolType", String.Empty); + var modelId = context.Variables.GetValueOrDefault("requestedModel", String.Empty); + var templates = context.Variables.GetValueOrDefault("config-backend-path-templates"); + + // Look up path template: backend-path-templates[poolType][operation] + var backendTemplates = templates?[poolType] as JObject; + var template = backendTemplates?[operation]?.ToString(); + + if (String.IsNullOrEmpty(template)) { + return String.Empty; + } + + // Replace {model} placeholder with actual model (URL-encoded for special chars) + if (template.Contains("{model}")) { + template = template.Replace("{model}", System.Uri.EscapeDataString(modelId)); + } + + return template; + }" /> + + + + + + + application/json + + @{ + var operation = context.Variables.GetValueOrDefault("openai-compat-operation", "unknown"); + var poolType = context.Variables.GetValueOrDefault("targetPoolType", "unknown"); + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Operation '{operation}' is not supported for backend type '{poolType}'."), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "unsupported_operation") + )) + ).ToString(); + } + + + + + + @(context.Variables.GetValueOrDefault("selected-api-version")) + + + + + ("requestedModel", String.Empty); + + return !String.IsNullOrEmpty(basePath) && !String.IsNullOrEmpty(modelId) + ? $"{basePath}/deployments/{modelId}/chat/completions" + : String.Empty; + }" /> + + @{ + var body = context.Request.Body.As(); + if (!String.IsNullOrEmpty(body)) { + try { + var parsedBody = JObject.Parse(body); + var modelId = context.Variables.GetValueOrDefault("requestedModel", String.Empty); + if (parsedBody["model"] == null || String.IsNullOrEmpty(parsedBody["model"].ToString())) { + parsedBody["model"] = modelId; + } + return parsedBody.ToString(); + } catch { + return body; + } + } + return body; + } + + @(context.Variables.GetValueOrDefault("selected-api-version")) + + + + + + + + + @($"Unified AI - Path builder error for API type: {context.Variables.GetValueOrDefault("api-type")}") + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("code", "PathBuildError"), + new JProperty("message", $"No path determined for API type: {context.Variables.GetValueOrDefault("api-type")}") + )) + ).ToString(); + } + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-anonymization.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-anonymization.xml new file mode 100644 index 000000000..eba9546d1 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-anonymization.xml @@ -0,0 +1,275 @@ + + + + + + + + + + + ("piiRegexPatterns"); + var mappings = new JArray(); + + if (!string.IsNullOrEmpty(regexPatterns)) + { + var patterns = JArray.Parse(regexPatterns); + var categoryCounts = new Dictionary(); + var processedTexts = new HashSet(); // Track already processed texts + + foreach (var pattern in patterns) + { + var regex = new Regex(pattern["pattern"].ToString()); + var category = pattern["category"].ToString(); + + if (!categoryCounts.ContainsKey(category)) + { + categoryCounts[category] = 0; + } + + var matches = regex.Matches(content); + foreach (Match match in matches) + { + if (!string.IsNullOrEmpty(match.Value)) + { + // Skip if we already have a mapping for this text + if (processedTexts.Contains(match.Value)) + { + continue; + } + + var placeholder = $"<{category}_{categoryCounts[category]}>"; + mappings.Add(new JObject { + ["original"] = match.Value, + ["placeholder"] = placeholder, + ["source"] = "regex" + }); + categoryCounts[category]++; + processedTexts.Add(match.Value); // Mark this text as processed + } + } + } + } + return mappings.ToString(); + }" /> + + + ("piiRegexMappings")); + + foreach (var mapping in mappings) + { + content = content.Replace( + mapping["original"].ToString(), + mapping["placeholder"].ToString() + ); + } + return content; + }" /> + + + + + + + @{ + var baseUrl = "{{piiServiceUrl}}".TrimEnd('/'); + return baseUrl + "/language/:analyze-text?api-version=2026-05-01"; + } + POST + + application/json + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + @{ + var request = new JObject(); + request["kind"] = "PiiEntityRecognition"; + request["parameters"] = new JObject{ + {"modelVersion", "latest"}, + {"redactionPolicy", new JObject{ + {"policyKind", "CharacterMask"}, + {"redactionCharacter", "#"} + }} + }; + request["analysisInput"] = new JObject{ + {"documents", new JArray{ + new JObject{ + {"text", context.Variables.GetValueOrDefault("piiInputContent")}, + {"id", "1"}, + {"language", context.Variables.GetValueOrDefault("piiDetectionLanguage")} + } + }} + }; + return request.ToString(); + } + + + + = 300) + { + var body = string.Empty; + try { body = response.Body?.As(preserveContent: true) ?? string.Empty; } catch { } + return $"PII service returned non-success status {response.StatusCode} {response.StatusReason}. Body: {body}"; + } + try + { + var parsed = JObject.Parse(response.Body.As(preserveContent: true)); + var errors = parsed.SelectToken("$.results.errors") as JArray; + if (errors != null && errors.Count > 0) + { + return $"PII service reported document errors: {errors.ToString(Newtonsoft.Json.Formatting.None)}"; + } + var entitiesToken = parsed.SelectToken("$.results.documents[0].entities"); + if (entitiesToken == null) + { + return "PII service response did not contain expected entities payload."; + } + return string.Empty; + } + catch (Exception ex) + { + return $"Failed to parse PII service response: {ex.Message}"; + } + }" /> + + + + + + @($"PII anonymization degraded: {context.Variables.GetValueOrDefault("piiAnalysisFailureReason")}") + + + + + + + + + + + + + application/json + + @{ + var error = new JObject { + ["error"] = new JObject { + ["code"] = "PIIAnonymizationFailed", + ["message"] = "The PII anonymization service is currently unavailable and 'continueOnNLPError' is disabled. Request was blocked to prevent unmasked content from reaching the backend.", + ["details"] = context.Variables.GetValueOrDefault("piiAnalysisFailureReason"), + ["requestId"] = context.RequestId.ToString() + } + }; + return error.ToString(); + } + + + + + + + + + + + ("piiConfidenceThreshold"); + var exclusions = context.Variables.GetValueOrDefault("piiEntityCategoryExclusions") + .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(c => c.Trim()) + .ToList(); + + var entitiesToken = JObject.Parse(response.Body.As()) + .SelectToken("$.results.documents[0].entities"); + var entitiesArray = entitiesToken as JArray ?? new JArray(); + + var entities = entitiesArray + .Where(e => e["confidenceScore"] != null && e["confidenceScore"].Value() >= confidenceThreshold) + .Where(e => !exclusions.Contains(e["category"]?.ToString() ?? string.Empty)) + .OrderByDescending(e => (e["text"]?.ToString() ?? string.Empty).Length); + + var mappings = new JArray(); + var categoryCounts = new Dictionary(); + var processedTexts = new HashSet(); // Track already processed texts + + foreach (var entity in entities) { + var text = (entity["text"]?.ToString() ?? string.Empty).TrimEnd('.', ',', ' '); + if (string.IsNullOrEmpty(text)) { continue; } + + // Skip if this text has already been processed + if (processedTexts.Contains(text)) { + continue; + } + + var category = entity["category"].ToString(); + + if (!categoryCounts.ContainsKey(category)) { + categoryCounts[category] = 0; + } + + var placeholder = $"<{category}_{categoryCounts[category]}>"; + mappings.Add(new JObject { + ["original"] = text, + ["placeholder"] = placeholder + }); + + categoryCounts[category]++; + processedTexts.Add(text); // Mark this text as processed + } + + return mappings.ToString(); + }" /> + + + + + ("piiRegexMappings") ?? "[]"); + + foreach (var mapping in regexMappings) + { + apiMappings.Add(mapping); + } + + return apiMappings.ToString(); + }" /> + + + ("piiMappings")); + + foreach (var mapping in mappings) { + var original = mapping["original"].ToString(); + var placeholder = mapping["placeholder"].ToString(); + content = content.Replace(original, placeholder); + } + + return content; + }" /> + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-deanonymization.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-deanonymization.xml new file mode 100644 index 000000000..731f5072f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-deanonymization.xml @@ -0,0 +1,20 @@ + + + "); + + var mappings = JArray.Parse( + context.Variables.GetValueOrDefault("piiMappings")); + + foreach (var mapping in mappings) { + var original = mapping["original"].ToString(); + var placeholder = mapping["placeholder"].ToString(); + content = content.Replace(placeholder, original); + } + return content; + }" /> + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-state-saving.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-state-saving.xml new file mode 100644 index 000000000..c1b248522 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-pii-state-saving.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + @{ + var eventData = new JObject(); + + // Operation metadata + eventData["id"] = context.Variables.GetValueOrDefault("piiOperationId"); + eventData["timestamp"] = context.Variables.GetValueOrDefault("piiOperationTimestamp"); + eventData["type"] = "PII_Processing"; + eventData["targetDeployment"] = context.Variables.GetValueOrDefault("targetDeployment"); + // Enhanced API and subscription context with content + eventData["context"] = new JObject { + ["api"] = new JObject { + ["id"] = context.Variables.GetValueOrDefault("piiApiId"), + ["name"] = context.Variables.GetValueOrDefault("piiApiName"), + ["operation"] = new JObject { + ["id"] = context.Operation.Id ?? "unknown", + ["name"] = context.Variables.GetValueOrDefault("piiOperationName"), + ["method"] = context.Operation.Method ?? "unknown", + ["urlTemplate"] = context.Operation.UrlTemplate ?? "unknown" + }, + ["content"] = new JObject { + ["originalRequest"] = context.Variables.GetValueOrDefault("piiInputContent"), + ["anonymizedRequest"] = context.Variables.GetValueOrDefault("piiAnonymizedContent"), + ["originalResponse"] = context.Variables.GetValueOrDefault("originalResponse"), + ["deanonymizedResponse"] = context.Variables.GetValueOrDefault("piiDeanonymizedContentOutput") + } + }, + ["product"] = new JObject { + ["id"] = context.Variables.GetValueOrDefault("piiProductId"), + ["name"] = context.Variables.GetValueOrDefault("piiProductName") + }, + ["subscription"] = new JObject { + ["id"] = context.Variables.GetValueOrDefault("piiSubscriptionId"), + ["name"] = context.Variables.GetValueOrDefault("piiSubscriptionName") + } + }; + + // Process information + eventData["process"] = new JObject { + ["anonymizationEnabled"] = context.Variables.GetValueOrDefault("piiAnonymizationEnabled") == "true", + ["confidenceThreshold"] = context.Variables.GetValueOrDefault("piiConfidenceThreshold", 0.8), + ["entityCategoryExclusions"] = context.Variables.GetValueOrDefault("piiEntityCategoryExclusions", ""), + ["deanonymizationPerformed"] = context.Variables.ContainsKey("piiDeanonymizedContentOutput") + }; + + // Results summary + var mappings = new JArray(); + if (context.Variables.ContainsKey("piiMappings")) { + try { + mappings = JArray.Parse(context.Variables.GetValueOrDefault("piiMappings")); + eventData["entityCount"] = mappings.Count; + + // Group entities by category for reporting + var categories = new Dictionary(); + foreach (var mapping in mappings) { + var placeholder = mapping["placeholder"].ToString(); + var category = placeholder.TrimStart('<').Split('_')[0]; + + if (!categories.ContainsKey(category)) { + categories[category] = 0; + } + categories[category]++; + } + + var categoryDetails = new JObject(); + foreach (var category in categories) { + categoryDetails[category.Key] = category.Value; + } + eventData["entityCategories"] = categoryDetails; + + // Include mappings for detailed audit (can be removed if too verbose) + eventData["mappings"] = mappings; + } + catch (Exception ex) { + eventData["error"] = "Failed to process mappings: " + ex.Message; + } + } + + // Content length metrics (not the actual content) + if (context.Variables.ContainsKey("piiInputContent")) { + eventData["metrics"] = new JObject { + ["inputContentLength"] = context.Variables.GetValueOrDefault("piiInputContent", "").Length + }; + + if (context.Variables.ContainsKey("piiAnonymizedContent")) { + eventData["metrics"]["anonymizedContentLength"] = + context.Variables.GetValueOrDefault("piiAnonymizedContent", "").Length; + } + + // Add deanonymization metrics + if (context.Variables.ContainsKey("piiDeanonymizeContentInput")) { + eventData["metrics"]["deanonymizeInputLength"] = + context.Variables.GetValueOrDefault("piiDeanonymizeContentInput", "").Length; + } + + if (context.Variables.ContainsKey("piiDeanonymizedContentOutput")) { + eventData["metrics"]["deanonymizedOutputLength"] = + context.Variables.GetValueOrDefault("piiDeanonymizedContentOutput", "").Length; + } + } + + // Add deanonymization flow details + eventData["deanonymization"] = new JObject { + ["performed"] = context.Variables.ContainsKey("piiDeanonymizedContentOutput"), + ["inputSource"] = context.Variables.ContainsKey("piiDeanonymizeContentInput") ? "present" : "missing", + ["status"] = context.Variables.ContainsKey("piiDeanonymizedContentOutput") ? "completed" : "not_performed", + ["timestamp"] = DateTime.UtcNow.ToString("o") + }; + + // Track end-to-end flow + eventData["processingFlow"] = new JObject { + ["anonymization"] = new JObject { + ["performed"] = context.Variables.ContainsKey("piiAnonymizedContent"), + ["entityCount"] = context.Variables.ContainsKey("piiMappings") ? + JArray.Parse(context.Variables.GetValueOrDefault("piiMappings")).Count : 0 + }, + ["deanonymization"] = new JObject { + ["performed"] = context.Variables.ContainsKey("piiDeanonymizedContentOutput"), + ["success"] = context.Variables.ContainsKey("piiDeanonymizedContentOutput") + } + }; + + return eventData.ToString(); + } + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-raise-throttling-events.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-raise-throttling-events.xml new file mode 100644 index 000000000..163d5b768 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-raise-throttling-events.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-request-processor.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-request-processor.xml new file mode 100644 index 000000000..99bbb1b5e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-request-processor.xml @@ -0,0 +1,345 @@ + + + + + + + + ("config-api-types"); + if (apiTypes != null) { + foreach (var prop in apiTypes.Children()) { + var basePath = prop.Value["base-path"]?.ToString(); + if (String.IsNullOrEmpty(basePath)) { continue; } + var basePathLower = basePath.ToLower(); + if (path.StartsWith(basePathLower, StringComparison.OrdinalIgnoreCase) + && basePathLower.Length > bestLength) { + bestName = prop.Name; + bestLength = basePathLower.Length; + } + } + } + return bestName; + } catch { return String.Empty; } + }" /> + + + + + + @($"Unified AI - Blocked request to {context.Request.Method} {context.Variables.GetValueOrDefault("routing-processed-path", "unknown")} from {context.Request.IpAddress}") + + + + + application/json + + {"error": {"code": "PathNotAllowed", "message": "The requested path is not allowed."}} + + + + + + ("config-api-types")?[apiType] as JObject; + }" /> + + + + + + + + + + + + + + + + + + + (); + }" /> + + + + + ("parsed-request-body")?["model"]?.ToString(); + if (!String.IsNullOrEmpty(bodyModel)) + { + return bodyModel; + } + + // Try path extraction using api-path-segment (e.g., /openai/deployments/{model}/...) + var segment = context.Variables.GetValueOrDefault("api-path-segment", String.Empty); + if (!String.IsNullOrEmpty(segment)) + { + var path = context.Variables.GetValueOrDefault("routing-processed-path", String.Empty); + var idx = path.IndexOf(segment + "/", StringComparison.OrdinalIgnoreCase); + if (idx >= 0) + { + var afterSegment = path.Substring(idx + segment.Length + 1); + var modelName = afterSegment.Split('/')[0]; + if (!String.IsNullOrEmpty(modelName)) + { + return modelName; + } + } + } + } catch { } + return String.Empty; + }" /> + + + ("routing-processed-path", ""); + // Remove /v1/ prefix to get the operation + var idx = path.IndexOf("/v1/", StringComparison.OrdinalIgnoreCase); + if (idx >= 0) { + return path.Substring(idx + 4).TrimEnd('/'); + } + var trimmed = path.TrimStart('/'); + return trimmed.Substring(Math.Min(3, trimmed.Length)).TrimStart('/'); + }" /> + + + + + 1 ? parts[1] : String.Empty; + }" /> + + + + + + + + + + ("config-model-aliases"); + return aliases != null && aliases.Property(model) != null; + }"> + + + ("config-model-aliases"); + if (aliases == null) { return new JArray(); } + var prop = aliases.Property(alias); + if (prop == null) { return new JArray(); } + var aliasEntry = prop.Value as JObject; + if (aliasEntry == null) { return new JArray(); } + var modelsProp = aliasEntry.Property("models"); + if (modelsProp == null) { return new JArray(); } + var models = modelsProp.Value as JArray; + return models ?? new JArray(); + }" /> + ("config-model-aliases"); + if (aliases == null) { return alias; } + var prop = aliases.Property(alias); + if (prop == null) { return alias; } + var aliasConfig = prop.Value as JObject; + if (aliasConfig == null) { return alias; } + var modelsProp = aliasConfig.Property("models"); + if (modelsProp == null) { return alias; } + var models = modelsProp.Value as JArray; + if (models == null || models.Count == 0) { return alias; } + + var strategyProp = aliasConfig.Property("strategy"); + var strategy = strategyProp != null ? strategyProp.Value.ToString() : "priority"; + if (strategy.Equals("weighted", StringComparison.OrdinalIgnoreCase)) + { + // Weighted round-robin using cache counter + var weightsProp = aliasConfig.Property("weights"); + var weights = weightsProp != null ? weightsProp.Value as JArray : null; + if (weights != null && weights.Count == models.Count) + { + var productId = context.Product != null ? context.Product.Id : "default"; + var cacheKey = "alias-counter-" + alias + "-" + productId; + int counter = 0; + try { + if (context.Variables.ContainsKey(cacheKey)) { + var cached = context.Variables.GetValueOrDefault(cacheKey, ""); + if (!string.IsNullOrEmpty(cached)) { int.TryParse(cached, out counter); } + } + } catch { } + + int totalWeight = 0; + foreach (var w in weights) { totalWeight += w.Value(); } + int pick = counter % Math.Max(totalWeight, 1); + int cumulative = 0; + for (int i = 0; i < models.Count; i++) + { + cumulative += weights[i].Value(); + if (pick < cumulative) { return models[i].ToString(); } + } + } + } + // Default: priority — return first model + return models[0].ToString(); + }" /> + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Model could not be detected from request body or path"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "missing_model_parameter"), + new JProperty("param", "model") + )) + ).ToString(); + } + + + + + + ("routing-processed-path", String.Empty); + return path.Substring(path.LastIndexOf('/') + 1); + }" /> + + + ("requestedModel", "unknown"); + var models = context.Variables.GetValueOrDefault("config-models"); + var apiTypeConfig = context.Variables.GetValueOrDefault("api-type-config"); + + // Model-specific API versions + var modelConfig = models?[modelId]; + if (modelConfig != null) { + if (apiType.Equals("inference", StringComparison.OrdinalIgnoreCase) && modelConfig["inferenceApiVersion"] != null) { + return modelConfig["inferenceApiVersion"].ToString(); + } + if (apiType.Equals("openai", StringComparison.OrdinalIgnoreCase) && modelConfig["apiVersion"] != null) { + return modelConfig["apiVersion"].ToString(); + } + } + + // Fall back to api-type config version + var apiTypeVersion = apiTypeConfig?["api-version"]?.ToString(); + if (!String.IsNullOrEmpty(apiTypeVersion)) { + return apiTypeVersion; + } + + // Fallback defaults + return apiType.Equals("responses", StringComparison.OrdinalIgnoreCase) ? "2025-03-01-preview" : "2024-10-21"; + }" /> + + + @($"Unified AI - Request processed: {context.Variables.GetValueOrDefault("routing-processed-path", "n/a")} | API: {context.Variables.GetValueOrDefault("api-type", "n/a")} | Model: {context.Variables.GetValueOrDefault("requestedModel", "n/a")} | Alias: {context.Variables.GetValueOrDefault("original-model-alias", "")} | Op: {context.Variables.GetValueOrDefault("openai-compat-operation", "")} | Streaming: {context.Variables.GetValueOrDefault("is-streaming", false)}") + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-cache-store.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-cache-store.xml new file mode 100644 index 000000000..6fa62743f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-cache-store.xml @@ -0,0 +1,75 @@ + + + + + = 200 + && context.Response.StatusCode < 300 + )"> + + + + + ("user-id", subId); + return string.Concat(subId, "|", model, "|", userId); + }" + duration="86400" + caching-type="internal" /> + + + @((string)context.Variables["newResponseId"]) + + + + @($"Cached response-id ownership: id={context.Variables.GetValueOrDefault("newResponseId", string.Empty)} sub={context.Subscription?.Id ?? string.Empty} model={context.Variables.GetValueOrDefault("requestedModel", string.Empty)}") + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-security.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-security.xml new file mode 100644 index 000000000..285d3aa07 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-responses-id-security.xml @@ -0,0 +1,224 @@ + + + + + + + + + + + ("parsed-request-body"); + if (bodyObj == null) + { + var raw = context.Request.Body.As(preserveContent: true); + if (string.IsNullOrEmpty(raw)) { return string.Empty; } + bodyObj = JObject.Parse(raw); + } + var prev = bodyObj["previous_response_id"]?.ToString(); + return string.IsNullOrEmpty(prev) ? string.Empty : prev.Trim(); + } + catch { return string.Empty; } + }" /> + + + + + + + + + 0 ? parts[0] : string.Empty; + var currentSubId = context.Subscription?.Id ?? string.Empty; + return !string.IsNullOrEmpty(cachedSubId) + && !cachedSubId.Equals(currentSubId, StringComparison.Ordinal); + }"> + + @($"Response-id ownership mismatch. responseId={context.Variables.GetValueOrDefault("responseId", string.Empty)} requestSub={context.Subscription?.Id ?? string.Empty}") + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. The supplied response_id was created by a different subscription and cannot be accessed."), + new JProperty("type", "access_error"), + new JProperty("code", "response_id_forbidden") + )) + ).ToString(); + } + + + + + + + ("cachedResponseOwner", string.Empty); + if (string.IsNullOrEmpty(cached)) { return false; } + var parts = cached.Split('|'); + return parts.Length >= 2 && !string.IsNullOrEmpty(parts[1]); + }"> + + + @($"Hydrated requestedModel='{context.Variables.GetValueOrDefault("requestedModel", string.Empty)}' from response-id cache for {context.Request.Method} {context.Request.Url.Path}") + + + + + + + ("cachedResponseOwner", string.Empty); + return string.IsNullOrEmpty(cached); + }"> + + @($"Response-id cache miss on {context.Request.Method} {context.Request.Url.Path}. Returning 404.") + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"The supplied response_id '{context.Variables.GetValueOrDefault("responseId", string.Empty)}' is not known to the gateway. It was either never created here or the ownership cache entry has expired."), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "response_id_not_found") + )) + ).ToString(); + } + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-security-handler.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-security-handler.xml new file mode 100644 index 000000000..965835c0d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-security-handler.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. A valid API key is required via 'api-key' header."), + new JProperty("code", "unauthorized") + )) + ).ToString(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "JWT Bearer token is required for this product. Provide via 'Authorization: Bearer {token}' header."), + new JProperty("code", "jwt_required") + )) + ).ToString(); + } + + + + + + + + ("_nv-jwt-audience", ""); + }" /> + ("_nv-jwt-openid-config", ""); + }" /> + ("_nv-jwt-issuer", ""); + }" /> + + + + ("jwt-openid-config"); + var issuer = context.Variables.GetValueOrDefault("jwt-issuer"); + return string.IsNullOrEmpty(audience) || string.IsNullOrEmpty(openidConfig) || string.IsNullOrEmpty(issuer); + }"> + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "JWT authentication is not configured properly on the gateway."), + new JProperty("code", "jwt_not_configured") + )) + ).ToString(); + } + + + + + + + + + @(context.Variables.GetValueOrDefault("jwt-audience")) + + + @(context.Variables.GetValueOrDefault("jwt-issuer")) + + + + + + + + + + + @($"Unified AI - JWT authenticated: {context.Variables.GetValueOrDefault("user-id", "unknown")} | Roles: {context.Variables.GetValueOrDefault("jwt-roles", "none")} | Path: {context.Request.Url.Path}") + + + + + ("jwt-roles", ""); + var required = requiredRoles.Split(',').Select(r => r.Trim()).Where(r => !string.IsNullOrEmpty(r)); + var actual = tokenRoles.Split(',').Select(r => r.Trim()).Where(r => !string.IsNullOrEmpty(r)); + return !required.Any(r => actual.Any(a => a.Equals(r, StringComparison.OrdinalIgnoreCase))); + }"> + + + + application/json + + @{ + var requiredRoles = context.Variables.GetValueOrDefault("requiredRoles", ""); + var tokenRoles = context.Variables.GetValueOrDefault("jwt-roles", ""); + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. Required app role not found in token."), + new JProperty("code", "insufficient_role"), + new JProperty("required_roles", requiredRoles), + new JProperty("token_roles", string.IsNullOrEmpty(tokenRoles) ? "none" : tokenRoles) + )) + ).ToString(); + } + + + + + + + + + + + + + @($"Unified AI - Auth: {context.Variables.GetValueOrDefault("auth-type", "none")} | User: {context.Variables.GetValueOrDefault("user-id", "anonymous")} | Roles: {context.Variables.GetValueOrDefault("jwt-roles", "none")} | Sub: {context.Variables.GetValueOrDefault("subscription-name", "none")}") + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-authorization.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-authorization.xml new file mode 100644 index 000000000..eff3db26d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-authorization.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + application/json + + {"error": {"code": "AWSCredentialsNotConfigured", "message": "Amazon Bedrock credentials are not configured. Set the APIM named values: aws-access-key, aws-secret-key, and aws-region."}} + + + + + + @(((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ")) + + + @{ + var body = context.Request.Body.As(preserveContent: true); + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + + + @{ + var accessKey = "{{aws-access-key}}"; + var secretKey = "{{aws-secret-key}}"; + var region = "{{aws-region}}"; + var service = "bedrock"; + + var method = context.Request.Method; + var uri = context.Request.Url; + var host = uri.Host; + + // Create canonical path + var path = uri.Path; + var modelSplit = path.Split(new[] { "model/" }, 2, StringSplitOptions.None); + var afterModel = modelSplit.Length > 1 ? modelSplit[1] : ""; + var parts = afterModel.Split(new[] { '/' }, 2); + var model = System.Uri.EscapeDataString(parts[0]); + var remainder = parts.Length > 1 ? parts[1] : ""; + var canonicalPath = $"/model/{model}/{remainder}"; + + var amzDate = ((DateTime)context.Variables["now"]).ToString("yyyyMMddTHHmmssZ"); + var dateStamp = ((DateTime)context.Variables["now"]).ToString("yyyyMMdd"); + + // Hash the payload + var body = context.Request.Body.As(preserveContent: true); + string hashedPayload; + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body)); + hashedPayload = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + // Create canonical query string + var queryDict = context.Request.Url.Query; + var canonicalQueryString = ""; + if (queryDict != null && queryDict.Count > 0) + { + var encodedParams = new List(); + foreach (var kvp in queryDict) + { + var encodedKey = System.Uri.EscapeDataString(kvp.Key); + var encodedValue = System.Uri.EscapeDataString(kvp.Value.First() ?? ""); + encodedParams.Add($"{encodedKey}={encodedValue}"); + } + canonicalQueryString = string.Join("&", encodedParams.OrderBy(p => p)); + } + + // Create signed headers and canonical headers + var headers = context.Request.Headers; + var canonicalHeaderList = new List(); + + var contentType = headers.GetValueOrDefault("Content-Type", "").ToLowerInvariant(); + if (!string.IsNullOrEmpty(contentType)) + { + canonicalHeaderList.Add(new[] { "content-type", contentType }); + } + + canonicalHeaderList.Add(new[] { "host", host }); + + foreach (var header in headers) + { + var name = header.Key.ToLowerInvariant(); + if (string.Equals(name, "x-amz-content-sha256", StringComparison.OrdinalIgnoreCase) || + string.Equals(name, "x-amz-date", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + if (name.StartsWith("x-amz-")) + { + var value = header.Value.First()?.Trim(); + canonicalHeaderList.Add(new[] { name, value }); + } + } + canonicalHeaderList.Add(new[] { "x-amz-content-sha256", hashedPayload }); + canonicalHeaderList.Add(new[] { "x-amz-date", amzDate }); + var canonicalHeadersOrdered = canonicalHeaderList.OrderBy(h => h[0]); + var canonicalHeaders = string.Join("\n", canonicalHeadersOrdered.Select(h => $"{h[0]}:{h[1].Trim()}")) + "\n"; + var signedHeaders = string.Join(";", canonicalHeadersOrdered.Select(h => h[0])); + + // Create and hash the canonical request + var canonicalRequest = $"{method}\n{canonicalPath}\n{canonicalQueryString}\n{canonicalHeaders}\n{signedHeaders}\n{hashedPayload}"; + string hashedCanonicalRequest = ""; + using (var sha256 = System.Security.Cryptography.SHA256.Create()) + { + var hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(canonicalRequest)); + hashedCanonicalRequest = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + + // Build string to sign + var credentialScope = $"{dateStamp}/{region}/{service}/aws4_request"; + var stringToSign = $"AWS4-HMAC-SHA256\n{amzDate}\n{credentialScope}\n{hashedCanonicalRequest}"; + + // Derive signing key + byte[] kSecret = System.Text.Encoding.UTF8.GetBytes("AWS4" + secretKey); + byte[] kDate, kRegion, kService, kSigning; + using (var h1 = new System.Security.Cryptography.HMACSHA256(kSecret)) + { + kDate = h1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(dateStamp)); + } + using (var h2 = new System.Security.Cryptography.HMACSHA256(kDate)) + { + kRegion = h2.ComputeHash(System.Text.Encoding.UTF8.GetBytes(region)); + } + using (var h3 = new System.Security.Cryptography.HMACSHA256(kRegion)) + { + kService = h3.ComputeHash(System.Text.Encoding.UTF8.GetBytes(service)); + } + using (var h4 = new System.Security.Cryptography.HMACSHA256(kService)) + { + kSigning = h4.ComputeHash(System.Text.Encoding.UTF8.GetBytes("aws4_request")); + } + + // Create signature + string signature; + using (var hmac = new System.Security.Cryptography.HMACSHA256(kSigning)) + { + var sigBytes = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)); + signature = BitConverter.ToString(sigBytes).Replace("-", "").ToLowerInvariant(); + } + + return $"AWS4-HMAC-SHA256 Credential={accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}"; + } + + + @(context.Request.Url.Host) + + + + + + @{ + var namedValue = context.Variables.GetValueOrDefault("targetAuthConfigNamedValue", ""); + // Named value is resolved at deploy-time by APIM; at runtime we just read the variable + // The actual key lookup happens via the pool's authConfigNamedValue → APIM named value + return "Bearer " + namedValue; + } + + + + + + @(context.Variables.GetValueOrDefault("targetAuthConfigNamedValue", "")) + + + + + + + + + + + + + ("targetPoolType", "") == "non-llm-request")"> + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-pools.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-pools.xml new file mode 100644 index 000000000..1ccfbf817 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-backend-pools.xml @@ -0,0 +1,33 @@ + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-requested-model.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-requested-model.xml new file mode 100644 index 000000000..ca42100f6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-requested-model.xml @@ -0,0 +1,107 @@ + + + + = 0) + { + var afterSegment = path.Substring(idx + deploymentsSegment.Length); + var slashPos = afterSegment.IndexOf('/'); + var modelName = slashPos >= 0 ? afterSegment.Substring(0, slashPos) : afterSegment; + if (!string.IsNullOrEmpty(modelName)) + { + return modelName; + } + } + + // 3. Try request body model field (Inference endpoint pattern) + var bodyText = context.Request.Body.As(preserveContent: true); + if (string.IsNullOrEmpty(bodyText)) + { + return string.Empty; + } + + var body = JObject.Parse(bodyText); + return body["model"] != null ? body["model"].ToString() : string.Empty; + } + catch + { + return string.Empty; + } + }" /> + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Model could not be detected from request body or path"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "missing_model_parameter"), + new JProperty("param", "model") + )) + ).ToString(); + } + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-usage.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-usage.xml new file mode 100644 index 000000000..e96d22a01 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-llm-usage.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-response-headers.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-response-headers.xml new file mode 100644 index 000000000..36105602c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-response-headers.xml @@ -0,0 +1,89 @@ + + + + + + + + @(context.Variables.GetValueOrDefault("auth-type", "not-set")) + + + @(context.Variables.GetValueOrDefault("user-id", "not-set")) + + + @(context.Variables.GetValueOrDefault("subscription-name", "not-set")) + + + + + @(context.Variables.GetValueOrDefault("requestedModel", "not-set")) + + + @(context.Variables.GetValueOrDefault("api-type", "not-set")) + + + @(context.Variables.GetValueOrDefault("routing-processed-path", "not-set")) + + + @(context.Variables.GetValueOrDefault("selected-api-version", "not-set")) + + + @(context.Variables.GetValueOrDefault("is-streaming", false).ToString().ToLower()) + + + + + @(context.Variables.GetValueOrDefault("selected-backend", "not-set")) + + + @(context.Variables.GetValueOrDefault("finalPath", "not-set")) + + + @(context.Variables.GetValueOrDefault("targetAuthType", "not-set")) + + + + + + + @(context.Variables.GetValueOrDefault("original-model-alias", "")) + + + @(context.Variables.GetValueOrDefault("requestedModel", "")) + + + + + + + @(context.Variables.GetValueOrDefault("cache-operation", "not-set")) + + + + + @(context.RequestId.ToString()) + + + @(context.Deployment.Region) + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-target-backend-pool.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-target-backend-pool.xml new file mode 100644 index 000000000..f24e166d4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-set-target-backend-pool.xml @@ -0,0 +1,271 @@ + + + + + ("apiTypeOverrideBackend", ""); + if (!string.IsNullOrEmpty(apiTypeOverride)) + { + return apiTypeOverride; + } + + string defaultBackendPool = context.Variables.GetValueOrDefault("defaultBackendPool", ""); + string allowedBackendPools = context.Variables.GetValueOrDefault("allowedBackendPools", ""); + JArray backendPools = (JArray)context.Variables["backendPools"]; + + var allowedPools = string.IsNullOrWhiteSpace(allowedBackendPools) + ? null + : allowedBackendPools.Split(',').Select(s => s.Trim()).Where(s => s.Length > 0).ToArray(); + + // Find all backend pools that support the requested model + List matchingPools = new List(); + foreach (JObject pool in backendPools) + { + JArray supportedModels = (JArray)pool["supportedModels"]; + if (supportedModels.Any(model => model.ToString().Equals(requestedModel, StringComparison.OrdinalIgnoreCase))) + { + matchingPools.Add(pool); + } + } + + // If we have matching pools, filter by allowed pools if necessary + if (matchingPools.Count > 0) + { + // If there are no restrictions, use the first matching pool + if (allowedPools == null || allowedPools.Length == 0) + { + return matchingPools[0]["poolName"].ToString(); + } + + // Otherwise, find the first matching pool that is also in allowed pools + foreach (var pool in matchingPools) + { + string poolName = pool["poolName"].ToString(); + if (allowedPools.Contains(poolName)) + { + return poolName; + } + } + + // If no allowed pools match, check if default backend pool should be used + if (!string.IsNullOrWhiteSpace(defaultBackendPool)) + { + return defaultBackendPool; + } + + // If no default is specified, return error that no allowed pools match + return "ERROR_NO_ALLOWED_POOLS"; + } + + // If no matching pools at all, check for default backend pool + if (!string.IsNullOrWhiteSpace(defaultBackendPool)) + { + return defaultBackendPool; + } + + // No matching pools and no default - error + return "ERROR_NO_MODEL"; + }" /> + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Model parameter is required in the request payload"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "missing_model_parameter") + )) + ).ToString(); + } + + + + + + + application/json + + @{ + string requestedModel = (string)context.Variables["requestedModel"]; + JArray backendPools = (JArray)context.Variables["backendPools"]; + JArray supportedModels = new JArray(); + + foreach (JObject pool in backendPools) + { + foreach (var model in (JArray)pool["supportedModels"]) + { + if (!supportedModels.Contains(model)) + { + supportedModels.Add(model); + } + } + } + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Model '{requestedModel}' is not supported. Supported models: {string.Join(", ", supportedModels.Select(m => m.ToString()))}"), + new JProperty("type", "invalid_request_error"), + new JProperty("code", "unsupported_model"), + new JProperty("param", "model"), + new JProperty("supported_models", supportedModels) + )) + ).ToString(); + } + + + + + + + application/json + + @{ + string requestedModel = (string)context.Variables["requestedModel"]; + string allowedBackendPools = (string)context.Variables["allowedBackendPools"]; + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Access to model '{requestedModel}' is not allowed for this client. You do not have access to any of the backend pools that support this model."), + new JProperty("type", "access_error"), + new JProperty("code", "backend_pool_access_forbidden"), + new JProperty("allowed_backend_pools", allowedBackendPools) + )) + ).ToString(); + } + + + + + + + + ("apiTypeOverrideBackend", ""); + if (!string.IsNullOrEmpty(apiTypeOverride) && targetBackendPool == apiTypeOverride) + { + // Look up poolType from backendPools if possible, otherwise default to api-type-override + JArray backendPools = context.Variables.GetValueOrDefault("backendPools"); + if (backendPools != null) + { + foreach (JObject pool in backendPools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["poolType"].ToString(); + } + } + } + return "api-type-override"; + } + + JArray pools = context.Variables.GetValueOrDefault("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["poolType"].ToString(); + } + } + } + + return string.Empty; + }" /> + + + + ("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + // Use explicit authType if present, otherwise derive from poolType + var authType = pool["authType"]?.ToString(); + if (!string.IsNullOrEmpty(authType)) { return authType; } + + var poolType = pool["poolType"]?.ToString() ?? ""; + if (poolType == "ai-foundry" || poolType == "azure-openai") { return "native-backend-authorization"; } + if (poolType == "aws-bedrock") { return "aws-sigv4"; } + if (poolType == "aws-bedrock-mantle" || poolType == "gemini-openai") { return "api-key-bearer"; } + if (poolType == "external") { return "none"; } + return "native-backend-authorization"; + } + } + } + return "native-backend-authorization"; + }" /> + + + ("backendPools"); + if (pools != null) + { + foreach (JObject pool in pools) + { + if (pool["poolName"].ToString() == targetBackendPool) + { + return pool["authConfigNamedValue"]?.ToString() ?? ""; + } + } + } + return ""; + }" /> + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-strip-backend-headers.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-strip-backend-headers.xml new file mode 100644 index 000000000..256115c1c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-strip-backend-headers.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-throttling-events.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-throttling-events.xml new file mode 100644 index 000000000..90c070b7e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-throttling-events.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-validate-model-access.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-validate-model-access.xml new file mode 100644 index 000000000..ab995d15d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/frag-validate-model-access.xml @@ -0,0 +1,63 @@ + + + + + + + + + m.Trim().ToLowerInvariant()).ToArray(); + var requestedModel = (context.Variables.GetValueOrDefault("requestedModel") ?? string.Empty).ToLowerInvariant(); + return !allowedModels.Any(m => m == requestedModel); + }"> + + + + application/json + + @{ + string requestedModel = context.Variables.GetValueOrDefault("requestedModel") ?? string.Empty; + string allowedModels = context.Variables.GetValueOrDefault("allowedModels") ?? string.Empty; + + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", $"Access to model '{requestedModel}' is not allowed for this product."), + new JProperty("type", "access_error"), + new JProperty("code", "unauthorized_model_access"), + new JProperty("allowed_models", allowedModels) + )) + ).ToString(); + } + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/mcp-default-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/mcp-default-policy.xml new file mode 100644 index 000000000..6de5842c8 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/mcp-default-policy.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai-realtime-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai-realtime-policy.xml new file mode 100644 index 000000000..b01c22b3d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai-realtime-policy.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy.xml new file mode 100644 index 000000000..94003e53d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy_dynamic_throttling.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy_dynamic_throttling.xml new file mode 100644 index 000000000..c4a3503ea --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/openai_api_policy_dynamic_throttling.xml @@ -0,0 +1,165 @@ + + + + + + + + + + (true)["stream"].Type != JTokenType.Null)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/product-llm-oauth-access.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/product-llm-oauth-access.xml new file mode 100644 index 000000000..9394d4ccb --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/product-llm-oauth-access.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/translator-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/translator-api-policy.xml new file mode 100644 index 000000000..63d7f53f7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/translator-api-policy.xml @@ -0,0 +1,24 @@ + + + + + + + @("Bearer " + (string)context.Variables["msi-access-token"]) + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployment-by-name-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployment-by-name-policy.xml new file mode 100644 index 000000000..d6ccbefb2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployment-by-name-policy.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + application/json + + @{ + string deploymentId = context.Request.MatchedParameters["deployment-id"]; + JArray models = JArray.Parse((string)context.Variables["availableFilteredModels"]); + + foreach (var model in models) + { + if (string.Equals(model["name"]?.ToString(), deploymentId, StringComparison.OrdinalIgnoreCase)) + { + return model.ToString(); + } + } + + // Return error response if not found + JObject error = new JObject(); + error["error"] = new JObject() + { + { "code", "DeploymentNotFound" }, + { "message", $"The deployment '{deploymentId}' does not exist." } + }; + return error.ToString(); + } + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployments-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployments-policy.xml new file mode 100644 index 000000000..fe04f6467 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-deployments-policy.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + application/json + + @{ + JObject response = new JObject(); + response["value"] = JArray.Parse((string)context.Variables["availableFilteredModels"]); + return response.ToString(); + } + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-policy.xml new file mode 100644 index 000000000..815b28871 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-api-policy.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. API requests must be made through a product subscription."), + new JProperty("code", "product_required") + )) + ).ToString(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ("config-timeout-settings"); + var model = context.Variables.GetValueOrDefault("requestedModel", ""); + var streaming = context.Variables.GetValueOrDefault("is-streaming", false); + + // Get model-specific timeout (fallback to 120 if not configured) + var modelConfig = models?[model]; + var baseTimeout = modelConfig?["timeout"]?.Value() ?? 120; + + // Apply streaming multiplier if applicable + if (streaming) { + var multiplier = timeoutSettings?["streaming-multiplier"]?.Value() ?? 3; + return baseTimeout * multiplier; + } + return baseTimeout; + }" /> + + + + ("alias-models"); + // Standard retries + one retry per additional alias model + return isAlias && aliasModels != null ? 2 + Math.Max(aliasModels.Count - 1, 0) : 2; + }" interval="0" first-fast-retry="true" condition="@(context.Response.StatusCode == 429 || (context.Response.StatusCode >= 500 && !context.Response.StatusReason.Contains("Backend pool") && !context.Response.StatusReason.Contains("is temporarily unavailable")))"> + + + = 500; + }"> + + + ("alias-models"); + return aliasModels != null && idx < aliasModels.Count; + }"> + + ("alias-models"); + return aliasModels[idx].ToString(); + }" /> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-product-subscription.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-product-subscription.xml new file mode 100644 index 000000000..288761bbf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/unified-ai-product-subscription.xml @@ -0,0 +1,33 @@ + + + + + + + + + + application/json + + @{ + return new JObject( + new JProperty("error", new JObject( + new JProperty("message", "Access denied. API requests must be made through a product subscription."), + new JProperty("code", "product_required") + )) + ).ToString(); + } + + + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml new file mode 100644 index 000000000..fa7731f17 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployment-by-name-policy.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + application/json + + @{ + string deploymentId = context.Request.MatchedParameters["deployment-id"]; + JArray models = JArray.Parse((string)context.Variables["availableFilteredModels"]); + + foreach (var model in models) + { + if (string.Equals(model["name"]?.ToString(), deploymentId, StringComparison.OrdinalIgnoreCase)) + { + return model.ToString(); + } + } + + // Return error response if not found + JObject error = new JObject(); + error["error"] = new JObject() + { + { "code", "DeploymentNotFound" }, + { "message", $"The deployment '{deploymentId}' does not exist." } + }; + return error.ToString(); + } + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployments-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployments-policy.xml new file mode 100644 index 000000000..42511d200 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-deployments-policy.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + application/json + + @{ + JObject response = new JObject(); + response["value"] = JArray.Parse((string)context.Variables["availableFilteredModels"]); + return response.ToString(); + } + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-policy-v2.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-policy-v2.xml new file mode 100644 index 000000000..5f27b06a0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policies/universal-llm-api-policy-v2.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policy-fragments.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policy-fragments.bicep new file mode 100644 index 000000000..ab70b28b3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/policy-fragments.bicep @@ -0,0 +1,181 @@ +/** + * @module policy-fragments + * @description This module creates all policy fragments for the API Management service. + * It includes configurations for authentication, routing, usage tracking, and PII handling. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('The name of the API Management service') +param apimServiceName string + +@description('Enable PII Anonymization features') +param enablePIIAnonymization bool = true + +@description('Enable AI Model Inference features') +param enableAIModelInference bool = true + +@description('Enable Unified AI API features') +param enableUnifiedAiApi bool = true + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2022-08-01' existing = { + name: apimServiceName +} + +resource aiUsagePolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = { + parent: apimService + name: 'ai-usage' + properties: { + description: 'Tracks usage of all AI-related APIs with flexible dimensions for models, features, and more' + value: loadTextContent('./policies/frag-ai-usage.xml') + format: 'rawxml' + } +} + +resource raiseThrottlingEventsPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = { + parent: apimService + name: 'raise-throttling-events' + properties: { + description: 'Raises custom events when throttling limits are hit through App Insights metrics, for proactive monitoring and alerting' + value: loadTextContent('./policies/frag-raise-throttling-events.xml') + format: 'rawxml' + } +} + +resource piiAnonymizationPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = { + parent: apimService + name: 'pii-anonymization' + properties: { + description: 'Anonymizes personally identifiable information (PII) in API requests' + value: loadTextContent('./policies/frag-pii-anonymization.xml') + format: 'rawxml' + } +} + +resource piiDenonymizationPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = { + parent: apimService + name: 'pii-deanonymization' + properties: { + description: 'Deanonymizes personally identifiable information (PII) in API responses when needed for backend processing' + value: loadTextContent('./policies/frag-pii-deanonymization.xml') + format: 'rawxml' + } +} + +resource piiStateSavingPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = if (enablePIIAnonymization) { + parent: apimService + name: 'pii-state-saving' + properties: { + description: 'Saves the state of personally identifiable information (PII) for testing & validation purposes' + value: loadTextContent('./policies/frag-pii-state-saving.xml') + format: 'rawxml' + } +} + +resource aiFoundryCompatibilityPolicyFragment 'Microsoft.ApiManagement/service/policyFragments@2022-08-01' = if (enablePIIAnonymization) { + parent: apimService + name: 'ai-foundry-compatibility' + properties: { + description: 'Ensures compatibility with Microsoft Foundry CORS requirements' + value: loadTextContent('./policies/frag-ai-foundry-compatibility.xml') + format: 'rawxml' + } +} + +// ------------------ +// UNIFIED AI API FRAGMENTS +// ------------------ + +resource centralCacheManagerFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = if (enableUnifiedAiApi) { + parent: apimService + name: 'central-cache-manager' + properties: { + description: 'Caches metadata configuration for Unified AI API performance' + value: loadTextContent('./policies/frag-central-cache-manager.xml') + format: 'rawxml' + } +} + +resource requestProcessorFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = if (enableUnifiedAiApi) { + parent: apimService + name: 'request-processor' + properties: { + description: 'Analyzes incoming Unified AI requests to extract routing context' + value: loadTextContent('./policies/frag-request-processor.xml') + format: 'rawxml' + } +} + +resource pathBuilderFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = if (enableUnifiedAiApi) { + parent: apimService + name: 'path-builder' + properties: { + description: 'Reconstructs backend URI paths for Unified AI API routing' + value: loadTextContent('./policies/frag-path-builder.xml') + format: 'rawxml' + } +} + +resource securityHandlerFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'security-handler' + properties: { + description: 'Unified authentication handler for all AI Gateway APIs (API Key + optional JWT per-product)' + value: loadTextContent('./policies/frag-security-handler.xml') + format: 'rawxml' + } +} + +resource setResponseHeadersFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = if (enableUnifiedAiApi) { + parent: apimService + name: 'set-response-headers' + properties: { + description: 'Adds UAIG-* response headers when enableResponseHeaders is true' + value: loadTextContent('./policies/frag-set-response-headers.xml') + format: 'rawxml' + } +} + +resource stripBackendHeadersFragment 'Microsoft.ApiManagement/service/policyFragments@2024-06-01-preview' = { + parent: apimService + name: 'strip-backend-headers' + properties: { + description: 'Removes browser, App Service / ARR, and X-Forwarded-* headers from requests forwarded to AI backends' + value: loadTextContent('./policies/frag-strip-backend-headers.xml') + format: 'rawxml' + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('The name of the AI usage policy fragment') +output aiUsagePolicyFragmentName string = aiUsagePolicyFragment.name + +@description('The name of the PII anonymization policy fragment') +output piiAnonymizationPolicyFragmentName string = piiAnonymizationPolicyFragment.name + +@description('The name of the PII deanonymization policy fragment') +output piiDenonymizationPolicyFragmentName string = piiDenonymizationPolicyFragment.name +@description('The name of the PII state saving policy fragment') +output piiStateSavingPolicyFragmentName string = enablePIIAnonymization ? piiStateSavingPolicyFragment.name : '' +@description('The name of the AI Foundry compatibility policy fragment') +output aiFoundryCompatibilityPolicyFragmentName string = enablePIIAnonymization ? aiFoundryCompatibilityPolicyFragment.name : '' + +@description('The name of the central cache manager policy fragment') +output centralCacheManagerFragmentName string = enableUnifiedAiApi ? centralCacheManagerFragment.name : '' +@description('The name of the request processor policy fragment') +output requestProcessorFragmentName string = enableUnifiedAiApi ? requestProcessorFragment.name : '' +@description('The name of the path builder policy fragment') +output pathBuilderFragmentName string = enableUnifiedAiApi ? pathBuilderFragment.name : '' +@description('The name of the security handler policy fragment') +output securityHandlerFragmentName string = enableUnifiedAiApi ? securityHandlerFragment.name : '' +@description('The name of the set response headers policy fragment') +output setResponseHeadersFragmentName string = enableUnifiedAiApi ? setResponseHeadersFragment.name : '' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/openapi.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/openapi.json new file mode 100644 index 000000000..d7171b80d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/openapi.json @@ -0,0 +1,120 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "City Weather API", + "description": "API to retrieve weather information, including the current temperature, for a specified city. Generated with GitHub Copilot and the following prompt - create an openapi file that retrieves weather information with the city query parameter and reply with temperature. Add examples to the specification.", + "version": "1.0" + }, + "servers": [{ + "url": "https://replace-me.local/weatherservice" + }], + "paths": { + "/weather": { + "get": { + "summary": "Get current weather information for a specified city", + "description": "Retrieves weather information, including the current temperature, for a specified city.", + "operationId": "get-weather", + "parameters": [{ + "name": "city", + "in": "query", + "description": "Name of the city to retrieve weather information for", + "required": true, + "schema": { + "type": "string", + "example": "Tokyo" + } + }], + "responses": { + "200": { + "description": "Weather information for the specified city", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CityWeather" + }, + "examples": { + "Tokyo": { + "summary": "Example response for Tokyo", + "value": { + "city": "Tokyo", + "temperature": 15.5, + "temperature_format": "Celsius", + "description": "Clear sky", + "humidity": 60, + "wind_speed": 5.5 + } + }, + "default": { + "value": { + "city": "New York City", + "temperature": 59, + "temperature_format": "Fahrenheit", + "description": "Partly cloudy", + "humidity": 70, + "wind_speed": 3.2 + } + } + } + } + } + }, + "400": { + "description": "Bad request" + }, + "404": { + "description": "City not found" + }, + "500": { + "description": "Internal server error" + } + } + } + } + }, + "components": { + "schemas": { + "CityWeather": { + "type": "object", + "properties": { + "city": { + "type": "string", + "example": "Tokyo" + }, + "temperature": { + "type": "number", + "format": "float", + "example": 15.5 + }, + "temperature_format": { + "type": "string", + "example": "Celsius" + }, + "description": { + "type": "string", + "example": "Clear sky" + }, + "humidity": { + "type": "number", + "format": "int32", + "example": 60 + }, + "wind_speed": { + "type": "number", + "format": "float", + "example": 5.5 + } + } + } + }, + "securitySchemes": { + "apiKeyHeader": { + "type": "apiKey", + "name": "api-key", + "in": "header" + } + } + }, + "security": [{ + "apiKeyHeader": [] + }] +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/policy.xml new file mode 100644 index 000000000..b5fd1e7fb --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/sample/weather/policy.xml @@ -0,0 +1,44 @@ + + + + + + @{ + var random = new Random(); + double temperature; + var format = "Celsius"; + var descriptions = new[] { "Clear skies", "Partly cloudy", "Overcast", "Rainy" }; + var city = context.Request.MatchedParameters["city"]; + switch (city.ToLower()) + { + case "seattle": + case "new york city": + case "los angeles": + format = "Fahrenheit"; + temperature = random.Next(14, 95) + random.NextDouble(); + break; + default: + temperature = random.Next(-5, 35) + random.NextDouble(); + break; + } + return new JObject( + new JProperty("city", city), + new JProperty("temperature", Math.Round(temperature, 1)), + new JProperty("temperature_format", format), + new JProperty("description", descriptions[random.Next(descriptions.Length)]), + new JProperty("humidity", random.Next(20, 100)), + new JProperty("wind_speed", Math.Round(random.NextDouble() * 10, 1)) + ).ToString(); + } + + + + + + + + + + + + \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/speech-api/speech-api-3-1.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/speech-api/speech-api-3-1.json new file mode 100644 index 000000000..dc3714c3a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/speech-api/speech-api-3-1.json @@ -0,0 +1,6473 @@ +{ + "swagger": "2.0", + "info": { + "title": "Speech Services API v3.1", + "description": "Speech Services API v3.1.", + "contact": { + "name": "Cognitive Services - Speech Services", + "url": "https://docs.microsoft.com/azure/cognitive-services/speech-service/support" + }, + "version": "v3.1" + }, + "paths": { + "/datasets/locales": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets a list of supported locales for datasets.", + "operationId": "Datasets_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/DatasetLocales" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_dataset_locales.json" + } + } + } + }, + "/datasets": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets a list of datasets for the authenticated subscription.", + "operationId": "Datasets_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available datasets.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n -Example:\r\n filter=createdDateTime gt 2022-02-01T11:00:00Z and displayName eq 'My dataset'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedDatasets" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all datasets": { + "$ref": "./examples/get_datasets.json" + }, + "Get all acoustic datasets": { + "$ref": "./examples/get_acoustic_datasets.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Uploads and creates a new dataset by getting the data from a specified URL or starts waiting for data blocks to be uploaded.", + "operationId": "Datasets_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "dataset", + "description": "Definition for the new dataset.", + "required": true, + "schema": { + "$ref": "#/definitions/Dataset" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Dataset" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a dataset with content url": { + "$ref": "./examples/create_dataset_with_content_url.json" + }, + "Create dataset from data blocks": { + "$ref": "./examples/create_dataset_with_blocks.json" + } + } + } + }, + "/datasets/{id}": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets the dataset identified by the given ID.", + "operationId": "Datasets_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Dataset" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a dataset": { + "$ref": "./examples/get_dataset.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Updates the mutable details of the dataset identified by its ID.", + "operationId": "Datasets_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "datasetUpdate", + "description": "The updated values for the dataset.", + "required": true, + "schema": { + "$ref": "#/definitions/DatasetUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Dataset" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a dataset": { + "$ref": "./examples/update_dataset.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Deletes the specified dataset.", + "operationId": "Datasets_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The dataset was successfully deleted or did not exist." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a dataset": { + "$ref": "./examples/delete_dataset.json" + } + } + } + }, + "/datasets/{id}/blocks": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets the list of uploaded blocks for this dataset.", + "operationId": "Datasets_GetBlocks", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/UploadedBlocks" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get uploaded blocks for dataset": { + "$ref": "./examples/get_dataset_blocks.json" + } + } + }, + "put": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Upload a block of data for the dataset. The maximum size of the block is 8MiB.", + "operationId": "Datasets_UploadBlock", + "consumes": [ + "application/octet-stream" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "query", + "name": "blockid", + "description": "A valid Base64 string value that identifies the block. Prior to encoding, the string must be less than or equal to 64 bytes in size. For a given blob, the length of the value specified for the blockid parameter must be the same size for each block. Note that the Base64 string must be URL-encoded.", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "format": "binary", + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "The data block was uploaded successfully." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Upload a block for a dataset": { + "$ref": "./examples/upload_block.json" + } + } + } + }, + "/datasets/upload": { + "post": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Uploads data and creates a new dataset.", + "operationId": "Datasets_Upload", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "formData", + "name": "project", + "description": "The optional string representation of the url of a project. If set, the dataset will be associated with that project.", + "type": "string" + }, + { + "in": "formData", + "name": "displayName", + "description": "The name of this dataset (required).", + "required": true, + "type": "string" + }, + { + "in": "formData", + "name": "description", + "description": "Optional description of this dataset.", + "type": "string" + }, + { + "in": "formData", + "name": "locale", + "description": "The locale of this dataset (required).", + "required": true, + "type": "string" + }, + { + "in": "formData", + "name": "kind", + "description": "The kind of the dataset (required). Possible values are \"Language\", \"Acoustic\", \"Pronunciation\", \"AudioFiles\", \"LanguageMarkdown\".", + "required": true, + "type": "string" + }, + { + "in": "formData", + "name": "customProperties", + "description": "The optional custom properties of this entity. The maximum allowed key length is 64 characters, the maximum allowed value length is 256 characters and the count of allowed entries is 10.", + "type": "string" + }, + { + "in": "formData", + "name": "data", + "description": "For acoustic datasets, a zip file containing the audio data and a text file containing the transcriptions for the audio data. For language datasets, a text file containing the language or pronunciation data. Required in both cases.", + "type": "file" + }, + { + "in": "formData", + "name": "email", + "description": "An optional string containing the email address to send email notifications to in case the operation completes. The value will be removed after successfully sending the email.", + "type": "string" + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Dataset" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "deprecated": true, + "x-ms-examples": { + "Upload a dataset": { + "$ref": "./examples/upload_dataset.json" + } + } + } + }, + "/datasets/{id}/blocks:commit": { + "post": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Commit block list to complete the upload of the dataset.", + "operationId": "Datasets_CommitBlocks", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "blockList", + "description": "The list of blocks that compile the dataset.", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/CommitBlocksEntry" + } + } + } + ], + "responses": { + "200": { + "description": "The block list is accepted and the data import process can continue." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Commit uploaded blocks to dataset": { + "$ref": "./examples/commit_block_list.json" + } + } + } + }, + "/datasets/{id}/files": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets the files of the dataset identified by the given ID.", + "operationId": "Datasets_ListFiles", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available files.\r\n - Supported properties: name, createdDateTime, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=name eq 'myaudio.wav' and kind eq 'Audio'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all files": { + "$ref": "./examples/get_dataset_files.json" + }, + "Get all dataset report files": { + "$ref": "./examples/get_dataset_report_files.json" + } + } + } + }, + "/datasets/{id}/files/{fileId}": { + "get": { + "tags": [ + "Custom Speech datasets for model adaptation:" + ], + "summary": "Gets one specific file (identified with fileId) from a dataset (identified with id).", + "operationId": "Datasets_GetFile", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the dataset.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "fileId", + "description": "The identifier of the file.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a file": { + "$ref": "./examples/get_dataset_file.json" + } + } + } + }, + "/endpoints/locales": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets a list of supported locales for endpoint creations.", + "operationId": "Endpoints_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_endpoint_locales.json" + } + } + } + }, + "/endpoints": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets the list of endpoints for the authenticated subscription.", + "operationId": "Endpoints_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available endpoints.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=locale eq 'en-US'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedEndpoints" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all endpoints": { + "$ref": "./examples/get_endpoints.json" + }, + "Get all queued endpoints.": { + "$ref": "./examples/get_not_started_endpoints.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Creates a new endpoint.", + "operationId": "Endpoints_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "endpoint", + "description": "The details of the endpoint.", + "required": true, + "schema": { + "$ref": "#/definitions/Endpoint" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Endpoint" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create an endpoint": { + "$ref": "./examples/create_endpoint.json" + } + } + } + }, + "/endpoints/{id}": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets the endpoint identified by the given ID.", + "operationId": "Endpoints_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Endpoint" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get an endpoint": { + "$ref": "./examples/get_endpoint.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Deletes the endpoint identified by the given ID.", + "operationId": "Endpoints_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The model endpoint was successfully deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete an endpoint": { + "$ref": "./examples/delete_endpoint.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Updates the metadata of the endpoint identified by the given ID.", + "operationId": "Endpoints_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "endpointUpdate", + "description": "The updated values for the endpoint.", + "required": true, + "schema": { + "$ref": "#/definitions/EndpointUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Endpoint" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update an endpoint": { + "$ref": "./examples/update_endpoint.json" + } + } + } + }, + "/endpoints/{id}/files/logs": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets the list of audio and transcription logs that have been stored for a given endpoint.", + "operationId": "Endpoints_ListLogs", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "in": "query", + "name": "skipToken", + "description": "Token to skip logs that were already retrieved in previous requests. Pagination starts from beginning when not defined.", + "type": "string" + }, + { + "$ref": "#/parameters/topQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all endpoint logs": { + "$ref": "./examples/get_endpoint_logs.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Deletes the specified audio and transcription logs that have been stored for a given endpoint. It deletes all logs before (and including) a specific day.", + "description": "The deletion process is done asynchronously and can take up to one day depending on the amount of log files.", + "operationId": "Endpoints_DeleteLogs", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "query", + "name": "endDate", + "description": "The end date of the audio logs deletion (specific day, UTC).\r\n Expected format: \"yyyy-mm-dd\". For instance, \"2019-09-20\" results in deleting all logs on September 20h, 2019 and before.\r\n Deletes all existing logs when date is not specified.", + "type": "string" + } + ], + "responses": { + "202": { + "description": "The logs will be deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete all endpoint logs": { + "$ref": "./examples/delete_endpoint_logs.json" + } + } + } + }, + "/endpoints/{id}/files/logs/{logId}": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets a specific audio or transcription log for a given endpoint.", + "operationId": "Endpoints_GetLog", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "logId", + "description": "The identifier of the log.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get an endpoint log": { + "$ref": "./examples/get_endpoint_log.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Deletes one audio or transcription log that have been stored for a given endpoint.", + "operationId": "Endpoints_DeleteLog", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the endpoint.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "logId", + "description": "The identifier of the log.", + "required": true, + "type": "string" + } + ], + "responses": { + "204": { + "description": "The log was successfully deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete an endpoint log": { + "$ref": "./examples/delete_endpoint_log.json" + } + } + } + }, + "/endpoints/base/{locale}/files/logs": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets the list of audio and transcription logs that have been stored when using the default base model of a given language.", + "operationId": "Endpoints_ListBaseModelLogs", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "locale", + "description": "The language used to select the default base model.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "in": "query", + "name": "skipToken", + "description": "Token to skip logs that were already retrieved in previous requests. Pagination starts from beginning when not defined.", + "type": "string" + }, + { + "$ref": "#/parameters/topQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all base model logs": { + "$ref": "./examples/get_base_model_logs.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Deletes the specified audio and transcription logs that have been stored when using the default base model of a given language. It deletes all logs before (and including) a specific day.", + "description": "Deletion process is done asynchronously and can take up to one day depending on the amount of log files.", + "operationId": "Endpoints_DeleteBaseModelLogs", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "locale", + "description": "The language used to select the default base model.", + "required": true, + "type": "string" + }, + { + "in": "query", + "name": "endDate", + "description": "The end date of the audio logs deletion (specific day, UTC).\r\n Expected format: \"yyyy-mm-dd\". For instance, \"2019-09-20\" results in deleting all logs on September 20h, 2019 and before.\r\n Deletes all existing logs when date is not specified.", + "type": "string" + } + ], + "responses": { + "202": { + "description": "The logs will be deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete all base model logs": { + "$ref": "./examples/delete_base_model_logs.json" + } + } + } + }, + "/endpoints/base/{locale}/files/logs/{logId}": { + "get": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Gets a specific audio or transcription log for the default base model in a given language.", + "operationId": "Endpoints_GetBaseModelLog", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "locale", + "description": "The language used to select the default base model.", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "logId", + "description": "The identifier of the log.", + "required": true, + "type": "string" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a base model log": { + "$ref": "./examples/get_base_model_log.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech endpoints:" + ], + "summary": "Deletes one audio or transcription log that have been stored when using the default base model of a given language.", + "operationId": "Endpoints_DeleteBaseModelLog", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "locale", + "description": "The language used to select the default base model.", + "required": true, + "type": "string" + }, + { + "in": "path", + "name": "logId", + "description": "The identifier of the log.", + "required": true, + "type": "string" + } + ], + "responses": { + "204": { + "description": "The log was successfully deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a base model log": { + "$ref": "./examples/delete_base_model_log.json" + } + } + } + }, + "/evaluations/locales": { + "get": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Gets a list of supported locales for evaluations.", + "operationId": "Evaluations_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_evaluations_locales.json" + } + } + } + }, + "/evaluations": { + "get": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Gets the list of evaluations for the authenticated subscription.", + "operationId": "Evaluations_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available evaluations.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status and locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=displayName eq 'My evaluation'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedEvaluations" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all evaluations": { + "$ref": "./examples/get_evaluations.json" + }, + "Get all evaluations with where status is equal to 'Running'": { + "$ref": "./examples/get_evaluations_with_status_running.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Creates a new evaluation.", + "operationId": "Evaluations_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "evaluation", + "description": "The details of the new evaluation.", + "required": true, + "schema": { + "$ref": "#/definitions/Evaluation" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Evaluation" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a evaluation": { + "$ref": "./examples/create_evaluation.json" + } + } + } + }, + "/evaluations/{id}/files": { + "get": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Gets the files of the evaluation identified by the given ID.", + "operationId": "Evaluations_ListFiles", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the evaluation.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available files.\r\n - Supported properties: name, createdDateTime, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=name eq 'myaudio.wav' and kind eq 'Audio'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all files": { + "$ref": "./examples/get_evaluation_files.json" + } + } + } + }, + "/evaluations/{id}/files/{fileId}": { + "get": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Gets one specific file (identified with fileId) from an evaluation (identified with id).", + "operationId": "Evaluations_GetFile", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the evaluation.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "fileId", + "description": "The identifier of the file.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a file": { + "$ref": "./examples/get_evaluation_file.json" + } + } + } + }, + "/evaluations/{id}": { + "get": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Gets the evaluation identified by the given ID.", + "operationId": "Evaluations_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the evaluation.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Evaluation" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a evaluation": { + "$ref": "./examples/get_evaluation.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Updates the mutable details of the evaluation identified by its id.", + "operationId": "Evaluations_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the evaluation.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "evaluationUpdate", + "description": "The object containing the updated fields of the evaluation.", + "required": true, + "schema": { + "$ref": "#/definitions/EvaluationUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Evaluation" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a evaluation": { + "$ref": "./examples/update_evaluation.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech model evaluations:" + ], + "summary": "Deletes the evaluation identified by the given ID.", + "operationId": "Evaluations_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the evaluation.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The evaluation was successfully deleted or did not exist." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a evaluation": { + "$ref": "./examples/delete_evaluation.json" + } + } + } + }, + "/models/locales": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets a list of supported locales for model adaptation.", + "operationId": "Models_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_model_locales.json" + } + } + } + }, + "/models": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets the list of custom models for the authenticated subscription.", + "operationId": "Models_ListCustomModels", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available models.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=status eq 'NotStarted' or status eq 'Running'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedCustomModels" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all models in state 'Running'": { + "$ref": "./examples/get_running_models.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Creates a new model.", + "operationId": "Models_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "model", + "description": "The details of the new model.", + "required": true, + "schema": { + "$ref": "#/definitions/CustomModel" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/CustomModel" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a model": { + "$ref": "./examples/create_model.json" + }, + "Create a model with custom model weight": { + "$ref": "./examples/create_model_with_weight.json" + } + } + } + }, + "/models/base": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets the list of base models for the authenticated subscription.", + "operationId": "Models_ListBaseModels", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available base models.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=status eq 'NotStarted' or status eq 'Running'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedBaseModels" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all english base models": { + "$ref": "./examples/get_english_base_models.json" + } + } + } + }, + "/models/{id}": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets the model identified by the given ID.", + "operationId": "Models_GetCustomModel", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/CustomModel" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a model": { + "$ref": "./examples/get_model.json" + }, + "Get a model with custom model weight": { + "$ref": "./examples/get_model_with_weight.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Updates the metadata of the model identified by the given ID.", + "operationId": "Models_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "modelUpdate", + "description": "The updated values for the model.", + "required": true, + "schema": { + "$ref": "#/definitions/ModelUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/CustomModel" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a model": { + "$ref": "./examples/update_model.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Deletes the model identified by the given ID.", + "operationId": "Models_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The model was successfully deleted or did not exist." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a model": { + "$ref": "./examples/delete_model.json" + } + } + } + }, + "/models/base/{id}": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets the base model identified by the given ID.", + "operationId": "Models_GetBaseModel", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the base model.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/BaseModel" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a base model": { + "$ref": "./examples/get_base_model.json" + } + } + } + }, + "/models/{id}:copyto": { + "post": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Copies a model from one subscription to another.", + "description": "This method can be used to copy a model from one location to another. If the target subscription\r\nkey belongs to a subscription created for another location, the model will be copied to that location.\r\nOnly adapted models are allowed to copy to another subscription.", + "operationId": "Models_CopyTo", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model that will be copied.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "modelCopy", + "description": "The body contains the subscription key of the target subscription.", + "required": true, + "schema": { + "$ref": "#/definitions/ModelCopy" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/CustomModel" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Copy a model": { + "$ref": "./examples/copy_model.json" + } + } + } + }, + "/models/{id}/manifest": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Returns an manifest for this model which can be used in an on-premise container.", + "operationId": "Models_GetCustomModelManifest", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The ID of the model to generate a manifest for.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "Successfully generated a model manifest.", + "schema": { + "$ref": "#/definitions/ModelManifest" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a model manifest": { + "$ref": "./examples/get_model_manifest.json" + } + } + } + }, + "/models/base/{id}/manifest": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Returns an manifest for this base model which can be used in an on-premise container.", + "operationId": "Models_GetBaseModelManifest", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The ID of the model to generate a manifest for.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "Successfully generated a model manifest.", + "schema": { + "$ref": "#/definitions/ModelManifest" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a base model manifest": { + "$ref": "./examples/get_base_model_manifest.json" + } + } + } + }, + "/models/{id}/files": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets the files of the model identified by the given ID.", + "operationId": "Models_ListFiles", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available files.\r\n - Supported properties: name, createdDateTime, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=name eq 'myaudio.wav' and kind eq 'Audio'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all files": { + "$ref": "./examples/get_model_files.json" + } + } + } + }, + "/models/{id}/files/{fileId}": { + "get": { + "tags": [ + "Custom Speech models:" + ], + "summary": "Gets one specific file (identified with fileId) from a model (identified with id).", + "operationId": "Models_GetFile", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the model.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "fileId", + "description": "The identifier of the file.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a file": { + "$ref": "./examples/get_model_file.json" + } + } + } + }, + "/projects/locales": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of supported locales.", + "operationId": "Projects_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_project_locales.json" + } + } + } + }, + "/projects": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of projects for the authenticated subscription.", + "operationId": "Projects_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available projects.\r\n - Supported properties: displayName, description, createdDateTime, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=displayName eq 'My test'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedProjects" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all projects": { + "$ref": "./examples/get_projects.json" + }, + "Get all projects created in 2018 or 2019": { + "$ref": "./examples/get_projects_from_2018_and_2019.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Creates a new project.", + "operationId": "Projects_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "project", + "description": "The details of the project.", + "required": true, + "schema": { + "$ref": "#/definitions/Project" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Project" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a project": { + "$ref": "./examples/create_project.json" + } + } + } + }, + "/projects/{id}": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the project identified by the given ID.", + "operationId": "Projects_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Project" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a project": { + "$ref": "./examples/get_project.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Updates the project identified by the given ID.", + "operationId": "Projects_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "projectUpdate", + "description": "The updated values for the project.", + "required": true, + "schema": { + "$ref": "#/definitions/ProjectUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Project" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a project": { + "$ref": "./examples/update_project.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Deletes the project identified by the given ID.", + "operationId": "Projects_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The project was successfully deleted or did not exist." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a project": { + "$ref": "./examples/delete_project.json" + } + } + } + }, + "/projects/{id}/evaluations": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of evaluations for specified project.", + "operationId": "Projects_ListEvaluations", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available evaluations.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status and locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example\r\n filter=displayName eq 'My evaluation'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedEvaluations" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all evaluations": { + "$ref": "./examples/get_project_evaluations.json" + } + } + } + }, + "/projects/{id}/datasets": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of datasets for specified project.", + "operationId": "Projects_ListDatasets", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available datasets.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=createdDateTime gt 2022-02-01T11:00:00Z", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedDatasets" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all datasets": { + "$ref": "./examples/get_project_datasets.json" + } + } + } + }, + "/projects/{id}/endpoints": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of endpoints for specified project.", + "operationId": "Projects_ListEndpoints", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available endpoints.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=locale eq 'en-US'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedEndpoints" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all endpoints": { + "$ref": "./examples/get_project_endpoints.json" + } + } + } + }, + "/projects/{id}/models": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of models for specified project.", + "operationId": "Projects_ListModels", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available models.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=status eq 'NotStarted' or status eq 'Running'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedCustomModels" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all models": { + "$ref": "./examples/get_project_models.json" + } + } + } + }, + "/projects/{id}/transcriptions": { + "get": { + "tags": [ + "Custom Speech projects:" + ], + "summary": "Gets the list of transcriptions for specified project.", + "operationId": "Projects_ListTranscriptions", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the project.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available transcriptions.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=createdDateTime gt 2022-02-01T11:00:00Z", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedTranscriptions" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all transcriptions": { + "$ref": "./examples/get_project_transcriptions.json" + } + } + } + }, + "/transcriptions/locales": { + "get": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Gets a list of supported locales for offline transcriptions.", + "operationId": "Transcriptions_ListSupportedLocales", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get the supported locales": { + "$ref": "./examples/get_supported_transcription_locales.json" + } + } + } + }, + "/transcriptions": { + "get": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Gets a list of transcriptions for the authenticated subscription.", + "operationId": "Transcriptions_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available transcriptions.\r\n - Supported properties: displayName, description, createdDateTime, lastActionDateTime, status, locale.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=createdDateTime gt 2022-02-01T11:00:00Z", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedTranscriptions" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all transcriptions": { + "$ref": "./examples/get_transcriptions.json" + }, + "Get all failed transcriptions": { + "$ref": "./examples/get_failed_transcriptions.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Creates a new transcription.", + "operationId": "Transcriptions_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "transcription", + "description": "The details of the new transcription.", + "required": true, + "schema": { + "$ref": "#/definitions/Transcription" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/Transcription" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a transcription for URIs": { + "$ref": "./examples/create_uri_transcription.json" + }, + "Create a transcription with basic two-speaker diarization": { + "$ref": "./examples/create_simple_diarization_transcription.json" + }, + "Create a transcription with multispeaker diarization": { + "$ref": "./examples/create_multispeaker_diarization_transcription.json" + }, + "Create a transcription with language identification": { + "$ref": "./examples/create_lid_transcription.json" + }, + "Create a transcription from blob container": { + "$ref": "./examples/create_container_transcription.json" + } + } + } + }, + "/transcriptions/{id}": { + "get": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Gets the transcription identified by the given ID.", + "operationId": "Transcriptions_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the transcription.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Transcription" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a transcription": { + "$ref": "./examples/get_transcription.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Updates the mutable details of the transcription identified by its ID.", + "operationId": "Transcriptions_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the transcription.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "transcriptionUpdate", + "description": "The updated values for the transcription.", + "required": true, + "schema": { + "$ref": "#/definitions/TranscriptionUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Transcription" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a transcription": { + "$ref": "./examples/update_transcription.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Deletes the specified transcription task.", + "operationId": "Transcriptions_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the transcription.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The transcription was successfully deleted or did not exist." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a transcription": { + "$ref": "./examples/delete_transcription.json" + } + } + } + }, + "/transcriptions/{id}/files": { + "get": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Gets the files of the transcription identified by the given ID.", + "operationId": "Transcriptions_ListFiles", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the transcription.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + }, + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available files.\r\n - Supported properties: name, createdDateTime, kind.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=name eq 'myaudio.wav.json' and kind eq 'Transcription'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedFiles" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all files": { + "$ref": "./examples/get_transcription_files.json" + }, + "Get all transcription files the name of which starts with the specified string": { + "$ref": "./examples/get_transcription_files_filtered_by_name.json" + } + } + } + }, + "/transcriptions/{id}/files/{fileId}": { + "get": { + "tags": [ + "Custom Speech transcriptions:" + ], + "summary": "Gets one specific file (identified with fileId) from a transcription (identified with id).", + "operationId": "Transcriptions_GetFile", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the transcription.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "path", + "name": "fileId", + "description": "The identifier of the file.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "$ref": "#/parameters/sasValidityQueryParameter" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/File" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a file": { + "$ref": "./examples/get_transcription_file.json" + } + } + } + }, + "/webhooks": { + "get": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Gets the list of web hooks for the authenticated subscription.", + "operationId": "WebHooks_List", + "produces": [ + "application/json" + ], + "parameters": [ + { + "$ref": "#/parameters/skipQueryParameter" + }, + { + "$ref": "#/parameters/topQueryParameter" + }, + { + "in": "query", + "name": "filter", + "description": "A filtering expression for selecting a subset of the available hooks.\r\n Supported properties: displayName, description, createdDateTime, lastActionDateTime, status and webUrl.\r\n - Operators:\r\n - eq, ne are supported for all properties.\r\n - gt, ge, lt, le are supported for createdDateTime and lastActionDateTime.\r\n - and, or, not are supported.\r\n - Example:\r\n filter=displayName eq 'test'", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/PaginatedWebHooks" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-pageable": { + "itemName": "values", + "nextLinkName": "@nextLink" + }, + "x-ms-examples": { + "Get all web hooks": { + "$ref": "./examples/get_web_hooks.json" + }, + "Get all web hooks created in 03/2020 ordered by their name": { + "$ref": "./examples/get_web_hooks_from_march_2020.json" + } + } + }, + "post": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Creates a new web hook.", + "description": "If the property secret in the configuration is present and contains a non-empty string, it will be used to create a SHA256 hash of the payload with\r\nthe secret as HMAC key. This hash will be set as X-MicrosoftSpeechServices-Signature header when calling back into the registered URL.\r\n \r\nWhen calling back into the registered URL, the request will contain a X-MicrosoftSpeechServices-Event header containing one of the registered event\r\ntypes. There will be one request per registered event type.\r\n \r\nAfter successfully registering the web hook, it will not be usable until a challenge/response is completed. To do this, a request with the event type\r\nchallenge will be made with a query parameter called validationToken. Respond to the challenge with a 200 OK containing the value of the validationToken\r\nquery parameter as the response body. When the challenge/response is successfully completed, the web hook will begin receiving events.", + "operationId": "WebHooks_Create", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "webHook", + "description": "The details of the new web hook.", + "required": true, + "schema": { + "$ref": "#/definitions/WebHook" + } + } + ], + "responses": { + "201": { + "description": "The response contains information about the entity as payload and its location as header.", + "schema": { + "$ref": "#/definitions/WebHook" + }, + "headers": { + "Location": { + "description": "The location of the created resource.", + "type": "string", + "format": "uri" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Create a web hook": { + "$ref": "./examples/create_web_hook.json" + } + } + } + }, + "/webhooks/{id}": { + "get": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Gets the web hook identified by the given ID.", + "operationId": "WebHooks_Get", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the web hook.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/WebHook" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Get a web hook": { + "$ref": "./examples/get_web_hook.json" + } + } + }, + "patch": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Updates the web hook identified by the given ID.", + "description": "If the property secret in the configuration is omitted or contains an empty string, future callbacks won't contain X-MicrosoftSpeechServices-Signature\r\nheaders. If the property contains a non-empty string, it will be used to create a SHA256 hash of the payload with the secret as HMAC key. This hash\r\nwill be set as X-MicrosoftSpeechServices-Signature header when calling back into the registered URL.\r\n \r\nIf the URL changes, the web hook will stop receiving events until a\r\nchallenge/response is completed. To do this, a request with the event type challenge will be made with a query parameter called validationToken.\r\nRespond to the challenge with a 200 OK containing the value of the validationToken query parameter as the response body. When the challenge/response\r\nis successfully completed, the web hook will begin receiving events.", + "operationId": "WebHooks_Update", + "consumes": [ + "application/json", + "application/merge-patch+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the web hook.", + "required": true, + "type": "string", + "format": "uuid" + }, + { + "in": "body", + "name": "webHookUpdate", + "description": "The updated values for the web hook.", + "required": true, + "schema": { + "$ref": "#/definitions/WebHookUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/WebHook" + }, + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait for a non terminal operation to complete.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Update a web hook": { + "$ref": "./examples/update_web_hook.json" + } + } + }, + "delete": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Deletes the web hook identified by the given ID.", + "operationId": "WebHooks_Delete", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the web hook.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "204": { + "description": "The web hook was successfully deleted." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Delete a web hook": { + "$ref": "./examples/delete_web_hook.json" + } + } + } + }, + "/webhooks/{id}:ping": { + "post": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Sends a ping event to the registered URL.", + "description": "The request body of the POST request sent to the registered web hook URL is of the same shape as in the GET request for a specific hook.\r\nThe Swagger Schema ID of the model is WebHookV3.\r\n \r\nThe request will contain a X-MicrosoftSpeechServices-Event header with the value ping. If the web hook was registered with\r\na secret it will contain a X-MicrosoftSpeechServices-Signature header with an SHA256 hash of the payload with\r\nthe secret as HMAC key. The hash is base64 encoded.", + "operationId": "WebHooks_Ping", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the web hook to ping.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "202": { + "description": "Started trying to send a ping request to the web hook.", + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait before accessing the resource created in this operation.", + "type": "integer", + "format": "int32" + } + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Ping a web hook": { + "$ref": "./examples/ping_web_hook.json" + } + } + } + }, + "/webhooks/{id}:test": { + "post": { + "tags": [ + "Custom Speech web hooks:" + ], + "summary": "Sends a request for each registered event type to the registered URL.", + "description": "The payload will be generated from the last entity that would have invoked the web hook. If no entity is present for none of the registered event types,\r\nthe POST will respond with 204. If a test request can be made, it will respond with 200.\r\nThe request will contain a X-MicrosoftSpeechServices-Event header with the respective registered event type.\r\nIf the web hook was registered with a secret it will contain a X-MicrosoftSpeechServices-Signature header with an SHA256 hash of the payload with\r\nthe secret as HMAC key. The hash is base64 encoded.", + "operationId": "WebHooks_Test", + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "description": "The identifier of the web hook to ping.", + "required": true, + "type": "string", + "format": "uuid" + } + ], + "responses": { + "202": { + "description": "A test request with the last entity is sent to the registered web hook.", + "headers": { + "Retry-After": { + "description": "The minimum number of seconds to wait before accessing the resource created in this operation.", + "type": "integer", + "format": "int32" + } + } + }, + "204": { + "description": "No entity could be found for any event type, so no test request is sent to the registered web hook." + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Test a web hook": { + "$ref": "./examples/test_web_hook.json" + } + } + } + }, + "/healthstatus": { + "get": { + "tags": [ + "Service Health:" + ], + "summary": "Returns the overall health of the service and optionally of the different subcomponents.", + "operationId": "ServiceHealth_Get", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ServiceHealth" + } + }, + "default": { + "description": "An error occurred.", + "schema": { + "$ref": "#/definitions/Error" + } + } + }, + "x-ms-examples": { + "Gets the health status": { + "$ref": "examples/get_healthstatus.json" + } + } + } + } + }, + "definitions": { + "BaseModel": { + "title": "BaseModel", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/SharedModel" + } + ], + "properties": { + "links": { + "$ref": "#/definitions/BaseModelLinks" + }, + "properties": { + "$ref": "#/definitions/BaseModelProperties" + } + } + }, + "BaseModelDeprecationDates": { + "title": "BaseModelDeprecationDates", + "type": "object", + "properties": { + "adaptationDateTime": { + "format": "date-time", + "description": "The date when adaptation becomes deprecated.", + "type": "string", + "readOnly": true + }, + "transcriptionDateTime": { + "format": "date-time", + "description": "The date when transcription becomes deprecated.", + "type": "string", + "readOnly": true + } + } + }, + "BaseModelFeatures": { + "title": "BaseModelFeatures", + "description": "Features supported by the model.", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/SharedModelFeatures" + } + ], + "properties": { + "supportsAdaptationsWith": { + "description": "Supported dataset kinds to adapt the model.", + "type": "array", + "items": { + "$ref": "#/definitions/DatasetKind" + }, + "readOnly": true + } + } + }, + "BaseModelLinks": { + "title": "BaseModelLinks", + "type": "object", + "properties": { + "manifest": { + "format": "uri", + "description": "The location to get a manifest for this model to be used in the on-prem container. See operation \"Models_GetCustomModelManifest\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "BaseModelProperties": { + "title": "BaseModelProperties", + "type": "object", + "properties": { + "deprecationDates": { + "$ref": "#/definitions/BaseModelDeprecationDates" + }, + "features": { + "$ref": "#/definitions/BaseModelFeatures" + } + } + }, + "BlockKind": { + "title": "BlockKind", + "description": "Type of data block.", + "enum": [ + "Committed", + "Uncommitted", + "Latest" + ], + "type": "string", + "x-ms-enum": { + "name": "BlockKind", + "modelAsString": true, + "values": [ + { + "value": "Committed", + "description": "A data block from the committed block list." + }, + { + "value": "Uncommitted", + "description": "A data block from the uncommitted block list." + }, + { + "value": "Latest", + "description": "A data block from the uncommitted block list, if present, otherwise from the committed block list." + } + ] + } + }, + "CommitBlocksEntry": { + "title": "CommitBlocksEntry", + "description": "Entry of the commit block list.", + "type": "object", + "properties": { + "kind": { + "$ref": "#/definitions/BlockKind" + }, + "id": { + "type": "string" + } + } + }, + "Component": { + "title": "Component", + "type": "object", + "properties": { + "message": { + "description": "Additional messages about the current service health.", + "type": "string", + "readOnly": true + }, + "name": { + "description": "The name of the component.", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/HealthStatus" + }, + "type": { + "description": "The type of this component.", + "type": "string", + "readOnly": true + } + } + }, + "CustomModel": { + "title": "CustomModel", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/SharedModel" + } + ], + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "links": { + "$ref": "#/definitions/CustomModelLinks" + }, + "properties": { + "$ref": "#/definitions/CustomModelProperties" + }, + "text": { + "description": "The text used to adapt this language model.", + "type": "string" + }, + "baseModel": { + "$ref": "#/definitions/EntityReference" + }, + "datasets": { + "description": "Datasets used for adaptation.", + "type": "array", + "items": { + "$ref": "#/definitions/EntityReference" + } + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "CustomModelDeprecationDates": { + "title": "CustomModelDeprecationDates", + "type": "object", + "properties": { + "transcriptionDateTime": { + "format": "date-time", + "description": "The date when transcription becomes deprecated.", + "type": "string", + "readOnly": true + } + } + }, + "CustomModelFeatures": { + "title": "CustomModelFeatures", + "description": "Features supported by the model.", + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/SharedModelFeatures" + } + ] + }, + "CustomModelLinks": { + "title": "CustomModelLinks", + "type": "object", + "properties": { + "copyTo": { + "format": "uri", + "description": "The location to the model copy action. See operation \"Models_CopyTo\" for more details.", + "type": "string", + "readOnly": true + }, + "files": { + "format": "uri", + "description": "The location to get all files of this entity. See operation \"Models_ListFiles\" for more details.", + "type": "string", + "readOnly": true + }, + "manifest": { + "format": "uri", + "description": "The location to get a manifest for this model to be used in the on-prem container. See operation \"Models_GetCustomModelManifest\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "CustomModelProperties": { + "title": "CustomModelProperties", + "type": "object", + "properties": { + "customModelWeightPercent": { + "format": "int32", + "description": "The weight of custom model between 1 (1% custom model and 99% base model) and 100 (100% custom model and 0% base model).\r\nWhen this property is not set, the service chooses a suitable value (get the model to retrieve the selected weight).\r\nStart without using this property. If needed, choose a larger (or smaller) weight to increase (or decrease) the impact of the custom model.", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "deprecationDates": { + "$ref": "#/definitions/CustomModelDeprecationDates" + }, + "features": { + "$ref": "#/definitions/CustomModelFeatures" + }, + "email": { + "description": "The email address to send email notifications to in case the operation completes.\r\nThe value will be removed after successfully sending the email.", + "type": "string" + }, + "error": { + "$ref": "#/definitions/EntityError" + } + } + }, + "Dataset": { + "title": "Dataset", + "required": [ + "displayName", + "kind", + "locale" + ], + "type": "object", + "properties": { + "links": { + "$ref": "#/definitions/DatasetLinks" + }, + "properties": { + "$ref": "#/definitions/DatasetProperties" + }, + "kind": { + "$ref": "#/definitions/DatasetKind" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "project": { + "$ref": "#/definitions/EntityReference" + }, + "contentUrl": { + "format": "uri", + "description": "The URL of the data for the dataset.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "locale": { + "description": "The locale of the contained data.", + "minLength": 1, + "type": "string" + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + } + } + }, + "DatasetKind": { + "title": "DatasetKind", + "description": "Type of data import.", + "enum": [ + "Language", + "Acoustic", + "Pronunciation", + "AudioFiles", + "LanguageMarkdown" + ], + "type": "string", + "x-ms-enum": { + "name": "DatasetKind", + "modelAsString": true, + "values": [ + { + "value": "Language", + "description": "A language dataset." + }, + { + "value": "Acoustic", + "description": "An acoustic dataset." + }, + { + "value": "Pronunciation", + "description": "A pronunciation dataset." + }, + { + "value": "AudioFiles", + "description": "An audio files dataset." + }, + { + "value": "LanguageMarkdown", + "description": "A language markdown dataset." + } + ] + } + }, + "DatasetLinks": { + "title": "DatasetLinks", + "type": "object", + "properties": { + "files": { + "format": "uri", + "description": "The location to get all files of this entity. See operation \"Datasets_ListFiles\" for more details.", + "type": "string", + "readOnly": true + }, + "commitBlocks": { + "format": "uri", + "description": "The location to commit the list of blocks when uploading a dataset using blocks. See operation \"Datasets_CommitBlocks\" for more details.", + "type": "string", + "readOnly": true + }, + "listBlocks": { + "format": "uri", + "description": "The location to list the already uploaded blocks of this entity when uploading a dataset using blocks. See operation \"Datasets_GetDatasetBlocks\" for more details.", + "type": "string", + "readOnly": true + }, + "uploadBlocks": { + "format": "uri", + "description": "The location to upload blocks to when uploading a dataset using blocks. See operation \"Datasets_UploadBlock\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "DatasetLocales": { + "title": "DatasetLocales", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/DatasetKind" + } + } + }, + "DatasetProperties": { + "title": "DatasetProperties", + "type": "object", + "properties": { + "acceptedLineCount": { + "format": "int32", + "description": "The number of lines accepted for this data set.", + "type": "integer", + "readOnly": true + }, + "rejectedLineCount": { + "format": "int32", + "description": "The number of lines rejected for this data set.", + "type": "integer", + "readOnly": true + }, + "duration": { + "description": "The total duration of the datasets if it contains audio files. The duration is encoded as ISO 8601 duration\r\n(\"PnYnMnDTnHnMnS\", see https://en.wikipedia.org/wiki/ISO_8601#Durations).", + "type": "string", + "readOnly": true + }, + "email": { + "description": "The email address to send email notifications to in case the operation completes.\r\nThe value will be removed after successfully sending the email.", + "type": "string" + }, + "error": { + "$ref": "#/definitions/EntityError" + } + } + }, + "DatasetUpdate": { + "title": "DatasetUpdate", + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "DetailedErrorCode": { + "title": "DetailedErrorCode", + "description": "Detailed error code enum.", + "enum": [ + "InvalidParameterValue", + "InvalidRequestBodyFormat", + "EmptyRequest", + "MissingInputRecords", + "InvalidDocument", + "ModelVersionIncorrect", + "InvalidDocumentBatch", + "UnsupportedLanguageCode", + "DataImportFailed", + "InUseViolation", + "InvalidLocale", + "InvalidBaseModel", + "InvalidAdaptationMapping", + "InvalidDataset", + "InvalidTest", + "FailedDataset", + "InvalidModel", + "InvalidTranscription", + "InvalidPayload", + "InvalidParameter", + "EndpointWithoutLogging", + "InvalidPermissions", + "InvalidPrerequisite", + "InvalidProductId", + "InvalidSubscription", + "InvalidProject", + "InvalidProjectKind", + "InvalidRecordingsUri", + "OnlyOneOfUrlsOrContainerOrDataset", + "ExceededNumberOfRecordingsUris", + "ModelMismatch", + "ProjectGenderMismatch", + "ModelDeprecated", + "ModelExists", + "ModelNotDeployable", + "EndpointNotUpdatable", + "SingleDefaultEndpoint", + "EndpointCannotBeDefault", + "InvalidModelUri", + "SubscriptionNotFound", + "QuotaViolation", + "UnsupportedDelta", + "UnsupportedFilter", + "UnsupportedPagination", + "UnsupportedDynamicConfiguration", + "UnsupportedOrderBy", + "NoUtf8WithBom", + "ModelDeploymentNotCompleteState", + "SkuLimitsExist", + "DeployingFailedModel", + "UnsupportedTimeRange", + "InvalidLogDate", + "InvalidLogId", + "InvalidLogStartTime", + "InvalidLogEndTime", + "InvalidTopForLogs", + "InvalidSkipTokenForLogs", + "DeleteNotAllowed", + "Forbidden", + "DeployNotAllowed", + "UnexpectedError", + "InvalidCollection", + "InvalidCallbackUri", + "InvalidSasValidityDuration", + "InaccessibleCustomerStorage", + "UnsupportedClassBasedAdaptation", + "InvalidWebHookEventKind", + "InvalidTimeToLive" + ], + "type": "string", + "x-ms-enum": { + "name": "DetailedErrorCode", + "modelAsString": true, + "values": [ + { + "value": "InvalidParameterValue", + "description": "Invalid parameter value." + }, + { + "value": "InvalidRequestBodyFormat", + "description": "Invalid request body format." + }, + { + "value": "EmptyRequest", + "description": "Empty Request." + }, + { + "value": "MissingInputRecords", + "description": "Missing Input Records." + }, + { + "value": "InvalidDocument", + "description": "Invalid Document." + }, + { + "value": "ModelVersionIncorrect", + "description": "Model Version Incorrect." + }, + { + "value": "InvalidDocumentBatch", + "description": "Invalid Document Batch." + }, + { + "value": "UnsupportedLanguageCode", + "description": "Unsupported language code." + }, + { + "value": "DataImportFailed", + "description": "Data import failed." + }, + { + "value": "InUseViolation", + "description": "In use violation." + }, + { + "value": "InvalidLocale", + "description": "Invalid locale." + }, + { + "value": "InvalidBaseModel", + "description": "Invalid base model." + }, + { + "value": "InvalidAdaptationMapping", + "description": "Invalid adaptation mapping." + }, + { + "value": "InvalidDataset", + "description": "Invalid dataset." + }, + { + "value": "InvalidTest", + "description": "Invalid test." + }, + { + "value": "FailedDataset", + "description": "Failed dataset." + }, + { + "value": "InvalidModel", + "description": "Invalid model." + }, + { + "value": "InvalidTranscription", + "description": "Invalid transcription." + }, + { + "value": "InvalidPayload", + "description": "Invalid payload." + }, + { + "value": "InvalidParameter", + "description": "Invalid parameter." + }, + { + "value": "EndpointWithoutLogging", + "description": "Endpoint without logging." + }, + { + "value": "InvalidPermissions", + "description": "Invalid permissions." + }, + { + "value": "InvalidPrerequisite", + "description": "Invalid prerequisite." + }, + { + "value": "InvalidProductId", + "description": "Invalid product id." + }, + { + "value": "InvalidSubscription", + "description": "Invalid subscription." + }, + { + "value": "InvalidProject", + "description": "Invalid project." + }, + { + "value": "InvalidProjectKind", + "description": "Invalid project kind." + }, + { + "value": "InvalidRecordingsUri", + "description": "Invalid recordings uri." + }, + { + "value": "OnlyOneOfUrlsOrContainerOrDataset", + "description": "Only one of urls or container or dataset." + }, + { + "value": "ExceededNumberOfRecordingsUris", + "description": "Exceeded number of recordings uris." + }, + { + "value": "ModelMismatch", + "description": "Model mismatch." + }, + { + "value": "ProjectGenderMismatch", + "description": "Project gender mismatch." + }, + { + "value": "ModelDeprecated", + "description": "Model deprecated." + }, + { + "value": "ModelExists", + "description": "Model exists." + }, + { + "value": "ModelNotDeployable", + "description": "Model not deployable." + }, + { + "value": "EndpointNotUpdatable", + "description": "Endpoint not updatable." + }, + { + "value": "SingleDefaultEndpoint", + "description": "Single default endpoint." + }, + { + "value": "EndpointCannotBeDefault", + "description": "Endpoint cannot be default." + }, + { + "value": "InvalidModelUri", + "description": "Invalid model uri." + }, + { + "value": "SubscriptionNotFound", + "description": "Subscription not found." + }, + { + "value": "QuotaViolation", + "description": "Quota violation." + }, + { + "value": "UnsupportedDelta", + "description": "Unsupported delta." + }, + { + "value": "UnsupportedFilter", + "description": "Unsupported filter." + }, + { + "value": "UnsupportedPagination", + "description": "Unsupported pagination." + }, + { + "value": "UnsupportedDynamicConfiguration", + "description": "Unsupported dynamic configuration." + }, + { + "value": "UnsupportedOrderBy", + "description": "Unsupported order by." + }, + { + "value": "NoUtf8WithBom", + "description": "No utf8 with bom." + }, + { + "value": "ModelDeploymentNotCompleteState", + "description": "Model deployment not complete state." + }, + { + "value": "SkuLimitsExist", + "description": "Sku limits exist." + }, + { + "value": "DeployingFailedModel", + "description": "Deploying failed model." + }, + { + "value": "UnsupportedTimeRange", + "description": "Unsupported time range." + }, + { + "value": "InvalidLogDate", + "description": "Invalid log date." + }, + { + "value": "InvalidLogId", + "description": "Invalid log id." + }, + { + "value": "InvalidLogStartTime", + "description": "Invalid log start time." + }, + { + "value": "InvalidLogEndTime", + "description": "Invalid log end time." + }, + { + "value": "InvalidTopForLogs", + "description": "Invalid top for logs." + }, + { + "value": "InvalidSkipTokenForLogs", + "description": "Invalid skip token for logs." + }, + { + "value": "DeleteNotAllowed", + "description": "Delete not allowed." + }, + { + "value": "Forbidden", + "description": "Forbidden." + }, + { + "value": "DeployNotAllowed", + "description": "Deploy not allowed." + }, + { + "value": "UnexpectedError", + "description": "Unexpected error." + }, + { + "value": "InvalidCollection", + "description": "Invalid collection." + }, + { + "value": "InvalidCallbackUri", + "description": "Invalid callback uri." + }, + { + "value": "InvalidSasValidityDuration", + "description": "Invalid sas validity duration." + }, + { + "value": "InaccessibleCustomerStorage", + "description": "Inaccessible customer storage." + }, + { + "value": "UnsupportedClassBasedAdaptation", + "description": "Unsupported class based adaptation." + }, + { + "value": "InvalidWebHookEventKind", + "description": "Invalid web hook event kind." + }, + { + "value": "InvalidTimeToLive", + "description": "Invalid time to live." + } + ] + } + }, + "DiarizationProperties": { + "title": "DiarizationProperties", + "required": [ + "speakers" + ], + "type": "object", + "properties": { + "speakers": { + "$ref": "#/definitions/DiarizationSpeakersProperties" + } + } + }, + "DiarizationSpeakersProperties": { + "title": "DiarizationSpeakersProperties", + "type": "object", + "properties": { + "minCount": { + "format": "int32", + "description": "A hint for the minimum number of speakers for diarization. Must be smaller than or equal to the maxSpeakers property.", + "minimum": 1, + "type": "integer" + }, + "maxCount": { + "format": "int32", + "description": "The maximum number of speakers for diarization. Must be less than 36 and larger than or equal to the minSpeakers property.", + "minimum": 1, + "type": "integer" + } + } + }, + "Endpoint": { + "title": "Endpoint", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "links": { + "$ref": "#/definitions/EndpointLinks" + }, + "properties": { + "$ref": "#/definitions/EndpointProperties" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "text": { + "description": "The text used to adapt a language model for this endpoint.", + "type": "string" + }, + "model": { + "$ref": "#/definitions/EntityReference" + }, + "locale": { + "description": "The locale of the contained data.", + "minLength": 1, + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + } + } + }, + "EndpointLinks": { + "title": "EndpointLinks", + "type": "object", + "properties": { + "restInteractive": { + "format": "uri", + "description": "The REST endpoint for short requests up to 15 seconds.", + "type": "string", + "readOnly": true + }, + "restConversation": { + "format": "uri", + "description": "The REST endpoint for requests up to 60 seconds.", + "type": "string", + "readOnly": true + }, + "restDictation": { + "format": "uri", + "description": "The REST endpoint for requests up to 60 seconds, supporting dictation of punctuation marks.", + "type": "string", + "readOnly": true + }, + "webSocketInteractive": { + "format": "uri", + "description": "The Speech SDK endpoint for short requests up to 15 seconds with a single final result.", + "type": "string", + "readOnly": true + }, + "webSocketConversation": { + "format": "uri", + "description": "The Speech SDK endpoint for long requests with multiple final results.", + "type": "string", + "readOnly": true + }, + "webSocketDictation": { + "format": "uri", + "description": "The Speech SDK endpoint for long requests with multiple final results, supporting dictation of\r\npunctuation marks.", + "type": "string", + "readOnly": true + }, + "logs": { + "format": "uri", + "description": "The audio and transcription logs for this endpoint. See operation \"Endpoints_ListLogs\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "EndpointProperties": { + "title": "EndpointProperties", + "type": "object", + "properties": { + "loggingEnabled": { + "description": "A value indicating whether content logging (audio & transcriptions) is being used for a deployment.", + "type": "boolean" + }, + "timeToLive": { + "description": "How long the endpoint will be kept in the system. Once the endpoint reaches the time to live\r\nafter completion (successful or failed) it will be automatically deleted. Not setting this value or setting\r\nto 0 will disable automatic deletion. The longest supported duration is 31 days.\r\nThe duration is encoded as ISO 8601 duration (\"PnYnMnDTnHnMnS\", see https://en.wikipedia.org/wiki/ISO_8601#Durations).", + "type": "string" + }, + "email": { + "description": "The email address to send email notifications to in case the operation completes.\r\nThe value will be removed after successfully sending the email.", + "type": "string" + }, + "error": { + "$ref": "#/definitions/EntityError" + } + } + }, + "EndpointPropertiesUpdate": { + "title": "EndpointPropertiesUpdate", + "type": "object", + "properties": { + "contentLoggingEnabled": { + "description": "A value indicating whether content logging (audio & transcriptions)\r\nis being used for a deployment.", + "type": "boolean" + } + } + }, + "EndpointUpdate": { + "title": "EndpointUpdate", + "type": "object", + "properties": { + "model": { + "$ref": "#/definitions/EntityReference" + }, + "properties": { + "$ref": "#/definitions/EndpointPropertiesUpdate" + }, + "project": { + "$ref": "#/definitions/EntityReference" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "EntityError": { + "title": "EntityError", + "type": "object", + "properties": { + "code": { + "description": "The code of this error.", + "type": "string", + "readOnly": true + }, + "message": { + "description": "The message for this error.", + "type": "string", + "readOnly": true + } + } + }, + "EntityReference": { + "title": "EntityReference", + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "format": "uri", + "description": "The location of the referenced entity.", + "type": "string" + } + } + }, + "Error": { + "title": "Error", + "description": "New format which conforms to the new Cognitive Services API guidelines which is available at https://microsoft.sharepoint.com/%3Aw%3A/t/CognitiveServicesPMO/EUoytcrjuJdKpeOKIK_QRC8BPtUYQpKBi8JsWyeDMRsWlQ?e=CPq8ow.\r\nThis contains an outer error with error code, message, details, target and an inner error with more descriptive details.", + "type": "object", + "properties": { + "code": { + "$ref": "#/definitions/ErrorCode" + }, + "details": { + "description": "Additional supportive details regarding the error and/or expected policies.", + "type": "array", + "items": { + "$ref": "#/definitions/Error" + } + }, + "message": { + "description": "High level error message.", + "type": "string" + }, + "target": { + "description": "The source of the error.\r\nFor example it would be \"documents\" or \"document id\" in case of invalid document.", + "type": "string" + }, + "innerError": { + "$ref": "#/definitions/InnerError" + } + } + }, + "ErrorCode": { + "title": "ErrorCode", + "description": "High level error codes.", + "enum": [ + "InvalidRequest", + "InvalidArgument", + "InternalServerError", + "ServiceUnavailable", + "NotFound", + "PipelineError", + "Conflict", + "InternalCommunicationFailed", + "Forbidden", + "NotAllowed", + "Unauthorized", + "UnsupportedMediaType", + "TooManyRequests", + "UnprocessableEntity" + ], + "type": "string", + "x-ms-enum": { + "name": "ErrorCode", + "modelAsString": true, + "values": [ + { + "value": "InvalidRequest", + "description": "Representing the invalid request error code." + }, + { + "value": "InvalidArgument", + "description": "Representing the invalid argument error code." + }, + { + "value": "InternalServerError", + "description": "Representing the internal server error error code." + }, + { + "value": "ServiceUnavailable", + "description": "Representing the service unavailable error code." + }, + { + "value": "NotFound", + "description": "Representing the not found error code." + }, + { + "value": "PipelineError", + "description": "Representing the pipeline error error code." + }, + { + "value": "Conflict", + "description": "Representing the conflict error code." + }, + { + "value": "InternalCommunicationFailed", + "description": "Representing the internal communication failed error code." + }, + { + "value": "Forbidden", + "description": "Representing the forbidden error code." + }, + { + "value": "NotAllowed", + "description": "Representing the not allowed error code." + }, + { + "value": "Unauthorized", + "description": "Representing the unauthorized error code." + }, + { + "value": "UnsupportedMediaType", + "description": "Representing the unsupported media type error code." + }, + { + "value": "TooManyRequests", + "description": "Representing the too many requests error code." + }, + { + "value": "UnprocessableEntity", + "description": "Representing the unprocessable entity error code." + } + ] + } + }, + "Evaluation": { + "title": "Evaluation", + "required": [ + "dataset", + "displayName", + "locale", + "model1", + "model2" + ], + "type": "object", + "properties": { + "model1": { + "$ref": "#/definitions/EntityReference" + }, + "model2": { + "$ref": "#/definitions/EntityReference" + }, + "transcription1": { + "$ref": "#/definitions/EntityReference" + }, + "transcription2": { + "$ref": "#/definitions/EntityReference" + }, + "dataset": { + "$ref": "#/definitions/EntityReference" + }, + "links": { + "$ref": "#/definitions/EvaluationLinks" + }, + "properties": { + "$ref": "#/definitions/EvaluationProperties" + }, + "project": { + "$ref": "#/definitions/EntityReference" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "locale": { + "description": "The locale of the contained data.", + "minLength": 1, + "type": "string" + } + } + }, + "EvaluationLinks": { + "title": "EvaluationLinks", + "type": "object", + "properties": { + "files": { + "format": "uri", + "description": "The location to get all files of this entity. See operation \"Evaluations_ListFiles\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "EvaluationProperties": { + "title": "EvaluationProperties", + "type": "object", + "properties": { + "wordErrorRate2": { + "format": "double", + "description": "The word error rate of recognition with model2.", + "type": "number", + "readOnly": true + }, + "wordErrorRate1": { + "format": "double", + "description": "The word error rate of recognition with model1.", + "type": "number", + "readOnly": true + }, + "sentenceErrorRate2": { + "format": "double", + "description": "The sentence error rate of recognition with model2.", + "type": "number", + "readOnly": true + }, + "sentenceCount2": { + "format": "int32", + "description": "The number of processed sentences by model2.", + "type": "integer", + "readOnly": true + }, + "wordCount2": { + "format": "int32", + "description": "The number of processed words by model2.", + "type": "integer", + "readOnly": true + }, + "correctWordCount2": { + "format": "int32", + "description": "The number of correctly recognized words by model2.", + "type": "integer", + "readOnly": true + }, + "wordSubstitutionCount2": { + "format": "int32", + "description": "The number of recognized words by model2, that are substitutions.", + "type": "integer", + "readOnly": true + }, + "wordDeletionCount2": { + "format": "int32", + "description": "The number of recognized words by model2, that are deletions.", + "type": "integer", + "readOnly": true + }, + "wordInsertionCount2": { + "format": "int32", + "description": "The number of recognized words by model2, that are insertions.", + "type": "integer", + "readOnly": true + }, + "sentenceErrorRate1": { + "format": "double", + "description": "The sentence error rate of recognition with model1.", + "type": "number", + "readOnly": true + }, + "sentenceCount1": { + "format": "int32", + "description": "The number of processed sentences by model1.", + "type": "integer", + "readOnly": true + }, + "wordCount1": { + "format": "int32", + "description": "The number of processed words by model1.", + "type": "integer", + "readOnly": true + }, + "correctWordCount1": { + "format": "int32", + "description": "The number of correctly recognized words by model1.", + "type": "integer", + "readOnly": true + }, + "wordSubstitutionCount1": { + "format": "int32", + "description": "The number of recognized words by model1, that are substitutions.", + "type": "integer", + "readOnly": true + }, + "wordDeletionCount1": { + "format": "int32", + "description": "The number of recognized words by model1, that are deletions.", + "type": "integer", + "readOnly": true + }, + "wordInsertionCount1": { + "format": "int32", + "description": "The number of recognized words by model1, that are insertions.", + "type": "integer", + "readOnly": true + }, + "email": { + "description": "The email address to send email notifications to in case the operation completes.\r\nThe value will be removed after successfully sending the email.", + "type": "string" + }, + "error": { + "$ref": "#/definitions/EntityError" + } + } + }, + "EvaluationUpdate": { + "title": "EvaluationUpdate", + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "File": { + "title": "File", + "type": "object", + "properties": { + "kind": { + "$ref": "#/definitions/FileKind" + }, + "links": { + "$ref": "#/definitions/FileLinks" + }, + "createdDateTime": { + "format": "date-time", + "description": "The creation time of this file.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "properties": { + "$ref": "#/definitions/FileProperties" + }, + "name": { + "description": "The name of this file.", + "type": "string", + "readOnly": true + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + } + } + }, + "FileKind": { + "title": "FileKind", + "description": "Type of data.", + "enum": [ + "DatasetReport", + "Audio", + "LanguageData", + "PronunciationData", + "AcousticDataArchive", + "AcousticDataTranscriptionV2", + "Transcription", + "TranscriptionReport", + "EvaluationDetails", + "ModelReport" + ], + "type": "string", + "readOnly": true, + "x-ms-enum": { + "name": "FileKind", + "modelAsString": true, + "values": [ + { + "value": "DatasetReport", + "description": "Type of data is dataset report." + }, + { + "value": "Audio", + "description": "Type of data is audio." + }, + { + "value": "LanguageData", + "description": "Type of data is language data." + }, + { + "value": "PronunciationData", + "description": "Type of data is pronunciation data." + }, + { + "value": "AcousticDataArchive", + "description": "Type of data is acoustic data archive." + }, + { + "value": "AcousticDataTranscriptionV2", + "description": "Type of data is acoustic data transcription v2." + }, + { + "value": "Transcription", + "description": "Type of data is transcription." + }, + { + "value": "TranscriptionReport", + "description": "Type of data is transcription report." + }, + { + "value": "EvaluationDetails", + "description": "Type of data is evaluation details." + }, + { + "value": "ModelReport", + "description": "Type of data is model report." + } + ] + } + }, + "FileLinks": { + "title": "FileLinks", + "type": "object", + "properties": { + "contentUrl": { + "format": "uri", + "description": "The url to retrieve the content of this file.", + "type": "string", + "readOnly": true + } + } + }, + "FileProperties": { + "title": "FileProperties", + "type": "object", + "properties": { + "size": { + "format": "int64", + "description": "The size of the data in bytes.", + "type": "integer", + "readOnly": true + }, + "duration": { + "description": "The duration in case this file is an audio file. The duration is encoded as ISO 8601\r\nduration (\"PnYnMnDTnHnMnS\", see https://en.wikipedia.org/wiki/ISO_8601#Durations).", + "type": "string", + "readOnly": true + } + } + }, + "HealthStatus": { + "title": "HealthStatus", + "description": "Health status of the service.", + "enum": [ + "Unhealthy", + "Degraded", + "Healthy" + ], + "type": "string", + "readOnly": true, + "x-ms-enum": { + "name": "HealthStatus", + "modelAsString": false, + "values": [ + { + "value": "Unhealthy", + "description": "Unhealthy." + }, + { + "value": "Degraded", + "description": "Degraded." + }, + { + "value": "Healthy", + "description": "Healthy." + } + ] + } + }, + "InnerError": { + "title": "InnerError", + "description": "New Inner Error format which conforms to Cognitive Services API Guidelines which is available at https://microsoft.sharepoint.com/%3Aw%3A/t/CognitiveServicesPMO/EUoytcrjuJdKpeOKIK_QRC8BPtUYQpKBi8JsWyeDMRsWlQ?e=CPq8ow.\r\nThis contains required properties ErrorCode, message and optional properties target, details(key value pair), inner error(this can be nested).", + "type": "object", + "properties": { + "code": { + "$ref": "#/definitions/DetailedErrorCode" + }, + "details": { + "description": "Additional supportive details regarding the error and/or expected policies.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "message": { + "description": "High level error message.", + "type": "string" + }, + "target": { + "description": "The source of the error.\r\nFor example it would be \"documents\" or \"document id\" in case of invalid document.", + "type": "string" + }, + "innerError": { + "$ref": "#/definitions/InnerError" + } + } + }, + "LanguageIdentificationProperties": { + "title": "LanguageIdentificationProperties", + "required": [ + "candidateLocales" + ], + "type": "object", + "properties": { + "candidateLocales": { + "description": "The candidate locales for language identification (example [\"en-US\", \"de-DE\", \"es-ES\"]). A minimum of 2 and a maximum of 10 candidate locales, including the main locale for the transcription, is supported.", + "type": "array", + "items": { + "type": "string" + } + }, + "speechModelMapping": { + "description": "An optional mapping of locales to speech model entities. If no model is given for a locale, the default base model is used.\r\nKeys must be locales contained in the candidate locales, values are entities for models of the respective locales.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/EntityReference" + } + } + } + }, + "ModelCopy": { + "title": "ModelCopy", + "required": [ + "targetSubscriptionKey" + ], + "type": "object", + "properties": { + "targetSubscriptionKey": { + "description": "The subscription key of the subscription that is the target of the copy operation.", + "minLength": 1, + "type": "string" + } + } + }, + "ModelFile": { + "title": "ModelFile", + "type": "object", + "properties": { + "name": { + "description": "The name of this file.", + "type": "string", + "readOnly": true + }, + "contentUrl": { + "format": "uri", + "description": "The url to retrieve the content of this file.", + "type": "string", + "readOnly": true + } + } + }, + "ModelManifest": { + "title": "ModelManifest", + "required": [ + "model", + "modelFiles", + "properties" + ], + "type": "object", + "properties": { + "model": { + "$ref": "#/definitions/EntityReference" + }, + "modelFiles": { + "description": "The model files of this model.", + "type": "array", + "items": { + "$ref": "#/definitions/ModelFile" + } + }, + "properties": { + "description": "The configuration for running this model in a container.", + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + }, + "ModelUpdate": { + "title": "ModelUpdate", + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "PaginatedBaseModels": { + "title": "PaginatedBaseModels", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/BaseModel" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedCustomModels": { + "title": "PaginatedCustomModels", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/CustomModel" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedDatasets": { + "title": "PaginatedDatasets", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/Dataset" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedEndpoints": { + "title": "PaginatedEndpoints", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/Endpoint" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedEvaluations": { + "title": "PaginatedEvaluations", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/Evaluation" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedFiles": { + "title": "PaginatedFiles", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/File" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedProjects": { + "title": "PaginatedProjects", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/Project" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedTranscriptions": { + "title": "PaginatedTranscriptions", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/Transcription" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "PaginatedWebHooks": { + "title": "PaginatedWebHooks", + "type": "object", + "properties": { + "values": { + "description": "A list of entities limited by either the passed query parameters 'skip' and 'top' or their default values.\r\n \r\nWhen iterating through a list using pagination and deleting entities in parallel, some entities will be skipped in the results.\r\nIt's recommended to build a list on the client and delete after the fetching of the complete list.", + "type": "array", + "items": { + "$ref": "#/definitions/WebHook" + }, + "readOnly": true + }, + "@nextLink": { + "format": "uri", + "description": "A link to the next set of paginated results if there are more entities available; otherwise null.", + "type": "string", + "readOnly": true + } + } + }, + "ProfanityFilterMode": { + "title": "ProfanityFilterMode", + "description": "Mode of profanity filtering.", + "enum": [ + "None", + "Removed", + "Tags", + "Masked" + ], + "type": "string", + "x-ms-enum": { + "name": "ProfanityFilterMode", + "modelAsString": false, + "values": [ + { + "value": "None", + "description": "Disable profanity filtering." + }, + { + "value": "Removed", + "description": "Remove profanity." + }, + { + "value": "Tags", + "description": "Add \"profanity\" XML tags</Profanity>" + }, + { + "value": "Masked", + "description": "Mask the profanity with * except of the first letter, e.g., f***" + } + ] + } + }, + "Project": { + "title": "Project", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "properties": { + "links": { + "$ref": "#/definitions/ProjectLinks" + }, + "properties": { + "$ref": "#/definitions/ProjectProperties" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "locale": { + "description": "The locale of the contained data.", + "minLength": 1, + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + } + } + }, + "ProjectLinks": { + "title": "ProjectLinks", + "type": "object", + "properties": { + "evaluations": { + "format": "uri", + "description": "The location to get a list of all evaluations of this project. See operation \"Projects_ListEvaluations\" for more details.", + "type": "string", + "readOnly": true + }, + "datasets": { + "format": "uri", + "description": "The location to get a list of all datasets of this project. See operation \"Projects_ListDatasets\" for more details.", + "type": "string", + "readOnly": true + }, + "models": { + "format": "uri", + "description": "The location to get a list of all models of this project. See operation \"Projects_ListModels\" for more details.", + "type": "string", + "readOnly": true + }, + "endpoints": { + "format": "uri", + "description": "The location to get a list of all endpoints of this project. See operation \"Projects_ListEndpoints\" for more details.", + "type": "string", + "readOnly": true + }, + "transcriptions": { + "format": "uri", + "description": "The location to get a list of all transcriptions of this project. See operation \"Projects_ListTranscriptions\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "ProjectProperties": { + "title": "ProjectProperties", + "type": "object", + "properties": { + "datasetCount": { + "format": "int32", + "description": "The number of datasets associated to this project.", + "type": "integer", + "readOnly": true + }, + "evaluationCount": { + "format": "int32", + "description": "The number of evaluations associated to this project.", + "type": "integer", + "readOnly": true + }, + "modelCount": { + "format": "int32", + "description": "The number of models associated to this project.", + "type": "integer", + "readOnly": true + }, + "transcriptionCount": { + "format": "int32", + "description": "The number of transcriptions associated to this project.", + "type": "integer", + "readOnly": true + }, + "endpointCount": { + "format": "int32", + "description": "The number of endpoints associated to this project.", + "type": "integer", + "readOnly": true + } + } + }, + "ProjectUpdate": { + "title": "ProjectUpdate", + "type": "object", + "properties": { + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "PunctuationMode": { + "title": "PunctuationMode", + "description": "The mode used for punctuation.", + "enum": [ + "None", + "Dictated", + "Automatic", + "DictatedAndAutomatic" + ], + "type": "string", + "x-ms-enum": { + "name": "PunctuationMode", + "modelAsString": false, + "values": [ + { + "value": "None", + "description": "No punctuation." + }, + { + "value": "Dictated", + "description": "Dictated punctuation marks only, i.e., explicit punctuation." + }, + { + "value": "Automatic", + "description": "Automatic punctuation." + }, + { + "value": "DictatedAndAutomatic", + "description": "Dictated punctuation marks or automatic punctuation." + } + ] + } + }, + "ResponseBlock": { + "title": "ResponseBlock", + "description": "ResponseBlock.", + "type": "object", + "properties": { + "name": { + "description": "The name of the block.", + "type": "string" + }, + "size": { + "format": "int32", + "description": "The size of the block.", + "type": "integer" + } + } + }, + "ServiceHealth": { + "title": "ServiceHealth", + "type": "object", + "properties": { + "status": { + "$ref": "#/definitions/HealthStatus" + }, + "message": { + "description": "Additional messages about the current service health.", + "type": "string", + "readOnly": true + }, + "components": { + "description": "Optional subcomponents of this service and their status.", + "type": "array", + "items": { + "$ref": "#/definitions/Component" + }, + "readOnly": true + } + } + }, + "SharedModel": { + "title": "SharedModel", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "properties": { + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "locale": { + "description": "The locale of the contained data.", + "minLength": 1, + "type": "string" + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + } + } + }, + "SharedModelFeatures": { + "title": "SharedModelFeatures", + "description": "Features supported by the model.", + "type": "object", + "properties": { + "supportsTranscriptions": { + "description": "A value indicating whether batch transcription is supported.", + "type": "boolean", + "readOnly": true + }, + "supportsEndpoints": { + "description": "A value indicating whether creation of endpoints for live transcription is supported.", + "type": "boolean", + "readOnly": true + }, + "supportsTranscriptionsOnSpeechContainers": { + "description": "A value indicating whether this model can be used for transcription on speech container. This feature can be added on existing models when it becomes usable on speech container.", + "type": "boolean", + "readOnly": true + } + } + }, + "Status": { + "title": "Status", + "description": "Describe the current state of the API", + "enum": [ + "NotStarted", + "Running", + "Succeeded", + "Failed" + ], + "type": "string", + "readOnly": true, + "x-ms-enum": { + "name": "Status", + "modelAsString": false, + "values": [ + { + "value": "NotStarted", + "description": "The long running operation has not yet started." + }, + { + "value": "Running", + "description": "The long running operation is currently processing." + }, + { + "value": "Succeeded", + "description": "The long running operation has successfully completed." + }, + { + "value": "Failed", + "description": "The long running operation has failed." + } + ] + } + }, + "Transcription": { + "title": "Transcription", + "required": [ + "displayName", + "locale" + ], + "type": "object", + "properties": { + "links": { + "$ref": "#/definitions/TranscriptionLinks" + }, + "properties": { + "$ref": "#/definitions/TranscriptionProperties" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "model": { + "$ref": "#/definitions/EntityReference" + }, + "project": { + "$ref": "#/definitions/EntityReference" + }, + "dataset": { + "$ref": "#/definitions/EntityReference" + }, + "contentUrls": { + "description": "A list of content urls to get audio files to transcribe. Up to 1000 urls are allowed.\r\nThis property will not be returned in a response.", + "type": "array", + "items": { + "format": "uri", + "type": "string" + } + }, + "contentContainerUrl": { + "format": "uri", + "description": "A URL for an Azure blob container that contains the audio files. A container is allowed to have a maximum size of 5GB and a maximum number of 10000 blobs.\r\nThe maximum size for a blob is 2.5GB.\r\nContainer SAS should contain 'r' (read) and 'l' (list) permissions.\r\nThis property will not be returned in a response.", + "type": "string" + }, + "locale": { + "description": "The locale of the contained data. If Language Identification is used, this locale is used to transcribe speech for which no language could be detected.", + "minLength": 1, + "type": "string" + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + } + } + }, + "TranscriptionLinks": { + "title": "TranscriptionLinks", + "type": "object", + "properties": { + "files": { + "format": "uri", + "description": "The location to get all files of this entity. See operation \"Transcriptions_ListFiles\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "TranscriptionProperties": { + "title": "TranscriptionProperties", + "type": "object", + "properties": { + "diarizationEnabled": { + "description": "A value indicating whether diarization (speaker identification) is requested. The default value\r\nis `false`.\r\nIf this field is set to true and the improved diarization system is configured by specifying\r\n`DiarizationProperties`, the improved diarization system will provide diarization for a configurable\r\nrange of speakers.\r\nIf this field is set to true and the improved diarization system is not enabled (not specifying\r\n`DiarizationProperties`), the basic diarization system will distinguish between up to two speakers.\r\nNo extra charges are applied for the basic diarization.\r\n \r\nThe basic diarization system is deprecated and will be removed in the next major version of the API.\r\nThis `diarizationEnabled` setting will also be removed.", + "type": "boolean" + }, + "wordLevelTimestampsEnabled": { + "description": "A value indicating whether word level timestamps are requested. The default value is\r\n`false`.", + "type": "boolean" + }, + "displayFormWordLevelTimestampsEnabled": { + "description": "A value indicating whether word level timestamps for the display form are requested. The default value is `false`.", + "type": "boolean" + }, + "duration": { + "description": "The duration of the transcription. The duration is encoded as ISO 8601 duration\r\n(\"PnYnMnDTnHnMnS\", see https://en.wikipedia.org/wiki/ISO_8601#Durations).", + "type": "string", + "readOnly": true + }, + "channels": { + "description": "A collection of the requested channel numbers.\r\nIn the default case, the channels 0 and 1 are considered.", + "type": "array", + "items": { + "format": "int32", + "type": "integer" + } + }, + "destinationContainerUrl": { + "format": "uri", + "description": "The requested destination container.\r\n### Remarks ###\r\nWhen a destination container is used in combination with a `timeToLive`, the metadata of a\r\ntranscription will be deleted normally, but the data stored in the destination container, including\r\ntranscription results, will remain untouched, because no delete permissions are required for this\r\ncontainer.
\r\nTo support automatic cleanup, either configure blob lifetimes on the container, or use \"Bring your own Storage (BYOS)\"\r\ninstead of `destinationContainerUrl`, where blobs can be cleaned up.", + "type": "string" + }, + "punctuationMode": { + "$ref": "#/definitions/PunctuationMode" + }, + "profanityFilterMode": { + "$ref": "#/definitions/ProfanityFilterMode" + }, + "timeToLive": { + "description": "How long the transcription will be kept in the system after it has completed. Once the\r\ntranscription reaches the time to live after completion (successful or failed) it will be automatically\r\ndeleted. Not setting this value or setting it to 0 will disable automatic deletion. The longest supported\r\nduration is 31 days.\r\nThe duration is encoded as ISO 8601 duration (\"PnYnMnDTnHnMnS\", see https://en.wikipedia.org/wiki/ISO_8601#Durations).", + "type": "string" + }, + "diarization": { + "$ref": "#/definitions/DiarizationProperties" + }, + "languageIdentification": { + "$ref": "#/definitions/LanguageIdentificationProperties" + }, + "email": { + "description": "The email address to send email notifications to in case the operation completes.\r\nThe value will be removed after successfully sending the email.", + "type": "string" + }, + "error": { + "$ref": "#/definitions/EntityError" + } + } + }, + "TranscriptionUpdate": { + "title": "TranscriptionUpdate", + "type": "object", + "properties": { + "project": { + "$ref": "#/definitions/EntityReference" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "UploadedBlocks": { + "title": "UploadedBlocks", + "description": "List of uploaded blocks.", + "type": "object", + "properties": { + "committedBlocks": { + "description": "The block description of blocks already committed.", + "type": "array", + "items": { + "$ref": "#/definitions/ResponseBlock" + } + }, + "uncommittedBlocks": { + "description": "The block description of blocks not committed to the blob.", + "type": "array", + "items": { + "$ref": "#/definitions/ResponseBlock" + } + } + } + }, + "WebHook": { + "title": "WebHook", + "required": [ + "displayName", + "events", + "webUrl" + ], + "type": "object", + "properties": { + "webUrl": { + "format": "uri", + "description": "The registered URL that will be used to send the POST requests for the registered events to.", + "type": "string" + }, + "links": { + "$ref": "#/definitions/WebHookLinks" + }, + "properties": { + "$ref": "#/definitions/WebHookProperties" + }, + "self": { + "format": "uri", + "description": "The location of this entity.", + "type": "string", + "readOnly": true + }, + "displayName": { + "description": "The display name of the object.", + "minLength": 1, + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "events": { + "$ref": "#/definitions/WebHookEvents" + }, + "createdDateTime": { + "format": "date-time", + "description": "The time-stamp when the object was created.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "lastActionDateTime": { + "format": "date-time", + "description": "The time-stamp when the current status was entered.\r\nThe time stamp is encoded as ISO 8601 date and time format\r\n(\"YYYY-MM-DDThh:mm:ssZ\", see https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations).", + "type": "string", + "readOnly": true + }, + "status": { + "$ref": "#/definitions/Status" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "WebHookEvents": { + "title": "WebHookEvents", + "type": "object", + "properties": { + "datasetCreation": { + "type": "boolean" + }, + "datasetProcessing": { + "type": "boolean" + }, + "datasetCompletion": { + "type": "boolean" + }, + "datasetDeletion": { + "type": "boolean" + }, + "modelCreation": { + "type": "boolean" + }, + "modelProcessing": { + "type": "boolean" + }, + "modelCompletion": { + "type": "boolean" + }, + "modelDeletion": { + "type": "boolean" + }, + "evaluationCreation": { + "type": "boolean" + }, + "evaluationProcessing": { + "type": "boolean" + }, + "evaluationCompletion": { + "type": "boolean" + }, + "evaluationDeletion": { + "type": "boolean" + }, + "transcriptionCreation": { + "type": "boolean" + }, + "transcriptionProcessing": { + "type": "boolean" + }, + "transcriptionCompletion": { + "type": "boolean" + }, + "transcriptionDeletion": { + "type": "boolean" + }, + "endpointCreation": { + "type": "boolean" + }, + "endpointProcessing": { + "type": "boolean" + }, + "endpointCompletion": { + "type": "boolean" + }, + "endpointDeletion": { + "type": "boolean" + }, + "ping": { + "type": "boolean" + }, + "challenge": { + "type": "boolean" + } + } + }, + "WebHookLinks": { + "title": "WebHookLinks", + "type": "object", + "properties": { + "ping": { + "format": "uri", + "description": "The URL that can be used to trigger the sending of a ping event to the registered URL of a web hook registration. See operation \"WebHooks_Ping\" for more details.", + "type": "string", + "readOnly": true + }, + "test": { + "format": "uri", + "description": "The URL that can be used sending test events to the registered URL of a web hook registration. See operation \"WebHooks_Test\" for more details.", + "type": "string", + "readOnly": true + } + } + }, + "WebHookProperties": { + "title": "WebHookProperties", + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/EntityError" + }, + "apiVersion": { + "description": "The API version the web hook was created in. This defines the shape of the payload in the callbacks.\r\nIf the payload type is not supported anymore, because the shape changed and the API version using it is removed (after deprecation),\r\nthe web hook will be disabled.", + "type": "string", + "readOnly": true + }, + "secret": { + "description": "A secret that will be used to create a SHA256 hash of the payload with the secret as HMAC key.\r\nThis hash will be set as X-MicrosoftSpeechServices-Signature header when calling back into the registered URL.", + "type": "string" + } + } + }, + "WebHookPropertiesUpdate": { + "title": "WebHookPropertiesUpdate", + "type": "object", + "properties": { + "secret": { + "description": "A secret that will be used to create a SHA256 hash of the payload with the secret as HMAC key.\r\nThis hash will be set as X-MicrosoftSpeechServices-Signature header when calling back into the registered URL.", + "type": "string" + } + } + }, + "WebHookUpdate": { + "title": "WebHookUpdate", + "type": "object", + "properties": { + "webUrl": { + "format": "uri", + "description": "The registered URL that will be used to send the POST requests for the registered events to.", + "type": "string" + }, + "properties": { + "$ref": "#/definitions/WebHookPropertiesUpdate" + }, + "events": { + "$ref": "#/definitions/WebHookEvents" + }, + "displayName": { + "description": "The name of the object.", + "type": "string" + }, + "description": { + "description": "The description of the object.", + "type": "string" + }, + "customProperties": { + "description": "The custom properties of this entity. The maximum allowed key length is 64 characters, the maximum\r\nallowed value length is 256 characters and the count of allowed entries is 10.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "parameters": { + "endpoint": { + "in": "path", + "name": "endpoint", + "description": "Supported Cognitive Services endpoints (protocol and hostname, for example: https://westus.api.cognitive.microsoft.com).", + "required": true, + "type": "string", + "x-ms-skip-url-encoding": true, + "x-ms-parameter-location": "client" + }, + "skipQueryParameter": { + "in": "query", + "name": "skip", + "description": "Number of datasets that will be skipped.", + "type": "integer", + "format": "int32", + "x-ms-parameter-location": "method" + }, + "topQueryParameter": { + "in": "query", + "name": "top", + "description": "Number of datasets that will be included after skipping.", + "type": "integer", + "format": "int32", + "x-ms-parameter-location": "method" + }, + "sasValidityQueryParameter": { + "in": "query", + "name": "sasValidityInSeconds", + "description": "The duration in seconds that an SAS url should be valid. The default duration is 12 hours. When using BYOS (https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-encryption-of-data-at-rest#bring-your-own-storage-byos-for-customization-and-logging): A value of 0 means that a plain blob URI without SAS token will be generated.", + "type": "integer", + "format": "int32", + "x-ms-parameter-location": "method" + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "Ocp-Apim-Subscription-Key", + "in": "header", + "description": "Provide your cognitive services account key here." + }, + "token": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "description": "Provide an access token from the JWT returned by the STS of this region. Make sure to add the management scope to the token by adding the following query string to the STS URL: ?scope=speechservicesmanagement" + } + }, + "security": [ + { + "api_key": [] + }, + { + "token": [] + } + ], + "schemes": [ + "https" + ], + "x-ms-parameterized-host": { + "hostTemplate": "{endpoint}/speechtotext/v3.1", + "useSchemePrefix": false, + "parameters": [ + { + "$ref": "#/parameters/endpoint" + } + ] + } + } \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/translator-api/translator-api-spec.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/translator-api/translator-api-spec.yaml new file mode 100644 index 000000000..9d8e67112 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/translator-api/translator-api-spec.yaml @@ -0,0 +1,879 @@ +openapi: 3.0.1 +info: + title: Translator API + description: "# Introduction\n\nThe Microsoft Translator Text API provides a JSON-based Web API. It provides:\n\n * Translation between any supported languages to any other supported language.\n * Translation to multiple languages in one request.\n * Transliteration to convert text from one script to another script of the same language.\n * Language detection, translation, and transliteration in one request.\n * Dictionary to lookup alternative translations of a term, to find back-translations and examples showing terms used in context.\n * Rich language detection.\n# Base URLs\n\nThe Translator Text API is available in the following clouds:\n\n| Description | Region | Base URL |\n| ------- | -------- | ------- |\n| Azure | Global | api.cognitive.microsofttranslator.com |\n| Azure | Europe | api-eur.cognitive.microsofttranslator.com |\n\n\n# Authentication\n\nSubscribe to the Translator Text API, part of Azure Cognitive Services, and use your subscription key from the Azure portal to authenticate. You can follow the steps in https://docs.microsoft.com/en-us/azure/cognitive-services/translator/translator-text-how-to-signup.\n\n\nThe simplest way is to pass your Azure secret key to the Translator service using the http request header `Ocp-Apim-Subscription-Key`.\n\nIf you prefer using a short-lived authentication, you may use your secret key to obtain an authorization token from the token service. In that case you pass the authorization token to the Translator service using the `Authorization` request header. To obtain an authorization token, make a `POST` request to the following URL:\n\n| Environment | Authentication service URL |\n| ---------- | ---------- |\n| Azure | `https://api.cognitive.microsoft.com/sts/v1.0/issueToken` |\n\nHere are example requests to obtain a token with a lifetime of 10 minutes, given a secret key:\n\n```python\n// Pass secret key using header\ncurl --header 'Ocp-Apim-Subscription-Key: ' --data \"\" 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken'\n// Pass secret key using query string parameter\ncurl --data \"\" 'https://api.cognitive.microsoft.com/sts/v1.0/issueToken?Subscription-Key='\n```\n\nA successful request returns the encoded access token as plain text in the response body. The valid token is passed to the Translator service as a bearer token in the Authorization. \n\n```\nAuthorization: Bearer \n```\n\nAn authentication token is valid for 10 minutes. The token should be re-used when making multiple calls to the Translator APIs. If you make requests to the Translator API over an extended period of time, you must request a new access token at regular intervals before the token expires, for instance every 9 minutes.\n\nTo summarize, a client request to the Translator API will include one authorization header taken from the following table:\n\n| Headers | Description |\n| ---------- | ---------- |\n| Ocp-Apim-Subscription-key | Use with Cognitive Services subscription if you are passing your secret key. The value is the Azure secret key for your subscription to Translator Text API. |\n| Authorization | Use with Cognitive Services subscription if you are passing an authentication token. The value is the Bearer token: `Bearer `. |\n\n## All-in-one subscription\nThe last authentication option is to use a Cognitive Service’s all-in-one subscription. This allows you to use a single secret key to authenticate requests for multiple services.\nWhen you use an all-in-one secret key, you must include two authentication headers with your request. The first passes the secret key, the second specifies the region associated with your subscription.\n`Ocp-Api-Subscription-Key` `Ocp-Apim-Subscription-Region`\nIf you pass the secret key in the query string with the parameter `Subscription-Key`, then you must specify the region with query parameter `Subscription-Region`.\nIf you use a bearer token, you must obtain the token from the region endpoint:\n`https://.api.cognitive.microsoft.com/sts/v1.0/issueToken`.\n\nAvailable regions are: `australiaeast`, `brazilsouth`, `canadacentral`, `centralindia`, `centraluseuap`, `eastasia`, `eastus`, `eastus2`, `japaneast`, `northeurope`, `southcentralus`, `southeastasia`, `uksouth`, `westcentralus`, `westeurope`, `westus`, and `westus2`.\n\nRegion is required for the all-in-one Text API subscription.\n\n\n# Errors\n\nA standard error response is a JSON object with name/value pair named `error`. The value is also a JSON object with properties:\n * `code`: A server-defined error code.\n * `message`: A string giving a human-readable representation of the error.\n\nFor example, a customer with a free trial subscription receives the following error once the free quota is exhausted:\n\n```json\n{\n \"error\": {\n \"code\":403000,\n \"message\":\"The subscription has exceeded its free quota.\"\n }\n}\n```\n# Enter your subscription keys to try out Microsoft Translator.\nSelect the `Authorize` button and enter your Microsoft Translator subscription key, OR your `all in one Cognitive Services` subscription key. If you are using the all in one Cognitive Services key you will need to also enter your subscription region.\n## Available regions are:\n\n `australiaeast`, `brazilsouth`, `canadacentral`, `centralindia`, `centraluseuap`, `eastasia`, `eastus`, `eastus2`, `japaneast`, `northeurope`, `southcentralus`, `southeastasia`, `uksouth`, `westcentralus`, `westeurope`, `westus`, `westus2`.\n" + termsOfService: http://www.microsoftvolumelicensing.com/DocumentSearch.aspx?Mode=3&DocumentTypeId=31 + contact: + name: Microsoft Translator team + url: https://www.microsoft.com/translator + email: translator@microsoft.com + version: '1.0' +servers: + - url: https://TOBEREPLACED/translator/text/v3.0 +paths: + /BreakSentence: + post: + tags: + - BreakSentence + summary: Translator_BreakSentence + description: "Identifies the position of sentence boundaries in a piece of text.\n" + operationId: Translator_BreakSentence + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: Language + in: query + description: 'Language tag of the language of the input text. If not specified, Translator will apply automatic language detection.' + schema: + type: string + - name: Script + in: query + description: 'Script identifier of the script used by the input text. If a script is not specified, the default script of the language will be assumed.' + schema: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request Body\nThe body of the request is a JSON array. Each array element is a JSON object with a string property named Text. Sentence boundaries are computed for the value of the Text property. \n\nThe following limitations apply:\n* The array can have at most 100 elements.\n* The text value of an array element cannot exceed 10,000 characters including spaces.\n* The entire text included in the request cannot exceed 50,000 characters including spaces.\n* If the `language` query parameter is specified, then all array elements must be in the same language. Otherwise, language auto-detection is applied to each array element independently.\n\n# Response Body\nA successful response is a JSON array with one result for each string in the input array. A result object includes the following properties:\n* `sentLen`- An array of integers representing the lengths of the sentences in the text element. The length of the array is the number of sentences, and the values are the length of each sentence.\n* `detectedLanguage`- An object describing the detected language through the following properties\n * `language`- Code of the detected language.\n * `score`- A float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.\n* Note that the `detectedLanguage` property is only present in the result object when language auto-detection is requested.\n# Response Header\nX-RequestId - Value generated by the service to identify the request. It is used for troubleshooting purposes.\n" + content: + application/json: + schema: + $ref: '#/components/schemas/BreakSentenceTextInputArray' + example: + - text: Today is a good day. + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/BreakSentenceResult' + example: + - sentLen: + - 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /Detect: + post: + tags: + - Detect + summary: Translator_Detect + description: "Identifies the language of a string of text.\n" + operationId: Translator_Detect + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request Body\nThe body of the request is a JSON array. Each array element is a JSON object with a string property named Text. Language detection is applied to the value of the Text property.\nThe following limitations apply:\n* The array can have at most 100 elements.\n* The text value of an array element cannot exceed 10,000 characters including spaces.\n* The entire text included in the request cannot exceed 50,000 characters including spaces.\n\n# Response Body\nA successful response is a JSON array with one result for each string in the input array. A result object includes the following properties:\n* language- Code of the detected language.\n* score- A float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.\n* isTranslationSupported- A boolean value which is true if the detected language is one of the languages supported for text translation. Not all detected languages can be translated by the API.\n* isTransliterationSupported- A boolean value which is true if the detected language is one of the languages supported for transliteration.\n* alternatives- An array of other possible languages. Each element of the array is another object with the same properties listed above- language, score, isTranslationSupported and isTransliterationSupported.\n# Response Header\nX-RequestId - Value generated by the service to identify the request. It is used for troubleshooting purposes.\n" + content: + application/json: + schema: + $ref: '#/components/schemas/DetectTextInputArray' + example: + - text: 'What language is this? I am not sure, nein' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/DetectResult' + example: + - text: string + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /Dictionary/Lookup: + post: + tags: + - Dictionary/Lookup + summary: Translator_DictionaryLookup + description: "Provides alternative translations for a word and a small number of idiomatic phrases. Each translation has a `part-of-speech` and a list of `back-translations`. The back-translations enable a user to understand the translation in context. The Dictionary Example operation allows further drill down to see example uses of each translation pair.\n" + operationId: Translator_DictionaryLookup + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: from + in: query + description: Specifies the language of the input text. The source language must be one of the supported languages included in the `dictionary` scope. + required: true + schema: + type: string + - name: to + in: query + description: Specifies the language of the output text. The target language must be one of the supported languages included in the `dictionary` scope of the Languages resource. + required: true + schema: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request Body\nThe body of the request is a JSON array. Each array element is a JSON object with a string property named `Text`, which represents the term to lookup.\nThe following limitations apply:\n* The array can have at most 10 elements.\n* The text value of an array element cannot exceed 100 characters including spaces.\n\n# Response Body\nA successful response is a JSON array with one result for each string in the input array. A result object includes the following properties:\n* `normalizedSource`- A string giving the normalized form of the source term. For example, if the request is \"JOHN\", the normalized form will be \"john\". The content of this field becomes the input to lookup examples.\n* `displaySource`- A string giving the source term in a form best suited for end-user display. For example, if the input is \"JOHN\", the display form will reflect the usual spelling of the name- \"John\".\n* `translations`- A list of translations for the source term. Each element of the list is an object with the following properties:\n * `normalizedTarget`- A string giving the normalized form of this term in the target language. This value should be used as input to lookup examples.\n * `displayTarget`- A string giving the term in the target language and in a form best suited for end-user display. Generally, this will only differ from the `normalizedTarget` in terms of capitalization. For example, a proper noun like \"Juan\" will have `normalizedTarget = \"juan\"` and `displayTarget = \"Juan\"`.\n * `posTag`- A string associating this term with a part-of-speech tag.\n \n | Tag name | Description |\n | --------- | ---------------- |\n | ADJ | Adjectives |\n | ADV | Adverbs |\n | CONJ | Conjunctions |\n | DET | Determiners |\n | MODAL | Verbs |\n | NOUN | Nouns |\n | PREP | Prepositions |\n | PRON | Pronouns |\n | VERB | Verbs |\n | OTHER | Other |\n \n As an implementation note, these tags were determined by part-of-speech tagging the English side, and then taking the most frequent tag for each source/target pair. So if people frequently translate a Spanish word to a different part-of-speech tag in English, tags may end up being wrong (with respect to the Spanish word).\n * `confidence`- A value between 0.0 and 1.0 which represents the \"confidence\" (or perhaps more accurately, \"probability in the training data\") of that translation pair. The sum of confidence scores for one source word may or may not sum to 1.0.\n * `prefixWord-` A string giving the word to display as a prefix of the translation. Currently, this is the gendered determiner of nouns, in languages that have gendered determiners. For example, the prefix of the Spanish word \"mosca\" is \"la\", since \"mosca\" is a feminine noun in Spanish. This is only dependent on the translation, and not on the source. If there is no prefix, it will be the empty string.\n * `backTranslations-` A list of \"back translations\" of the target. For example, source words that the target can translate to. The list is guaranteed to contain the source word that was requested (e.g., if the source word being looked up is \"fly\", then it is guaranteed that \"fly\" will be in the `backTranslations` list). However, it is not guaranteed to be in the first position, and often will not be. Each element of the `backTranslations` list is an object described by the following properties-\n * `normalizedText-` A string giving the normalized form of the source term that is a back-translation of the target. This value should be used as input to lookup examples.\n * `displayText-` A string giving the source term that is a back-translation of the target in a form best suited for end-user display.\n * `numExamples-` An integer representing the number of examples that are available for this translation pair. Actual examples must be retrieved with a separate call to lookup examples. The number is mostly intended to facilitate display in a UX. For example, a user interface may add a hyperlink to the back-translation if the number of examples is greater than zero and show the back-translation as plain text if there are no examples. Note that the actual number of examples returned by a call to lookup examples may be less than `numExamples`, because additional filtering may be applied on the fly to remove \"bad\" examples.\n * `frequencyCount-` An integer representing the frequency of this translation pair in the data. The main purpose of this field is to provide a user interface with a means to sort back-translations so the most frequent terms are first.\n \nNote - If the term being looked up does not exist in the dictionary, the response is 200 (OK) but the `translations` list is an empty list.\n \n# Response Header\nX-RequestId - Value generated by the service to identify the request. It is used for troubleshooting purposes.\n" + content: + application/json: + schema: + $ref: '#/components/schemas/DictionaryLookupTextInputArray' + example: + - text: Bug + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/DictionaryLookupResult' + example: + - normalizedSource: string + displaySource: string + translations: + - normalizedTarget: string + displayTarget: string + posTag: string + confidence: 0 + prefixWord: string + backTranslations: + - normalizedText: string + displayText: string + numExamples: 0 + frequencyCount: 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /Dictionary/Examples: + post: + tags: + - DictionaryExamples + summary: Translator_DictionaryExamples + description: "Provides examples that show how terms in the dictionary are used in context. This operation is used in tandem with `Dictionary lookup`.\n" + operationId: Translator_DictionaryExamples + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: from + in: query + description: Specifies the language of the input text. The source language must be one of the supported languages included in the `dictionary` scope. + required: true + schema: + type: string + - name: to + in: query + description: Specifies the language of the output text. The target language must be one of the supported languages included in the `dictionary` scope. + required: true + schema: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request body\nThe body of the request is a JSON array. Each array element is a JSON object with the following properties:\n* `Text-` A string specifying the term to lookup. This should be the value of a `normalizedText` field from the back-translations of a previous Dictionary lookup request. It can also be the value of the `normalizedSource` field.\n* `Translation-` A string specifying the translated text previously returned by the Dictionary lookup operation. This should be the value from the `normalizedTarget` field in the `translations` list of the Dictionary lookup response. The service will return examples for the specific source-target word-pair.\n\nThe following limitations apply:\n* The array can have at most 10 elements.\n* The text value of an array element cannot exceed 100 characters including spaces.\n\n# Response body\nA successful response is a JSON array with one result for each string in the input array. A result object includes the following properties:\n* `normalizedSource-` A string giving the normalized form of the source term. Generally, this should be identical to the value of the `Text` field at the matching list index in the body of the request.\n* `normalizedTarget-` A string giving the normalized form of the target term. Generally, this should be identical to the value of the `Translation` field at the matching list index in the body of the request.\n* `examples-` A list of examples for the (source term, target term) pair. Each element of the list is an object with the following properties:\n * `sourcePrefix-` The string to concatenate before the value of `sourceTerm` to form a complete example. Do not add a space character, since it is already there when it should be. This value may be an empty string.\n * `sourceTerm-` A string equal to the actual term looked up. The string is added with `sourcePrefix` and `sourceSuffix` to form the complete example. Its value is separated so it can be marked in a user interface, e.g., by bolding it.\n * `sourceSuffix-` The string to concatenate after the value of `sourceTerm` to form a complete example. Do not add a space character, since it is already there when it should be. This value may be an empty string.\n * `targetPrefix-` A string similar to `sourcePrefix` but for the target.\n * `targetTerm-` A string similar to `sourceTerm` but for the target.\n * `targetSuffix-` A string similar to `sourceSuffix` but for the target.\n\n# Response Header\nX-RequestId - Value generated by the service to identify the request. It is used for troubleshooting purposes.\nNOTE - If there are no examples in the dictionary, the response is 200 (OK) but the `examples` list is an empty list.\n" + content: + application/json: + schema: + $ref: '#/components/schemas/DictionaryExampleTextInputArray' + example: + - text: bug + translation: bicho + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/DictionaryExampleResult' + example: + - normalizedSource: string + normalizedTarget: string + examples: + - sourcePrefix: string + sourceTerm: string + sourceSuffix: string + targetPrefix: string + targetTerm: string + targetSuffix: string + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /Languages: + get: + tags: + - Languages + summary: Translator_Languages + description: "Gets the set of languages currently supported by other operations of the Translator Text API.\n**Authentication is not required to get language resources.**\n\n# Response Body\nA client uses the `scope` query parameter to define which groups of languages it is interested in.\n* `scope=translation` provides languages supported to translate text from one language to another language.\n* `scope=transliteration` provides capabilities for converting text in one language from one script to another script.\n* `scope=dictionary` provides language pairs for which `Dictionary` operations return data.\n\nA client may retrieve several groups simultaneously by specifying a comma-separated list of names. For example, `scope=translation,transliteration,dictionary` would return supported languages for all groups.\n\nA successful response is a JSON object with one property for each requested group.\nThe value for each property is as follows.\n\n* `translation` property\n The value of the `translation` property is a dictionary of (key, value) pairs. Each key is a BCP 47 language tag. A key identifies a language for which text can be translated to or translated from. The value associated with the key is a JSON object with properties that describe the language\n * `name-` Display name of the language in the locale requested via `Accept-Language` header.\n * `nativeName-` Display name of the language in the locale native for this language.\n * `dir-` Directionality, which is `rtl` for right-to-left languages or `ltr` for left-to-right languages.\n```json\n{\n \"translation\": {\n ...\n \"fr\": {\n \"name\": \"French\",\n \"nativeName\": \"Français\",\n \"dir\": \"ltr\"\n },\n...\n }\n}\n```\n* `transliteration` property\n The value of the `transliteration` property is a dictionary of (key, value) pairs. Each key is a BCP 47 language tag. A key identifies a language for which text can be converted from one script to another script. The value associated with the key is a JSON object with properties that describe the language and its supported scripts\n * `name-` Display name of the language in the locale requested via `Accept-Language` header.\n * `nativeName-` Display name of the language in the locale native for this language.\n * `scripts-` List of scripts to convert from. Each element of the `scripts` list has properties-\n * `code-` Code identifying the script.\n * `name-` Display name of the script in the locale requested via `Accept-Language` header.\n * `nativeName-` Display name of the language in the locale native for the language.\n * `dir-` Directionality, which is `rtl` for right-to-left languages or `ltr` for left-to-right languages.\n * `toScripts-` List of scripts available to convert text to. Each element of the `toScripts` list has properties `code`, `name`, `nativeName`, and `dir` as described earlier.\n\n```json\n{\n \"transliteration\": {\n ...\n \"ja\": {\n \"name\": \"Japanese\",\n \"nativeName\": \"日本語\",\n \"scripts\": [\n {\n \"code\": \"Jpan\",\n \"name\": \"Japanese\",\n \"nativeName\": \"日本語\",\n \"dir\": \"ltr\",\n \"toScripts\": [\n {\n \"code\": \"Latn\",\n \"name\": \"Latin\",\n \"nativeName\": \"ラテン語\",\n \"dir\": \"ltr\"\n }\n ]\n },\n {\n \"code\": \"Latn\",\n \"name\": \"Latin\",\n \"nativeName\": \"ラテン語\",\n \"dir\": \"ltr\",\n \"toScripts\": [\n {\n \"code\": \"Jpan\",\n \"name\": \"Japanese\",\n \"nativeName\": \"日本語\",\n \"dir\": \"ltr\"\n }\n ]\n }\n ]\n },\n ...\n }\n}\n\n```\n* `dictionary` property\nThe value of the `dictionary` property is a dictionary of (key, value) pairs. Each key is a BCP 47 language tag. The key identifies a language for which alternative translations and back-translations are available. The value is a JSON object that describes the source language and the target languages with available translations.\n * `name-` Display name of the source language in the locale requested via `Accept-Language` header.\n * `nativeName-` Display name of the language in the locale native for this language.\n * `dir-` Directionality, which is `rtl` for right-to-left languages or `ltr` for left-to-right languages.\n * `translations-` List of languages with alterative translations and examples for the query expressed in the source language. Each element of the `translations` list has properties\n * `name-` Display name of the target language in the locale requested via `Accept-Language` header.\n * `nativeName-` Display name of the target language in the locale native for the target language.\n * `dir-` Directionality, which is `rtl` for right-to-left languages or `ltr` for left-to-right languages.\n * `code-` Language code identifying the target language.\n\n```json\n\n\"es\": {\n \"name\": \"Spanish\",\n \"nativeName\": \"Español\",\n \"dir\": \"ltr\",\n \"translations\": [\n {\n \"name\": \"English\",\n \"nativeName\": \"English\",\n \"dir\": \"ltr\",\n \"code\": \"en\"\n }\n ]\n},\n\n```\n\nThe structure of the response object will not change without a change in the version of the API. For the same version of the API, the list of available languages may change over time because Microsoft Translator continually extends the list of languages supported by its services.\n\nThe list of supported languages will not change frequently. To save network bandwidth and improve responsiveness, a client application should consider caching language resources and the corresponding entity tag (`ETag`). Then, the client application can periodically (for example, once every 24 hours) query the service to fetch the latest set of supported languages. Passing the current `ETag` value in an `If-None-Match` header field will allow the service to optimize the response. If the resource has not been modified, the service will return status code 304 and an empty response body.\n \n# Response Header\nETag - Current value of the entity tag for the requested groups of supported languages. To make subsequent requests more efficient, the client may send the `ETag` value in an `If-None-Match` header field.\n\nX-RequestId - Value generated by the service to identify the request. It is used for troubleshooting purposes. \n \n \n" + operationId: Translator_Languages + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: scope + in: query + description: 'A comma-separated list of names defining the group of languages to return. Allowed group names are- `translation`, `transliteration` and `dictionary`. If no scope is given, then all groups are returned, which is equivalent to passing `scope=translation,transliteration,dictionary`. To decide which set of supported languages is appropriate for your scenario, see the description of the response object.' + schema: + maxItems: 3 + minItems: 0 + type: array + items: + enum: + - translation + - transliteration + - dictionary + type: string + - name: Accept-Language + in: header + description: 'The language to use for user interface strings. Some of the fields in the response are names of languages or names of regions. Use this parameter to define the language in which these names are returned. The language is specified by providing a well-formed BCP 47 language tag. For instance, use the value `fr` to request names in French or use the value `zh-Hant` to request names in Chinese Traditional. Names are provided in the English language when a target language is not specified or when localization is not available.' + schema: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/LanguagesResult' + example: + translation: + languageCode: + name: string + nativeName: string + dir: string + transliteration: + languageCode: + name: string + nativeName: string + scripts: + - code: string + name: string + nativeName: string + dir: string + toScripts: + - code: string + name: string + nativeName: string + dir: string + dictionary: + languageCode: + name: string + nativeName: string + dir: string + translations: + - name: string + nativeName: string + dir: string + code: string + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /translate: + post: + tags: + - Translate + summary: Translator_Translate + description: "Translates text into one or more languages.\n" + operationId: Translator_Translate + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: to + in: query + description: "Specifies the language of the output text. Find which languages are available to translate to by using the languages method. For example, use `to=de` to translate to German.\n It's possible to translate to multiple languages simultaneously by repeating the `to` parameter in the query string. For example, use `to=de&to=it` to translate to German and Italian in the same request.\n" + required: true + schema: + type: array + items: + type: string + - name: from + in: query + description: "Specifies the language of the input text. Find which languages are available to translate from by using the languages method. If the `from` parameter is not specified, automatic language detection is applied to determine the source language.\n" + schema: + type: string + - name: textType + in: query + description: "Defines whether the text being translated is plain text or HTML text. Any HTML needs to be a well-formed, complete HTML element. Possible values are `plain` (default) or `html`\n" + schema: + enum: + - plain + - html + type: string + - name: category + in: query + description: "A string specifying the category (domain) of the translation. This parameter retrieves translations from a customized system built with Custom Translator. Default value is `general`.\n" + schema: + type: string + - name: profanityAction + in: query + description: "Specifies how profanities should be treated in translations. Possible values are: `NoAction` (default), `Marked` or `Deleted`.\n### Handling Profanity\nNormally the Translator service will retain profanity that is present in the source in the translation. The degree of profanity and the context that makes words profane differ between cultures, and as a result the degree of profanity in the target language may be amplified or reduced.\n\nIf you want to avoid getting profanity in the translation, regardless of the presence of profanity in the source text, you can use the profanity filtering option. The option allows you to choose whether you want to see profanity deleted, whether you want to mark profanities with appropriate tags (giving you the option to add your own post-processing), or you want no action taken. The accepted values of `ProfanityAction` are `Deleted`, `Marked` and `NoAction` (default).\n\n| ProfanityAction | Action |\n| ---------- | ---------- |\n| `NoAction` | This is the default behavior. Profanity will pass from source to target. |\n| | Example Source (Japanese)- 彼はジャッカスです。 |\n| | Example Translation (English)- He is a jackass. |\n| | |\n| `Deleted` | Profane words will be removed from the output without replacement. |\n| | Example Source (Japanese)- 彼はジャッカスです。 |\n| | Example Translation (English)- He is a. |\n| `Marked` | Profane words are replaced by a marker in the output. The marker depends on the `ProfanityMarker` parameter.\n| | For `ProfanityMarker=Asterisk`, profane words are replaced with `***` |\n| | Example Source (Japanese)- 彼はジャッカスです。 |\n| | Example Translation (English)- He is a ***. |\n| | For `ProfanityMarker=Tag`, profane words are surrounded by XML tags and \n| | Example Source (Japanese)- 彼はジャッカスです。 |\n| | Example Translation (English)- He is a jackass.\n" + schema: + enum: + - NoAction + - Marked + - Deleted + type: string + - name: profanityMarker + in: query + description: "Specifies how profanities should be marked in translations. Possible values are- `Asterisk` (default) or `Tag`.\n" + schema: + type: string + - name: includeAlignment + in: query + description: "Specifies whether to include alignment projection from source text to translated text. Possible values are- `true` or `false` (default).\n" + schema: + type: boolean + - name: includeSentenceLength + in: query + description: "Specifies whether to include sentence boundaries for the input text and the translated text. Possible values are- `true` or `false` (default).\n" + schema: + type: boolean + - name: suggestedFrom + in: query + description: "Specifies a fallback language if the language of the input text can't be identified. Language auto-detection is applied when the `from` parameter is omitted. If detection fails, the `suggestedFrom` language will be assumed.\n" + schema: + type: string + - name: fromScript + in: query + description: Specifies the script of the input text. Supported scripts are available from the languages method + schema: + type: string + - name: toScript + in: query + description: Specifies the script of the translated text. Supported scripts are available from the languages method + schema: + type: array + items: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request body\nThe body of the request is a JSON array. Each array element is a JSON object with a string property named `Text`, which represents the string to translate.\nThe following limitations apply:\n* The array can have at most 25 elements.\n* The entire text included in the request cannot exceed 5,000 characters including spaces.\n# Response body\nA successful response is a JSON array with one result for each string in the input array. A result object includes the following properties-\n* `detectedLanguage`- An object describing the detected language through the following properties.\n * `language`- A string representing the code of the detected language.\n * `score`- A float value indicating the confidence in the result. The score is between zero and one and a low score indicates a low confidence.\n The `detectedLanguage` property is only present in the result object when language auto-detection is requested.\n* `translations`- An array of translation results. The size of the array matches the number of target languages specified in the `to` query parameter. Each element in the array includes.\n * `to` A string representing the language code of the target language.\n * `text`- A string giving the translated text.\n * `transliteration`- An object giving the translated text in the script specified by the `toScript` parameter.\n * `script`- A string specifying the target script.\n * `text`- A string giving the translated text in the target script.\n The `transliteration` object is not included if transliteration does not take place.\n *`alignment`- An object with a single string property named `proj`, which maps input text to translated text. The alignment information is only provided when the request parameter `includeAlignment` is `true`. Alignment is returned as a string value of the following format- `[[SourceTextStartIndex]-[SourceTextEndIndex]–[TgtTextStartIndex]-[TgtTextEndIndex]]`. The colon separates start and end index, the dash separates the languages, and space separates the words. One word may align with zero, one, or multiple words in the other language, and the aligned words may be non-contiguous. When no alignment information is available, the alignment element will be empty. See Obtain alignment information for an example and restrictions.\n * `sentLen`- An object returning sentence boundaries in the input and output texts.\n * `srcSentLen`- An integer array representing the lengths of the sentences in the input text. The length of the array is the number of sentences, and the values are the length of each sentence.\n * `transSentLen`- An integer array representing the lengths of the sentences in the translated text. The length of the array is the number of sentences, and the values are the length of each sentence.\n Sentence boundaries are only included when the request parameter `includeSentenceLength` is `true`.\n * `sourceText`- An object with a single string property named `text`, which gives the input text in the default script of the source language. `sourceText` property is present only when the input is expressed in a script that's not the usual script for the language. For example, if the input were Arabic written in Latin script, then `sourceText.text` would be the same Arabic text converted into Arab script.\n Example of JSON responses are provided in the examples section.\n \n \n" + content: + application/json: + schema: + $ref: '#/components/schemas/TranslateTextInputArray' + example: + - text: I would really like to drive your car. + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/TranslateResultAll' + example: + - detectedLanguage: + language: string + score: 0 + translations: + - text: string + transliteration: + text: string + script: string + to: string + alignment: + proj: string + sentLen: + srcSentLen: + - integer: 0 + transSentLen: + - integer: 0 + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string + /transliterate: + post: + tags: + - Transliterate + summary: Translator_Transliterate + description: "Converts the text of a language in one script into another type of script. Example- \nJapanese script \"こんにちは\"\nSame word in Latin script \"konnichiha\"\n" + operationId: Translator_Transliterate + parameters: + - name: api-version + in: query + description: Version of the API requested by the client. Value must be **3.0**. + required: true + schema: + type: string + default: '3.0' + - name: language + in: query + description: "Specifies the language of the text to convert from one script to another. Possible languages are listed in the `transliteration` scope obtained by querying the service for its supported languages.\n" + required: true + schema: + type: string + - name: fromScript + in: query + description: "Specifies the script used by the input text. Lookup supported languages using the `transliteration` scope, to find input scripts available for the selected language.\n" + required: true + schema: + type: string + - name: toScript + in: query + description: "Specifies the output script. Lookup supported languages using the `transliteration` scope, to find output scripts available for the selected combination of input language and input script.\n" + required: true + schema: + type: string + - name: X-ClientTraceId + in: header + description: A client-generated GUID to uniquely identify the request. Note that you can omit this header if you include the trace ID in the query string using a query parameter named ClientTraceId. + schema: + type: string + requestBody: + description: "# Request body\n The body of the request is a JSON array. Each array element is a JSON object with a string property named `Text`, which represents the string to convert.\n The following limitations apply:\n * The array can have at most 10 elements.\n * The text value of an array element cannot exceed 1,000 characters including spaces.\n * The entire text included in the request cannot exceed 5,000 characters including spaces.\n \n# Response body\n A successful response is a JSON array with one result for each element in the input array. A result object includes the following properties:\n * `text`- A string which is the result of converting the input string to the output script.\n * `script`- A string specifying the script used in the output.\n \n" + content: + application/json: + schema: + $ref: '#/components/schemas/TransliterateTextInputArray' + example: + - text: 'Good morning, how are you today?' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/TransliterateResult' + example: + - text: this is cool + script: this is very cool + '500': + description: Error response. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + example: + error: + code: string + message: string +components: + schemas: + LanguagesResult: + type: object + properties: + translation: + type: object + properties: + languageCode: + type: object + properties: + name: + type: string + nativeName: + type: string + dir: + type: string + transliteration: + type: object + properties: + languageCode: + type: object + properties: + name: + type: string + nativeName: + type: string + scripts: + type: array + items: + type: object + properties: + code: + type: string + name: + type: string + nativeName: + type: string + dir: + type: string + toScripts: + type: array + items: + type: object + properties: + code: + type: string + name: + type: string + nativeName: + type: string + dir: + type: string + dictionary: + type: object + properties: + languageCode: + type: object + properties: + name: + type: string + nativeName: + type: string + dir: + type: string + translations: + type: array + items: + type: object + properties: + name: + type: string + nativeName: + type: string + dir: + type: string + code: + type: string + description: Example of a successful languages request + DictionaryExampleResult: + type: array + items: + type: object + properties: + normalizedSource: + type: string + normalizedTarget: + type: string + examples: + type: array + items: + type: object + properties: + sourcePrefix: + type: string + sourceTerm: + type: string + sourceSuffix: + type: string + targetPrefix: + type: string + targetTerm: + type: string + targetSuffix: + type: string + description: Example of a successful dictionary example request + DictionaryLookupResult: + type: array + items: + type: object + properties: + normalizedSource: + type: string + displaySource: + type: string + translations: + type: array + items: + type: object + properties: + normalizedTarget: + type: string + displayTarget: + type: string + posTag: + type: string + confidence: + type: number + prefixWord: + type: string + backTranslations: + type: array + items: + type: object + properties: + normalizedText: + type: string + displayText: + type: string + numExamples: + type: integer + frequencyCount: + type: integer + description: Example of a successful dictionary lookup request + TranslateResult: + type: array + items: + type: object + properties: + translation: + type: array + items: + type: object + properties: + text: + type: string + to: + type: string + description: Example of a successful translate request + TranslateResultAll: + type: array + items: + type: object + properties: + detectedLanguage: + type: object + properties: + language: + type: string + score: + type: integer + translations: + type: array + items: + type: object + properties: + text: + type: string + transliteration: + type: object + properties: + text: + type: string + script: + type: string + to: + type: string + alignment: + type: object + properties: + proj: + type: string + sentLen: + type: object + properties: + srcSentLen: + type: array + items: + properties: + integer: + type: integer + transSentLen: + type: array + items: + properties: + integer: + type: integer + description: 'Example of a successful translate request, including all optional parameters.' + BreakSentenceResult: + type: array + items: + type: object + properties: + sentLen: + type: array + items: + type: integer + description: Example of a successful break sentence request + TransliterateResult: + type: array + items: + type: object + properties: + text: + type: string + script: + type: string + example: + text: this is cool + script: this is very cool + description: Example of a successful transliterate request + DetectResult: + type: array + items: + type: object + properties: + text: + type: string + description: Example of a successful detect request + BreakSentenceTextInput: + type: object + properties: + text: + maximum: 10000 + type: string + description: Text needed for break sentence request + example: + text: Today is a good day. + ErrorMessage: + type: object + properties: + error: + type: object + properties: + code: + type: string + message: + type: string + DetectTextInput: + type: object + properties: + text: + maximum: 10000 + type: string + description: 'Text needed for detect request ' + example: + text: 'What language is this? I am not sure, nein' + DictionaryLookupTextInput: + type: object + properties: + text: + maximum: 100 + type: string + description: 'Text needed for a dictionary lookup request ' + example: + text: Bug + DictionaryExampleTextInput: + type: object + properties: + text: + maximum: 100 + type: string + translation: + type: string + description: 'Text needed for a dictionary example request ' + example: + text: bug + translation: bicho + TranslateTextInput: + type: object + properties: + text: + maximum: 5000 + type: string + description: 'Text needed for a translate request ' + example: + text: I would really like to drive your car. + TransliterateTextInput: + type: object + properties: + text: + maximum: 5000 + type: string + description: 'Text needed for a transliterate request ' + example: + text: 'Good morning, how are you today?' + BreakSentenceTextInputArray: + type: array + items: + $ref: '#/components/schemas/BreakSentenceTextInput' + DetectTextInputArray: + type: array + items: + $ref: '#/components/schemas/DetectTextInput' + DictionaryLookupTextInputArray: + type: array + items: + $ref: '#/components/schemas/DictionaryLookupTextInput' + DictionaryExampleTextInputArray: + type: array + items: + $ref: '#/components/schemas/DictionaryExampleTextInput' + TranslateTextInputArray: + type: array + items: + $ref: '#/components/schemas/TranslateTextInput' + TransliterateTextInputArray: + type: array + items: + $ref: '#/components/schemas/TransliterateTextInput' + securitySchemes: + apiKeyHeader: + type: apiKey + name: Ocp-Apim-Subscription-Key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api.bicep new file mode 100644 index 000000000..a010f16ae --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api.bicep @@ -0,0 +1,214 @@ +/** + * @module unified-ai-api + * @description Creates the Unified AI Wildcard API in APIM with deployment listing operations. + * + * This module registers a wildcard API (/*) that routes all AI requests through + * dynamic path-based routing using policy fragments. It includes: + * - Wildcard catch-all operations for all HTTP methods + * - /deployments and /deployments/{deployment-id} GET operations for model discovery + * - Operation-level policies for deployment listing + * - API-level policy for request routing orchestration + */ + +// ------------------ +// PARAMETERS +// ------------------ + +@description('Name of the API Management service') +param apiManagementName string + +@description('Id of the APIM Logger for Azure Monitor diagnostics') +param apimLoggerId string = '' + +@description('Whether to enable the Unified AI API (feature flag)') +param enabled bool = true + +@description('Azure Monitor diagnostic log settings') +param azureMonitorLogSettings object = { + frontend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + backend: { + request: { headers: [], body: { bytes: 0 } } + response: { headers: [], body: { bytes: 0 } } + } + largeLanguageModel: { + logs: 'enabled' + requests: { messages: 'all', maxSizeInBytes: 262144 } + responses: { messages: 'all', maxSizeInBytes: 262144 } + } +} + +// ------------------ +// RESOURCES +// ------------------ + +resource apimService 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apiManagementName +} + +// Unified AI Wildcard API +resource unifiedAiApi 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = if (enabled) { + name: 'unified-ai-api' + parent: apimService + properties: { + apiType: 'http' + description: 'Unified AI Gateway API - Routes requests to multiple AI model providers (Azure OpenAI, AI Foundry, Gemini) using dynamic path-based routing with support for multiple API types.' + displayName: 'Unified AI API' + format: 'openapi+json' + path: 'unified-ai' + protocols: [ + 'https' + ] + subscriptionKeyParameterNames: { + header: 'api-key' + query: 'api-key' + } + subscriptionRequired: true + type: 'http' + value: string(loadJsonContent('./unified-ai-api/UnifiedAIWildcard.json')) + } +} + +// API-level policy (orchestration policy with all fragments) +resource unifiedAiApiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-06-01-preview' = if (enabled) { + name: 'policy' + parent: unifiedAiApi + properties: { + format: 'rawxml' + value: loadTextContent('./policies/unified-ai-api-policy.xml') + } +} + +// APIM Product for Unified AI API (subscription-based, API Key auth) +resource unifiedAiProduct 'Microsoft.ApiManagement/service/products@2024-06-01-preview' = if (enabled) { + name: 'unified-ai-product' + parent: apimService + properties: { + displayName: 'Unified AI Gateway' + description: 'Unified AI Gateway product - provides access to all AI model providers through a single wildcard endpoint.' + subscriptionRequired: true + approvalRequired: false + subscriptionsLimit: 10 + state: 'published' + } +} + +// Associate API with product +resource unifiedAiProductApi 'Microsoft.ApiManagement/service/products/apis@2024-06-01-preview' = if (enabled) { + name: 'unified-ai-api' + parent: unifiedAiProduct +} + +// Product policy +resource unifiedAiProductPolicy 'Microsoft.ApiManagement/service/products/policies@2024-06-01-preview' = if (enabled) { + name: 'policy' + parent: unifiedAiProduct + properties: { + format: 'rawxml' + value: loadTextContent('./policies/unified-ai-product-subscription.xml') + } +} + +////// Deployment Listing Operations ////// + +// Reference the deployment operations from the OpenAPI spec +resource deploymentsOperation 'Microsoft.ApiManagement/service/apis/operations@2024-06-01-preview' existing = if (enabled) { + name: 'deployments' + parent: unifiedAiApi +} + +resource deploymentByNameOperation 'Microsoft.ApiManagement/service/apis/operations@2024-06-01-preview' existing = if (enabled) { + name: 'deployment-by-name' + parent: unifiedAiApi +} + +// Operation-level policy for listing all deployments +resource deploymentsOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2024-06-01-preview' = if (enabled) { + name: 'policy' + parent: deploymentsOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/unified-ai-api-deployments-policy.xml') + } +} + +// Operation-level policy for getting a specific deployment by name +resource deploymentByNameOperationPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2024-06-01-preview' = if (enabled) { + name: 'policy' + parent: deploymentByNameOperation + properties: { + format: 'rawxml' + value: loadTextContent('./policies/unified-ai-api-deployment-by-name-policy.xml') + } +} + +////// Diagnostics ////// + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2024-06-01-preview' = if (enabled && length(apimLoggerId) > 0) { + parent: unifiedAiApi + name: 'azuremonitor' + properties: { + alwaysLog: 'allErrors' + verbosity: 'verbose' + logClientIp: true + loggerId: apimLoggerId + sampling: { + samplingType: 'fixed' + percentage: json('100') + } + frontend: { + request: { + headers: azureMonitorLogSettings.frontend.request.headers + body: { + bytes: azureMonitorLogSettings.frontend.request.body.bytes + } + } + response: { + headers: azureMonitorLogSettings.frontend.response.headers + body: { + bytes: azureMonitorLogSettings.frontend.response.body.bytes + } + } + } + backend: { + request: { + headers: azureMonitorLogSettings.backend.request.headers + body: { + bytes: azureMonitorLogSettings.backend.request.body.bytes + } + } + response: { + headers: azureMonitorLogSettings.backend.response.headers + body: { + bytes: azureMonitorLogSettings.backend.response.body.bytes + } + } + } + largeLanguageModel: { + logs: azureMonitorLogSettings.largeLanguageModel.logs + requests: { + messages: azureMonitorLogSettings.largeLanguageModel.requests.messages + maxSizeInBytes: azureMonitorLogSettings.largeLanguageModel.requests.maxSizeInBytes + } + responses: { + messages: azureMonitorLogSettings.largeLanguageModel.responses.messages + maxSizeInBytes: azureMonitorLogSettings.largeLanguageModel.responses.maxSizeInBytes + } + } + } +} + +// ------------------ +// OUTPUTS +// ------------------ + +@description('Name of the Unified AI API') +output apiName string = enabled ? unifiedAiApi.name : '' + +@description('Path of the Unified AI API') +output apiPath string = enabled ? 'unified-ai' : '' + +@description('Name of the Unified AI product') +output productName string = enabled ? unifiedAiProduct.name : '' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api/UnifiedAIWildcard.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api/UnifiedAIWildcard.json new file mode 100644 index 000000000..cd65f12dc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/unified-ai-api/UnifiedAIWildcard.json @@ -0,0 +1,132 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "unified-ai-wildcard-api", + "description": "Unified AI Gateway Wildcard API - Routes requests to multiple AI model providers (Azure OpenAI, AI Foundry, Gemini) using dynamic path-based routing.", + "version": "1.0" + }, + "paths": { + "/deployments": { + "get": { + "operationId": "deployments", + "summary": "List available model deployments", + "description": "Returns a list of all available model deployments that the caller has access to.", + "responses": { + "200": { + "description": "Successful response with deployment list" + } + } + } + }, + "/deployments/{deployment-id}": { + "get": { + "operationId": "deployment-by-name", + "summary": "Get a specific model deployment", + "description": "Returns details of a specific model deployment by name.", + "parameters": [ + { + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response with deployment details" + }, + "404": { + "description": "Deployment not found" + } + } + } + }, + "/*": { + "get": { + "operationId": "wildcard-get", + "summary": "Wildcard GET", + "description": "Handles all GET requests not matched by specific operations.", + "responses": { + "200": { + "description": "Successful response" + } + } + }, + "post": { + "operationId": "wildcard-post", + "summary": "Wildcard POST", + "description": "Handles all POST requests (chat completions, embeddings, etc.).", + "responses": { + "200": { + "description": "Successful response" + } + } + }, + "put": { + "operationId": "wildcard-put", + "summary": "Wildcard PUT", + "description": "Handles all PUT requests.", + "responses": { + "200": { + "description": "Successful response" + } + } + }, + "patch": { + "operationId": "wildcard-patch", + "summary": "Wildcard PATCH", + "description": "Handles all PATCH requests.", + "responses": { + "200": { + "description": "Successful response" + } + } + }, + "delete": { + "operationId": "wildcard-delete", + "summary": "Wildcard DELETE", + "description": "Handles all DELETE requests (e.g., responses API).", + "responses": { + "200": { + "description": "Successful response" + } + } + }, + "options": { + "operationId": "wildcard-options", + "summary": "Wildcard OPTIONS (CORS preflight)", + "description": "Handles CORS preflight requests.", + "responses": { + "200": { + "description": "Successful response" + } + } + } + } + }, + "components": { + "securitySchemes": { + "apiKeyHeader": { + "type": "apiKey", + "name": "api-key", + "in": "header" + }, + "apiKeyQuery": { + "type": "apiKey", + "name": "api-key", + "in": "query" + } + } + }, + "security": [ + {}, + { + "apiKeyHeader": [] + }, + { + "apiKeyQuery": [] + } + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryAzureAI.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryAzureAI.json new file mode 100644 index 000000000..cfece2fa7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryAzureAI.json @@ -0,0 +1,2363 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "AI Model Inference", + "version": "2025-05-15-preview" + }, + "servers": [ + { + "url": "https://{endpoint}.services.ai.azure.com/models", + "description": "AI Model Inference", + "variables": { + "resource": { + "default": "", + "description": "The Azure AI Services resource name, for example 'my-resource'" + } + } + } + ], + "paths": { + "/deployments": { + "get": { + "operationId": "deployments", + "description": "Get deployments for Azure OpenAI resource for Microsoft Foundry Integration", + "parameters": [ + ], + "responses": { + "200": { + "description": "AI Foundry integration operations for retrieving available deployments", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "summary": "Deployments" + } + }, + "/deployments/{deployment-id}": { + "get": { + "summary": "Get deployment by name", + "operationId": "deployment-by-name", + "description": "Get deployment details by deployment name for LLM resource for Microsoft Foundry Integration", + "parameters": [{ + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "enum": [""], + "type": "" + } + }], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/chat/completions": { + "post": { + "operationId": "getChatCompletions", + "description": "Gets chat completions for the provided chat messages.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data. The method makes a REST API call to the `/chat/completions` route\non the given endpoint.", + "parameters": [ + { + "name": "extra-parameters", + "in": "header", + "required": false, + "description": "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`.", + "schema": { + "$ref": "#/components/schemas/ExtraParameters" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatCompletions" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatCompletionsOptions" + }, + "example": { + "model": "gpt-4o", + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant" + }, + { + "role": "user", + "content": "How are you?" + } + ] + } + } + }, + "description": "The parameters of the chat completions request." + }, + "summary": "Gets chat completions for the provided chat messages." + } + }, + "/embeddings": { + "post": { + "operationId": "getEmbeddings", + "description": "Return the embedding vectors for given text prompts.\nThe method makes a REST API call to the `/embeddings` route on the given endpoint.", + "parameters": [ + { + "name": "extra-parameters", + "in": "header", + "required": false, + "description": "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`.", + "schema": { + "$ref": "#/components/schemas/ExtraParameters" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmbeddingsResult" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmbeddingsOptions" + }, + "example": { + "model": "text-embedding-3-large", + "input": "How are you?" + } + } + }, + "description": "The parameters of the embeddings request." + }, + "summary": "Return the embedding vectors for given text prompts." + } + }, + "/images/embeddings": { + "post": { + "operationId": "getImageEmbeddings", + "description": "Return the embedding vectors for given images.\nThe method makes a REST API call to the `/images/embeddings` route on the given endpoint.", + "parameters": [ + { + "name": "extra-parameters", + "in": "header", + "required": false, + "description": "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`.", + "schema": { + "$ref": "#/components/schemas/ExtraParameters" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmbeddingsResult" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageEmbeddingsOptions" + } + } + }, + "description": "The parameters of the image embeddings request." + }, + "summary": "Return the embedding vectors for given images." + } + }, + "/images/generations": { + "post": { + "operationId": "getImageGenerations", + "description": "Generates an image based on a text or image prompt.\nThe method makes a REST API call to the `/images/generations` route on the given endpoint.", + "parameters": [ + { + "name": "extra-parameters", + "in": "header", + "required": false, + "description": "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`.", + "schema": { + "$ref": "#/components/schemas/ExtraParameters" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageGenerationsResult" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageGenerationsOptions" + } + } + } + }, + "summary": "Generates an image based on a text or image prompt." + } + }, + "/info": { + "get": { + "operationId": "getModelInfo", + "description": "Returns information about the AI model deployed.\nThe method makes a REST API call to the `/info` route on the given endpoint.\nThis method will only work when using Serverless API, Managed Compute, or Model .\ninference endpoint. Azure OpenAI endpoints don't support i.", + "parameters": [ + { + "name": "model", + "in": "query", + "required": false, + "description": "The model deployment name you want information from.", + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelInfo" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Azure.Core.Foundations.ErrorResponse" + } + } + } + } + }, + "summary": "Returns information about the AI model deployed." + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "BearerAuth": [] + }, + { + "OAuth2Auth": [ + "https://cognitiveservices.azure.com/.default" + ] + } + ], + "components": { + "parameters": { + "Azure.Core.Foundations.ApiVersionParameter": { + "name": "api-version", + "in": "query", + "required": false, + "description": "The API version to use for this operation.", + "schema": { + "type": "string", + "minLength": 1, + "default": "2024-05-01-preview" + }, + "explode": false + } + }, + "schemas": { + "AudioContentFormat": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "wav", + "mp3" + ] + } + ], + "description": "A representation of the possible audio formats for audio." + }, + "Azure.Core.Foundations.Error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "message": { + "type": "string", + "description": "A human-readable representation of the error." + }, + "target": { + "type": "string", + "description": "The target of the error." + }, + "details": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Azure.Core.Foundations.Error" + }, + "description": "An array of details about specific errors that led to this reported error." + }, + "innererror": { + "allOf": [ + { + "$ref": "#/components/schemas/Azure.Core.Foundations.InnerError" + } + ], + "description": "An object containing more specific information than the current object about the error." + } + }, + "description": "The error object." + }, + "Azure.Core.Foundations.ErrorResponse": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "allOf": [ + { + "$ref": "#/components/schemas/Azure.Core.Foundations.Error" + } + ], + "description": "The error object." + } + }, + "description": "A response containing error details." + }, + "Azure.Core.Foundations.InnerError": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "One of a server-defined set of error codes." + }, + "innererror": { + "allOf": [ + { + "$ref": "#/components/schemas/Azure.Core.Foundations.InnerError" + } + ], + "description": "Inner error." + } + }, + "description": "An object containing more specific information about the error. As per Microsoft One API guidelines - https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors." + }, + "ChatChoice": { + "type": "object", + "required": [ + "index", + "finish_reason", + "message" + ], + "properties": { + "index": { + "type": "integer", + "format": "int32", + "description": "The ordered index associated with this chat completions choice." + }, + "finish_reason": { + "allOf": [ + { + "$ref": "#/components/schemas/CompletionsFinishReason" + } + ], + "nullable": true, + "description": "The reason that this chat completions choice completed its generated.", + "readOnly": true + }, + "message": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatResponseMessage" + } + ], + "description": "The chat message for a given chat completions prompt.", + "readOnly": true + } + }, + "description": "The representation of a single prompt completion as part of an overall chat completions request.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + }, + "ChatCompletions": { + "type": "object", + "required": [ + "id", + "object", + "created", + "model", + "choices", + "usage" + ], + "properties": { + "id": { + "type": "string", + "description": "A unique identifier associated with this chat completions response." + }, + "object": { + "type": "string", + "enum": [ + "chat.completion" + ], + "description": "The response object type, which is always `chat.completion`." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.", + "readOnly": true + }, + "model": { + "type": "string", + "description": "The model used for the chat completion.", + "readOnly": true + }, + "choices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatChoice" + }, + "minItems": 1, + "description": "The collection of completions choices associated with this completions response.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated.", + "readOnly": true + }, + "usage": { + "allOf": [ + { + "$ref": "#/components/schemas/CompletionsUsage" + } + ], + "description": " Usage information for tokens processed and generated as part of this completions operation.", + "readOnly": true + } + }, + "description": "Representation of the response data from a chat completions request.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + }, + "ChatCompletionsAudio": { + "type": "object", + "required": [ + "id", + "expires_at", + "data", + "transcript" + ], + "properties": { + "id": { + "type": "string", + "description": " Unique identifier for the audio response. This value can be used in chat history messages instead of passing \n the full audio object.", + "readOnly": true + }, + "expires_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) at which the audio piece expires and can't be any longer referenced by its ID in \nmulti-turn conversations.", + "readOnly": true + }, + "data": { + "type": "string", + "description": "Base64 encoded audio data", + "readOnly": true + }, + "format": { + "allOf": [ + { + "$ref": "#/components/schemas/AudioContentFormat" + } + ], + "description": "The format of the audio content. If format is not provided, it will match the format used in the\ninput audio request.", + "readOnly": true + }, + "transcript": { + "type": "string", + "description": "The transcript of the audio file.", + "readOnly": true + } + }, + "description": "A representation of the audio generated by the model." + }, + "ChatCompletionsModality": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "text", + "audio" + ] + } + ], + "description": "The modalities that the model is allowed to use for the chat completions response." + }, + "ChatCompletionsNamedToolChoice": { + "type": "object", + "required": [ + "type", + "function" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsNamedToolChoiceFunction" + } + ], + "description": "The function that should be called." + } + }, + "description": "A tool selection of a specific, named function tool that will limit chat completions to using the named function." + }, + "ChatCompletionsNamedToolChoiceFunction": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the function that should be called." + } + }, + "description": "A tool selection of a specific, named function tool that will limit chat completions to using the named function." + }, + "ChatCompletionsOptions": { + "type": "object", + "required": [ + "messages" + ], + "properties": { + "messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatRequestMessage" + }, + "minItems": 1, + "description": "The collection of context messages associated with this chat completions request.\nTypical usage begins with a chat message for the System role that provides instructions for\nthe behavior of the assistant, followed by alternating messages between the User and\nAssistant roles." + }, + "frequency_penalty": { + "type": "number", + "format": "float", + "minimum": -2, + "maximum": 2, + "description": "A value that influences the probability of generated tokens appearing based on their cumulative\nfrequency in generated text.\nPositive values will make tokens less likely to appear as their frequency increases and\ndecrease the likelihood of the model repeating the same statements verbatim.\nSupported range is [-2, 2].", + "default": 0 + }, + "stream": { + "type": "boolean", + "description": "A value indicating whether chat completions should be streamed for this request." + }, + "presence_penalty": { + "type": "number", + "format": "float", + "minimum": -2, + "maximum": 2, + "description": "A value that influences the probability of generated tokens appearing based on their existing\npresence in generated text.\nPositive values will make tokens less likely to appear when they already exist and increase the\nmodel's likelihood to output new topics.\nSupported range is [-2, 2].", + "default": 0 + }, + "temperature": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "The sampling temperature to use that controls the apparent creativity of generated completions.\nHigher values will make output more random while lower values will make results more focused\nand deterministic.\nIt is not recommended to modify temperature and top_p for the same completions request as the\ninteraction of these two settings is difficult to predict.\nSupported range is [0, 1].", + "default": 0.7 + }, + "top_p": { + "type": "number", + "format": "float", + "minimum": 0, + "maximum": 1, + "description": "An alternative to sampling with temperature called nucleus sampling. This value causes the\nmodel to consider the results of tokens with the provided probability mass. As an example, a\nvalue of 0.15 will cause only the tokens comprising the top 15% of probability mass to be\nconsidered.\nIt is not recommended to modify temperature and top_p for the same completions request as the\ninteraction of these two settings is difficult to predict.\nSupported range is [0, 1].", + "default": 1 + }, + "max_tokens": { + "type": "integer", + "format": "int32", + "minimum": 0, + "description": "The maximum number of tokens to generate." + }, + "response_format": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsResponseFormat" + } + ], + "description": "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length." + }, + "stop": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "A collection of textual sequences that will end completions generation." + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatCompletionsToolDefinition" + }, + "minItems": 1, + "description": "A list of tools the model may request to call. Currently, only functions are supported as a tool. The model\nmay response with a function call request and provide the input arguments in JSON format for that function." + }, + "tool_choice": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsToolChoicePreset" + }, + { + "$ref": "#/components/schemas/ChatCompletionsNamedToolChoice" + } + ], + "description": "If specified, the model will configure which of the provided tools it can use for the chat completions response." + }, + "seed": { + "type": "integer", + "format": "int64", + "description": "If specified, the system will make a best effort to sample deterministically such that repeated requests with the\nsame seed and parameters should return the same result. Determinism is not guaranteed." + }, + "model": { + "type": "string", + "description": "ID of the specific AI model to use, if more than one model is available on the endpoint." + }, + "modalities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatCompletionsModality" + }, + "description": "The modalities that the model is allowed to use for the chat completions response. The default modality\nis `text`. Indicating an unsupported modality combination results in an 422 error." + }, + "user_security_context": { + "allOf": [ + { + "$ref": "#/components/schemas/UserSecurityContext" + } + ], + "description": "User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. \nThese fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. \n[Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud." + } + }, + "additionalProperties": {}, + "description": "The configuration information for a chat completions request.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + }, + "ChatCompletionsResponseFormat": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "The response format type to use for chat completions." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/ChatCompletionsResponseFormatText", + "json_object": "#/components/schemas/ChatCompletionsResponseFormatJsonObject", + "json_schema": "#/components/schemas/ChatCompletionsResponseFormatJsonSchema" + } + }, + "description": "Represents the format that the model must output. Use this to enable JSON mode instead of the default text mode.\nNote that to enable JSON mode, some AI models may also require you to instruct the model to produce JSON\nvia a system or user message." + }, + "ChatCompletionsResponseFormatJsonObject": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_object" + ], + "description": "Response format type: always 'json_object' for this object." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsResponseFormat" + } + ], + "description": "A response format for Chat Completions that restricts responses to emitting valid JSON objects.\nNote that to enable JSON mode, some AI models may also require you to instruct the model to produce JSON\nvia a system or user message." + }, + "ChatCompletionsResponseFormatJsonSchema": { + "type": "object", + "required": [ + "type", + "json_schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_schema" + ], + "description": "The type of response format being defined: `json_schema`" + }, + "json_schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsResponseFormatJsonSchemaDefinition" + } + ], + "description": "The definition of the required JSON schema in the response, and associated metadata." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsResponseFormat" + } + ], + "description": "A response format for Chat Completions that restricts responses to emitting valid JSON objects, with a\nJSON schema specified by the caller." + }, + "ChatCompletionsResponseFormatJsonSchemaDefinition": { + "type": "object", + "required": [ + "name", + "schema" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "The definition of the JSON schema" + }, + "description": { + "type": "string", + "description": "A description of the response format, used by the AI model to determine how to generate responses in this format." + }, + "strict": { + "type": "boolean", + "description": "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of\nJSON Schema is supported when `strict` is `true`.", + "default": false + } + }, + "description": "The definition of the required JSON schema in the response, and associated metadata." + }, + "ChatCompletionsResponseFormatText": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ], + "description": "Response format type: always 'text' for this object." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsResponseFormat" + } + ], + "description": "A response format for Chat Completions that emits text responses. This is the default response format." + }, + "ChatCompletionsToolCall": { + "type": "object", + "required": [ + "id", + "type", + "function" + ], + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of tool call. Currently, only `function` is supported." + }, + "function": { + "allOf": [ + { + "$ref": "#/components/schemas/FunctionCall" + } + ], + "description": "The details of the function call requested by the AI model." + } + }, + "description": "A function tool call requested by the AI model." + }, + "ChatCompletionsToolChoicePreset": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "auto", + "none", + "required" + ] + } + ], + "description": "Represents a generic policy for how a chat completions tool may be selected." + }, + "ChatCompletionsToolDefinition": { + "type": "object", + "required": [ + "type", + "function" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "allOf": [ + { + "$ref": "#/components/schemas/FunctionDefinition" + } + ], + "description": "The function definition details for the function tool." + } + }, + "description": "The definition of a chat completions tool that can call a function." + }, + "ChatMessageAudioContentItem": { + "type": "object", + "required": [ + "type", + "audio_url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "audio_url" + ], + "description": "The discriminated object type: always 'image_url' for this type." + }, + "audio_url": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageAudioUrl" + } + ], + "description": "An internet location, which must be accessible to the model, from which the audio may be retrieved." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageContentItem" + } + ], + "description": "A structured chat content item containing an audio reference." + }, + "ChatMessageAudioUrl": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The URL of the audio." + } + }, + "description": "An internet location from which the model may retrieve an audio." + }, + "ChatMessageContentItem": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "The discriminated object type." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/ChatMessageTextContentItem", + "image_url": "#/components/schemas/ChatMessageImageContentItem", + "audio_url": "#/components/schemas/ChatMessageAudioContentItem", + "input_audio": "#/components/schemas/ChatMessageInputAudioContentItem" + } + }, + "description": "An abstract representation of a structured content item within a chat message." + }, + "ChatMessageImageContentItem": { + "type": "object", + "required": [ + "type", + "image_url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_url" + ], + "description": "The discriminated object type: always 'image_url' for this type." + }, + "image_url": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageImageUrl" + } + ], + "description": "An internet location, which must be accessible to the model,from which the image may be retrieved." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageContentItem" + } + ], + "description": "A structured chat content item containing an image reference." + }, + "ChatMessageImageDetailLevel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "auto", + "low", + "high" + ] + } + ], + "description": "A representation of the possible image detail levels for image-based chat completions message content." + }, + "ChatMessageImageUrl": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "description": "The URL of the image." + }, + "detail": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageImageDetailLevel" + } + ], + "description": "The evaluation quality setting to use, which controls relative prioritization of speed, token consumption, and\naccuracy." + } + }, + "description": "An internet location from which the model may retrieve an image." + }, + "ChatMessageInputAudio": { + "type": "object", + "required": [ + "data", + "format" + ], + "properties": { + "data": { + "type": "string", + "description": "Base64 encoded audio data" + }, + "format": { + "allOf": [ + { + "$ref": "#/components/schemas/AudioContentFormat" + } + ], + "description": "The audio format of the audio content." + } + }, + "description": "The details of an audio chat message content part." + }, + "ChatMessageInputAudioContentItem": { + "type": "object", + "required": [ + "type", + "format" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_audio" + ], + "description": "The discriminated object type: always 'input_audio' for this type." + }, + "format": { + "allOf": [ + { + "$ref": "#/components/schemas/AudioContentFormat" + } + ], + "description": "The audio format of the audio reference." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageContentItem" + } + ], + "description": "A structured chat content item containing an audio content." + }, + "ChatMessageTextContentItem": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ], + "description": "The discriminated object type: always 'text' for this type." + }, + "text": { + "type": "string", + "description": "The content of the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatMessageContentItem" + } + ], + "description": "A structured chat content item containing plain text." + }, + "ChatRequestAssistantMessage": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The chat role associated with this message, which is always 'assistant' for assistant messages." + }, + "content": { + "type": "string", + "description": "The content of the message." + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatCompletionsToolCall" + }, + "description": "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured." + }, + "audio": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatRequestAudioReference" + } + ], + "description": " The audio generated by a previous response in a multi-turn conversation." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatRequestMessage" + } + ], + "description": "A request chat message representing response or action from the assistant." + }, + "ChatRequestAudioReference": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": " Unique identifier for the audio response. This value corresponds to the id of a previous audio completion." + } + }, + "description": "A reference to an audio response generated by the model." + }, + "ChatRequestMessage": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatRole" + } + ], + "description": "The chat role associated with this message." + } + }, + "discriminator": { + "propertyName": "role", + "mapping": { + "system": "#/components/schemas/ChatRequestSystemMessage", + "user": "#/components/schemas/ChatRequestUserMessage", + "assistant": "#/components/schemas/ChatRequestAssistantMessage", + "tool": "#/components/schemas/ChatRequestToolMessage" + } + }, + "description": "An abstract representation of a chat message as provided in a request." + }, + "ChatRequestSystemMessage": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "system" + ], + "description": "The chat role associated with this message, which is always 'system' for system messages." + }, + "content": { + "type": "string", + "description": "The contents of the system message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatRequestMessage" + } + ], + "description": "A request chat message containing system instructions that influence how the model will generate a chat completions\nresponse." + }, + "ChatRequestToolMessage": { + "type": "object", + "required": [ + "role", + "tool_call_id" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "tool" + ], + "description": "The chat role associated with this message, which is always 'tool' for tool messages." + }, + "content": { + "type": "string", + "description": "The content of the message." + }, + "tool_call_id": { + "type": "string", + "description": "The ID of the tool call resolved by the provided content." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatRequestMessage" + } + ], + "description": "A request chat message representing requested output from a configured tool." + }, + "ChatRequestUserMessage": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "user" + ], + "description": "The chat role associated with this message, which is always 'user' for user messages." + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatMessageContentItem" + } + } + ], + "description": "The contents of the user message, with available input types varying by selected model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/ChatRequestMessage" + } + ], + "description": "A request chat message representing user input to the assistant." + }, + "ChatResponseMessage": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatRole" + } + ], + "description": "The chat role associated with the message.", + "readOnly": true + }, + "content": { + "type": "string", + "nullable": true, + "description": "The content of the message.", + "readOnly": true + }, + "reasoning_content": { + "type": "string", + "description": "The reasoning content the model used for generating the response", + "readOnly": true + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatCompletionsToolCall" + }, + "description": "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured.", + "readOnly": true + }, + "audio": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatCompletionsAudio" + } + ], + "description": " The audio generated by the model as a response to the messages if the model is configured to generate audio.", + "readOnly": true + } + }, + "description": "A representation of a chat message as received in a response." + }, + "ChatRole": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "system", + "developer", + "user", + "assistant", + "tool" + ] + } + ], + "description": "A description of the intended purpose of a message within a chat completions interaction." + }, + "CompletionsFinishReason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "stop", + "length", + "content_filter", + "tool_calls" + ] + } + ], + "description": "Representation of the manner in which a completions response concluded." + }, + "CompletionsUsage": { + "type": "object", + "required": [ + "completion_tokens", + "prompt_tokens", + "total_tokens" + ], + "properties": { + "completion_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens generated across all completions emissions.", + "readOnly": true + }, + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens in the provided prompts for the completions request.", + "readOnly": true + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens processed for the completions request and response.", + "readOnly": true + }, + "completion_tokens_details": { + "allOf": [ + { + "$ref": "#/components/schemas/CompletionsUsageDetails" + } + ], + "description": "Breakdown of tokens used in a completion.", + "readOnly": true + }, + "prompt_tokens_details": { + "allOf": [ + { + "$ref": "#/components/schemas/PromptUsageDetails" + } + ], + "description": "Breakdown of tokens used in the prompt/chat history.", + "readOnly": true + } + }, + "description": "Representation of the token counts processed for a completions request.\nCounts consider all tokens across prompts, choices, choice alternates, best_of generations, and\nother consumers." + }, + "CompletionsUsageDetails": { + "type": "object", + "required": [ + "audio_tokens", + "reasoning_tokens", + "total_tokens" + ], + "properties": { + "audio_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens corresponding to audio input.", + "readOnly": true + }, + "reasoning_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens corresponding to reasoning.", + "readOnly": true + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens processed for the completions request and response.", + "readOnly": true + } + }, + "description": "A breakdown of tokens used in a completion." + }, + "EmbeddingEncodingFormat": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "base64", + "binary", + "float", + "int8", + "ubinary", + "uint8" + ] + } + ], + "description": "Specifies the types of embeddings to generate. Compressed embeddings types like `uint8`, `int8`, `ubinary` and \n`binary`, may reduce storage costs without sacrificing the integrity of the data. Returns a 422 error if the\nmodel doesn't support the value or parameter. Read the model's documentation to know the values supported by\nthe your model." + }, + "EmbeddingInputType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "text", + "query", + "document" + ] + } + ], + "description": "Represents the input types used for embedding search." + }, + "EmbeddingItem": { + "type": "object", + "required": [ + "embedding", + "index", + "object" + ], + "properties": { + "embedding": { + "type": "array", + "items": { + "type": "number", + "format": "float" + }, + "description": "List of embedding values for the input prompt. These represent a measurement of the\nvector-based relatedness of the provided input. Or a base64 encoded string of the embedding vector.", + "readOnly": true + }, + "index": { + "type": "integer", + "format": "int32", + "description": "Index of the prompt to which the EmbeddingItem corresponds.", + "readOnly": true + }, + "object": { + "type": "string", + "enum": [ + "embedding" + ], + "description": "The object type of this embeddings item. Will always be `embedding`." + } + }, + "description": "Representation of a single embeddings relatedness comparison." + }, + "EmbeddingsOptions": { + "type": "object", + "required": [ + "input" + ], + "properties": { + "input": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Input text to embed, encoded as a string or array of tokens.\nTo embed multiple inputs in a single request, pass an array\nof strings or array of token arrays." + }, + "dimensions": { + "type": "integer", + "format": "int32", + "description": "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + }, + "encoding_format": { + "allOf": [ + { + "$ref": "#/components/schemas/EmbeddingEncodingFormat" + } + ], + "description": "Optional. The desired format for the returned embeddings." + }, + "input_type": { + "allOf": [ + { + "$ref": "#/components/schemas/EmbeddingInputType" + } + ], + "description": "Optional. The type of the input.\nReturns a 422 error if the model doesn't support the value or parameter." + }, + "model": { + "type": "string", + "description": "ID of the specific AI model to use, if more than one model is available on the endpoint." + } + }, + "additionalProperties": {}, + "description": "The configuration information for an embeddings request." + }, + "EmbeddingsResult": { + "type": "object", + "required": [ + "id", + "data", + "usage", + "object", + "model" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the embeddings result.", + "readOnly": true + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmbeddingItem" + }, + "description": "Embedding values for the prompts submitted in the request.", + "readOnly": true + }, + "usage": { + "allOf": [ + { + "$ref": "#/components/schemas/EmbeddingsUsage" + } + ], + "description": "Usage counts for tokens input using the embeddings API.", + "readOnly": true + }, + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The object type of the embeddings result. Will always be `list`." + }, + "model": { + "type": "string", + "description": "The model ID used to generate this result.", + "readOnly": true + } + }, + "description": "Representation of the response data from an embeddings request.\nEmbeddings measure the relatedness of text strings and are commonly used for search, clustering,\nrecommendations, and other similar scenarios." + }, + "EmbeddingsUsage": { + "type": "object", + "required": [ + "prompt_tokens", + "total_tokens" + ], + "properties": { + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "Number of tokens in the request.", + "readOnly": true + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "Total number of tokens transacted in this request/response. Should equal the\nnumber of tokens in the request.", + "readOnly": true + } + }, + "description": "Measurement of the amount of tokens used in this request and response." + }, + "ExtraParameters": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "error", + "drop", + "pass-through" + ] + } + ], + "description": "Controls what happens if extra parameters, undefined by the REST API, are passed in the JSON request payload." + }, + "FunctionCall": { + "type": "object", + "required": [ + "name", + "arguments" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call.", + "readOnly": true + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format.\nNote that the model does not always generate valid JSON, and may hallucinate parameters\nnot defined by your function schema. Validate the arguments in your code before calling\nyour function.", + "readOnly": true + } + }, + "description": "The name and arguments of a function that should be called, as generated by the model." + }, + "FunctionDefinition": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the function to be called." + }, + "description": { + "type": "string", + "description": "A description of what the function does. The model will use this description when selecting the function and\ninterpreting its parameters." + }, + "parameters": { + "type": "object", + "additionalProperties": {}, + "description": "The parameters the function accepts, described as a JSON Schema object." + } + }, + "description": "The definition of a caller-specified function that chat completions may invoke in response to matching user input." + }, + "ImageEmbeddingInput": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "description": "The input image encoded in base64 string as a data URL. Example: `data:image/{format};base64,{data}`." + }, + "text": { + "type": "string", + "description": "Optional. The text input to feed into the model (like DINO, CLIP).\nReturns a 422 error if the model doesn't support the value or parameter." + } + }, + "description": "Represents an image with optional text." + }, + "ImageEmbeddingsOptions": { + "type": "object", + "required": [ + "input" + ], + "properties": { + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ImageEmbeddingInput" + }, + "description": "Input image to embed. To embed multiple inputs in a single request, pass an array.\nThe input must not exceed the max input tokens for the model." + }, + "dimensions": { + "type": "integer", + "format": "int32", + "description": "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + }, + "encoding_format": { + "allOf": [ + { + "$ref": "#/components/schemas/EmbeddingEncodingFormat" + } + ], + "description": "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + }, + "input_type": { + "allOf": [ + { + "$ref": "#/components/schemas/EmbeddingInputType" + } + ], + "description": "Optional. The type of the input.\nReturns a 422 error if the model doesn't support the value or parameter." + }, + "model": { + "type": "string", + "description": "ID of the specific AI model to use, if more than one model is available on the endpoint." + } + }, + "additionalProperties": {}, + "description": "The configuration information for an image embeddings request." + }, + "ImageGenerationItem": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "description": "The image generated, encoded in base64." + }, + "seed": { + "type": "integer", + "format": "int64", + "description": "The seed that can be used to generate the image." + } + }, + "description": "Representation of a single image generation." + }, + "ImageGenerationOutputFormat": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "png", + "jpg" + ] + } + ], + "description": "The image generation format to use in the output." + }, + "ImageGenerationPreset": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "672x1566", + "768x1366", + "836x1254", + "916x1145", + "1024x1024", + "1145x916", + "1254x836", + "1366x768", + "1566x672" + ] + } + ], + "description": "The preset size of the image to generate." + }, + "ImageGenerationPrompt": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string", + "description": "The input image encoded in base64 string as a data URL.\nExample: `data:image/{format};base64,{data}`.\"" + }, + "strength": { + "type": "number", + "format": "float", + "description": "The degree at which the generation process uses the image prompt as guidance. 1 indicates\na generation process that fully follows the input image. 0 a generation a process that doesn't\ntake into consideration the prompt image.", + "default": 1 + } + }, + "description": "Represents an image to use for guidance during the generation." + }, + "ImageGenerationQuality": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "hd", + "standard" + ] + } + ], + "description": "The quality of the image to generate." + }, + "ImageGenerationSize": { + "type": "object", + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer", + "format": "int32", + "description": "The width of the image to generate, in pixels." + }, + "height": { + "type": "integer", + "format": "int32", + "description": "The height of the image to generate, in pixels." + } + }, + "description": "The size of the image to generate." + }, + "ImageGenerationUsage": { + "type": "object", + "required": [ + "image_generations" + ], + "properties": { + "image_generations": { + "type": "integer", + "format": "int32", + "description": "The number of image generations performed." + } + }, + "description": "Usage counts for image generation API." + }, + "ImageGenerationsOptions": { + "type": "object", + "required": [ + "prompt", + "size", + "n" + ], + "properties": { + "prompt": { + "type": "string", + "description": "The prompt to use for the image geneartion. Read the model documentation to understand\nwhich language you should use to prompt the model and get specific results." + }, + "negative_prompt": { + "type": "string", + "description": "Optional. The prompt to use for the negative image generation. Read the model documentation\nto understand to prompt the model and get specific results. If the model doesn't support\nnegative prompts, a 422 error is returned." + }, + "image_prompt": { + "allOf": [ + { + "$ref": "#/components/schemas/ImageGenerationPrompt" + } + ], + "description": "Optional. An image to use as guidance for the image generation process." + }, + "size": { + "anyOf": [ + { + "$ref": "#/components/schemas/ImageGenerationPreset" + }, + { + "$ref": "#/components/schemas/ImageGenerationSize" + } + ], + "description": "The size of the image to generate." + }, + "output_format": { + "allOf": [ + { + "$ref": "#/components/schemas/ImageGenerationOutputFormat" + } + ], + "description": "Optional. The format in which to generate the image.\nReturns a 422 error if the model doesn't support the value or parameter.", + "default": "png" + }, + "quality": { + "allOf": [ + { + "$ref": "#/components/schemas/ImageGenerationQuality" + } + ], + "description": "Optional. The quality of the image to generate.\nReturns a 422 error if the model doesn't support the value or parameter.", + "default": "standard" + }, + "n": { + "type": "integer", + "format": "int32", + "description": "The number of images to generate. Defaults to 1.", + "default": 1 + }, + "model": { + "type": "string", + "description": "ID of the specific AI model to use, if more than one model is available on the endpoint." + }, + "seed": { + "type": "integer", + "format": "int64", + "description": "If specified, the system will make a best effort to sample deterministically such that repeated requests with the\nsame seed and parameters should return the same result. Determinism is not guaranteed." + } + }, + "additionalProperties": {}, + "description": "The configuration information for an image generation request." + }, + "ImageGenerationsResult": { + "type": "object", + "required": [ + "id", + "created", + "model", + "data", + "usage", + "object" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the generation result." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970." + }, + "model": { + "type": "string", + "description": "The model used for the chat completion." + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ImageGenerationItem" + }, + "description": "Embedding values for the prompts submitted in the request." + }, + "usage": { + "allOf": [ + { + "$ref": "#/components/schemas/ImageGenerationUsage" + } + ], + "description": "Usage counts for tokens input using the embeddings API." + }, + "object": { + "type": "string", + "enum": [ + "image.generation" + ], + "description": "The object type of the image generation result. Will always be `list`." + } + }, + "description": "The result of an image generation request." + }, + "ModelInfo": { + "type": "object", + "required": [ + "model_name", + "model_type", + "model_provider_name" + ], + "properties": { + "model_name": { + "type": "string", + "description": "The name of the AI model. For example: `Phi21`", + "readOnly": true + }, + "model_type": { + "allOf": [ + { + "$ref": "#/components/schemas/ModelType" + } + ], + "description": "The type of the AI model. A Unique identifier for the profile.", + "readOnly": true + }, + "model_provider_name": { + "type": "string", + "description": "The model provider name. For example: `Microsoft`", + "readOnly": true + } + }, + "description": "Represents some basic information about the AI model." + }, + "ModelType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "embeddings", + "chat-completion" + ] + } + ], + "description": "The type of AI model" + }, + "PromptUsageDetails": { + "type": "object", + "required": [ + "audio_tokens", + "cached_tokens" + ], + "properties": { + "audio_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens corresponding to audio input.", + "readOnly": true + }, + "cached_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens cached.", + "readOnly": true + } + }, + "description": "A breakdown of tokens used in the prompt/chat history." + }, + "StreamingChatChoiceUpdate": { + "type": "object", + "required": [ + "index", + "finish_reason", + "delta" + ], + "properties": { + "index": { + "type": "integer", + "format": "int32", + "description": "The ordered index associated with this chat completions choice." + }, + "finish_reason": { + "allOf": [ + { + "$ref": "#/components/schemas/CompletionsFinishReason" + } + ], + "nullable": true, + "description": "The reason that this chat completions choice completed its generated.", + "readOnly": true + }, + "delta": { + "allOf": [ + { + "$ref": "#/components/schemas/StreamingChatResponseMessageUpdate" + } + ], + "description": "An update to the chat message for a given chat completions prompt.", + "readOnly": true + } + }, + "description": "Represents an update to a single prompt completion when the service is streaming updates \nusing Server Sent Events (SSE).\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + }, + "StreamingChatCompletionsUpdate": { + "type": "object", + "required": [ + "id", + "object", + "created", + "model", + "choices" + ], + "properties": { + "id": { + "type": "string", + "description": "A unique identifier associated with this chat completions response." + }, + "object": { + "type": "string", + "enum": [ + "chat.completion" + ], + "description": "The response object type, which is always `chat.completion`." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970.", + "readOnly": true + }, + "model": { + "type": "string", + "description": "The model used for the chat completion.", + "readOnly": true + }, + "choices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamingChatChoiceUpdate" + }, + "minItems": 1, + "description": "An update to the collection of completion choices associated with this completions response.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated.", + "readOnly": true + }, + "usage": { + "allOf": [ + { + "$ref": "#/components/schemas/CompletionsUsage" + } + ], + "description": "Usage information for tokens processed and generated as part of this completions operation.", + "readOnly": true + } + }, + "description": "Represents a response update to a chat completions request, when the service is streaming updates \nusing Server Sent Events (SSE).\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + }, + "StreamingChatResponseMessageUpdate": { + "type": "object", + "properties": { + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/ChatRole" + } + ], + "description": "The chat role associated with the message. If present, should always be 'assistant'", + "readOnly": true + }, + "content": { + "type": "string", + "description": "The content of the message.", + "readOnly": true + }, + "reasoning_content": { + "type": "string", + "description": "The reasoning content the model used for generating the response", + "readOnly": true + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StreamingChatResponseToolCallUpdate" + }, + "description": "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured.", + "readOnly": true + } + }, + "description": "A representation of a chat message update as received in a streaming response." + }, + "StreamingChatResponseToolCallUpdate": { + "type": "object", + "required": [ + "id", + "function" + ], + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call.", + "readOnly": true + }, + "function": { + "allOf": [ + { + "$ref": "#/components/schemas/FunctionCall" + } + ], + "description": "Updates to the function call requested by the AI model.", + "readOnly": true + } + }, + "description": "An update to the function tool call information requested by the AI model." + }, + "UserSecurityContext": { + "type": "object", + "properties": { + "application_name": { + "type": "string", + "maxLength": 100, + "description": "The name of the application. Sensitive personal information should not be included in this field." + }, + "end_user_id": { + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "description": "This identifier is the Microsoft Entra ID (formerly Azure Active Directory) user object ID used to authenticate end-users within the generative AI application. Sensitive personal information should not be included in this field." + }, + "end_user_tenant_id": { + "type": "string", + "minLength": 36, + "maxLength": 36, + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", + "description": "The Microsoft 365 tenant ID the end user belongs to. It's required when the generative AI application is multi tenant." + }, + "source_ip": { + "type": "string", + "minLength": 2, + "maxLength": 45, + "description": "Captures the original client's IP address, accepting both IPv4 and IPv6 formats." + } + }, + "description": "User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. \nThese fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. \n[Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud." + }, + "Versions": { + "type": "string", + "enum": [ + "2024-05-01-preview", + "2025-04-01", + "2025-05-01", + "2025-05-15-preview" + ], + "description": "The AI.Model service versions." + } + }, + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "api-key" + }, + "BearerAuth": { + "type": "http", + "scheme": "Bearer" + }, + "OAuth2Auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + "scopes": { + "https://cognitiveservices.azure.com/.default": "" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAI.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAI.json new file mode 100644 index 000000000..d37539708 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAI.json @@ -0,0 +1,16648 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Azure OpenAI Service API", + "description": "Azure OpenAI APIs for completions and search", + "version": "2024-05-01-preview" + }, + "servers": [ + { + "url": "https://{endpoint}/openai", + "variables": { + "endpoint": { + "default": "{endpoint}" + } + } + } + ], + "security": [ + { + "bearer": [ + "api.read" + ] + }, + { + "apiKey": [] + } + ], + "paths": { + "/deployments": { + "get": { + "operationId": "deployments", + "description": "Get deployments for Azure OpenAI resource for AI Foundry Integration", + "parameters": [ + ], + "responses": { + "200": { + "description": "AI Foundry integration operations for retrieving available deployments", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + } + } + } + }, + "summary": "Get deployments" + } + }, + "/deployments/{deployment-id}": { + "get": { + "summary": "Get deployment by name", + "operationId": "deployment-by-name", + "description": "Get deployment details by deployment name for LLM resource for Microsoft Foundry Integration", + "parameters": [{ + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "enum": [""], + "type": "" + } + }], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/deployments/{deployment-id}/completions": { + "post": { + "description": "Creates a completion for the provided prompt, parameters and chosen model.", + "operationId": "Completions_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "davinci", + "description": "Deployment id of the model which was deployed." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createCompletionRequest" + }, + "example": { + "prompt": "Say this is a test", + "max_tokens": 7 + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createCompletionResponse" + }, + "example": { + "model": "davinci", + "object": "text_completion", + "id": "cmpl-4509KAos68kxOqpE2uYGw81j6m7uo", + "created": 1637097562, + "choices": [ + { + "index": 0, + "text": "The price for bubblegum decreased on thursday.", + "logprobs": null, + "finish_reason": "stop" + } + ] + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + }, + "default": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + } + }, + "x-ms-examples": { + "Create a completion.": { + "$ref": "./examples/completions.json" + } + }, + "summary": "Creates a completion for the provided prompt, parameters and chosen model." + } + }, + "/deployments/{deployment-id}/embeddings": { + "post": { + "description": "Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms.", + "operationId": "Embeddings_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "ada-search-index-v1" + }, + "description": "The deployment id of the model which was deployed." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "input": { + "description": "Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less.", + "oneOf": [ + { + "type": "string", + "default": "", + "example": "This is a test.", + "nullable": true + }, + { + "type": "array", + "minItems": 1, + "maxItems": 2048, + "items": { + "type": "string", + "minLength": 1, + "example": "This is a test.", + "nullable": false + } + } + ] + }, + "user": { + "description": "A unique identifier representing your end-user, which can help monitoring and detecting abuse.", + "type": "string", + "nullable": false + }, + "input_type": { + "description": "input type of embedding search to use", + "type": "string", + "example": "query" + }, + "encoding_format": { + "description": "The format to return the embeddings in. Can be either `float` or `base64`. Defaults to `float`.", + "type": "string", + "example": "float", + "nullable": true + }, + "dimensions": { + "description": "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.", + "type": "integer", + "example": 1, + "nullable": true + } + }, + "required": [ + "input" + ] + }, + "example": { + "input": "How are you?" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "object": { + "type": "string" + }, + "model": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "index": { + "type": "integer" + }, + "object": { + "type": "string" + }, + "embedding": { + "type": "array", + "items": { + "type": "number" + } + } + }, + "required": [ + "index", + "object", + "embedding" + ] + } + }, + "usage": { + "type": "object", + "properties": { + "prompt_tokens": { + "type": "integer" + }, + "total_tokens": { + "type": "integer" + } + }, + "required": [ + "prompt_tokens", + "total_tokens" + ] + } + }, + "required": [ + "object", + "model", + "data", + "usage" + ] + } + } + } + } + }, + "x-ms-examples": { + "Create a embeddings.": { + "$ref": "./examples/embeddings.json" + } + }, + "summary": "Get a vector representation of a given input that can be easily consumed by machine learning models and algorithms." + } + }, + "/deployments/{deployment-id}/chat/completions": { + "post": { + "description": "Creates a completion for the chat message", + "operationId": "ChatCompletions_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "description": "Deployment id of the model which was deployed." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createChatCompletionRequest" + }, + "example": { + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant" + }, + { + "role": "user", + "content": "How are you?" + } + ], + "max_tokens": 50 + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/createChatCompletionResponse" + }, + { + "$ref": "#/components/schemas/createChatCompletionStreamResponse" + } + ] + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + }, + "default": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + } + }, + "x-ms-examples": { + "Create a chat completion.": { + "$ref": "./examples/chat_completions.json" + }, + "Creates a completion based on Azure Search data and system-assigned managed identity.": { + "$ref": "./examples/chat_completions_azure_search_minimum.json" + }, + "Creates a completion based on Azure Search image vector data.": { + "$ref": "./examples/chat_completions_azure_search_image_vector.json" + }, + "Creates a completion based on Azure Search vector data, previous assistant message and user-assigned managed identity.": { + "$ref": "./examples/chat_completions_azure_search_advanced.json" + }, + "Creates a completion for the provided Azure Cosmos DB.": { + "$ref": "./examples/chat_completions_cosmos_db.json" + }, + "Creates a completion for the provided Mongo DB.": { + "$ref": "./examples/chat_completions_mongo_db.json" + }, + "Creates a completion for the provided Elasticsearch.": { + "$ref": "./examples/chat_completions_elasticsearch.json" + }, + "Creates a completion for the provided Pinecone resource.": { + "$ref": "./examples/chat_completions_pinecone.json" + } + }, + "summary": "Creates a completion for the chat message" + } + }, + "/deployments/{deployment-id}/audio/transcriptions": { + "post": { + "description": "Transcribes audio into the input language.", + "operationId": "Transcriptions_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "whisper", + "description": "Deployment id of the whisper model." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/createTranscriptionRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/audioResponse" + }, + { + "$ref": "#/components/schemas/audioVerboseResponse" + } + ] + } + }, + "text/plain": { + "schema": { + "type": "string", + "description": "Transcribed text in the output format (when response_format was one of text, vtt or srt)." + } + } + } + } + }, + "x-ms-examples": { + "Create an audio transcription with json response format.": { + "$ref": "./examples/audio_transcription_object.json" + }, + "Create an audio transcription with text response format.": { + "$ref": "./examples/audio_transcription_text.json" + } + }, + "summary": "Transcribes audio into the input language." + } + }, + "/deployments/{deployment-id}/audio/translations": { + "post": { + "description": "Transcribes and translates input audio into English text.", + "operationId": "Translations_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "whisper", + "description": "Deployment id of the whisper model which was deployed." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/createTranslationRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/audioResponse" + }, + { + "$ref": "#/components/schemas/audioVerboseResponse" + } + ] + } + }, + "text/plain": { + "schema": { + "type": "string", + "description": "Transcribed text in the output format (when response_format was one of text, vtt or srt)." + } + } + } + } + }, + "x-ms-examples": { + "Create an audio translation with json response format.": { + "$ref": "./examples/audio_translation_object.json" + }, + "Create an audio translation with text response format.": { + "$ref": "./examples/audio_translation_text.json" + } + }, + "summary": "Transcribes and translates input audio into English text." + } + }, + "/deployments/{deployment-id}/audio/speech": { + "post": { + "description": "Generates audio from the input text.", + "operationId": "Speech_Create", + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "tts-1", + "description": "Deployment id of the tts model which was deployed." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/createSpeechRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + }, + "x-ms-examples": { + "Create an audio from text with response format mp3.": { + "$ref": "./examples/audio_speech.json" + } + }, + "summary": "Generates audio from the input text." + } + }, + "/deployments/{deployment-id}/images/generations": { + "post": { + "description": "Generates a batch of images from a text caption on a given DALLE model deployment", + "operationId": "ImageGenerations_Create", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/imageGenerationsRequest" + } + } + } + }, + "parameters": [ + { + "in": "path", + "name": "deployment-id", + "required": true, + "schema": { + "type": "string", + "example": "dalle-deployment", + "description": "Deployment id of the dalle model which was deployed." + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/generateImagesResponse" + } + } + } + }, + "default": { + "description": "An error occurred.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dalleErrorResponse" + } + } + } + } + }, + "x-ms-examples": { + "Create an image.": { + "$ref": "./examples/image_generation.json" + } + }, + "summary": "Generates a batch of images from a text caption on a given DALLE model deployment" + } + }, + "/assistants": { + "get": { + "operationId": "List_Assistants", + "description": "Returns a list of assistants.", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListAssistantsOrder", + "modelAsString": true, + "values": [ + { + "value": "asc", + "description": "Order results in ascending order" + }, + { + "value": "desc", + "description": "Order results in descending order" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listAssistantsResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all assistants.": { + "$ref": "./examples/list_assistants.json" + } + }, + "summary": "Returns a list of assistants." + }, + "post": { + "operationId": "Create_Assistant", + "description": "Create an assistant with a model and instructions.", + "parameters": [ + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createAssistantRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/assistantObject" + } + } + } + } + }, + "x-ms-examples": { + "Create an assistant.": { + "$ref": "./examples/create_assistant.json" + } + }, + "summary": "Create an assistant with a model and instructions." + } + }, + "/assistants/{assistant_id}": { + "get": { + "operationId": "Get_Assistant", + "description": "Retrieves an assistant.", + "parameters": [ + { + "in": "path", + "name": "assistant_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the assistant to retrieve." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/assistantObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieves an assistant.": { + "$ref": "./examples/retrieve_assistant.json" + } + }, + "summary": "Retrieves an assistant." + }, + "post": { + "operationId": "Modify_Assistant", + "description": "Modifies an assistant.", + "parameters": [ + { + "in": "path", + "name": "assistant_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the assistant to modify." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/modifyAssistantRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/assistantObject" + } + } + } + } + }, + "x-ms-examples": { + "Modifies an assistant.": { + "$ref": "./examples/modify_assistant.json" + } + }, + "summary": "Modifies an assistant." + }, + "delete": { + "operationId": "Delete_Assistant", + "description": "Delete an assistant.", + "parameters": [ + { + "in": "path", + "name": "assistant_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the assistant to delete." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deleteAssistantResponse" + } + } + } + } + }, + "x-ms-examples": { + "Delete an assistant.": { + "$ref": "./examples/delete_assistant.json" + } + }, + "summary": "Delete an assistant." + } + }, + "/threads": { + "post": { + "operationId": "Create_Thread", + "description": "Create a thread.", + "parameters": [ + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createThreadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/threadObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a thread.": { + "$ref": "./examples/create_thread.json" + } + }, + "summary": "Create a thread." + } + }, + "/threads/{thread_id}": { + "get": { + "operationId": "Get_Thread", + "description": "Retrieves a thread.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to retrieve." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/threadObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a thread.": { + "$ref": "./examples/retrieve_thread.json" + } + }, + "summary": "Retrieves a thread." + }, + "post": { + "operationId": "Modify_Thread", + "description": "Modifies a thread.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to modify. Only the `metadata` can be modified." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/modifyThreadRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/threadObject" + } + } + } + } + }, + "x-ms-examples": { + "Modify a thread.": { + "$ref": "./examples/modify_thread.json" + } + }, + "summary": "Modifies a thread." + }, + "delete": { + "operationId": "Delete_Thread", + "description": "Delete a thread.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to delete." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deleteThreadResponse" + } + } + } + } + }, + "x-ms-examples": { + "Delete a thread.": { + "$ref": "./examples/delete_thread.json" + } + }, + "summary": "Delete a thread." + } + }, + "/threads/{thread_id}/messages": { + "get": { + "operationId": "List_Messages", + "description": "Returns a list of messages for a given thread.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) the messages belong to." + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListMessagesOrder", + "modelAsString": true, + "values": [ + { + "value": "asc", + "description": "Order results in ascending order" + }, + { + "value": "desc", + "description": "Order results in descending order" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "query", + "description": "Filter messages by the run ID that generated them.\n", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listMessagesResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all messages.": { + "$ref": "./examples/list_messages.json" + } + }, + "summary": "Returns a list of messages for a given thread." + }, + "post": { + "operationId": "Create_Message", + "description": "Create a message.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) to create a message for." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createMessageRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/messageObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a message.": { + "$ref": "./examples/create_message.json" + } + }, + "summary": "Create a message." + } + }, + "/threads/{thread_id}/messages/{message_id}": { + "get": { + "operationId": "Get_Message", + "description": "Retrieve a message.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) to which this message belongs." + }, + { + "in": "path", + "name": "message_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the message to retrieve." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/messageObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a message.": { + "$ref": "./examples/get_message.json" + } + }, + "summary": "Retrieve a message." + }, + "post": { + "operationId": "Modify_Message", + "description": "Modifies a message.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to which this message belongs." + }, + { + "in": "path", + "name": "message_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the message to modify." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/modifyMessageRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/messageObject" + } + } + } + } + }, + "x-ms-examples": { + "Modify a message.": { + "$ref": "./examples/modify_message.json" + } + }, + "summary": "Modifies a message." + } + }, + "/threads/runs": { + "post": { + "operationId": "Create_Thread_And_Run", + "description": "Create a thread and run it in one request.", + "parameters": [ + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createThreadAndRunRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a thread and run it.": { + "$ref": "./examples/create_thread_and_run.json" + } + }, + "summary": "Create a thread and run it in one request." + } + }, + "/threads/{thread_id}/runs": { + "get": { + "operationId": "List_Runs", + "description": "Returns a list of runs belonging to a thread.", + "parameters": [ + { + "name": "thread_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread the run belongs to." + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListRunsOrder", + "modelAsString": true, + "values": [ + { + "value": "asc", + "description": "Order results in ascending order" + }, + { + "value": "desc", + "description": "Order results in descending order" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listRunsResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all runs.": { + "$ref": "./examples/list_runs.json" + } + }, + "summary": "Returns a list of runs belonging to a thread." + }, + "post": { + "operationId": "Create_Run", + "description": "Create a run.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to run." + }, + { + "name": "include[]", + "in": "query", + "description": "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "step_details.tool_calls[*].file_search.results[*].content" + ], + "x-ms-enum": { + "name": "Include", + "modelAsString": true, + "values": [ + { + "value": "step_details.tool_calls[*].file_search.results[*].content" + } + ] + } + } + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createRunRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a run.": { + "$ref": "./examples/create_run.json" + } + }, + "summary": "Create a run." + } + }, + "/threads/{thread_id}/runs/{run_id}": { + "get": { + "operationId": "Get_Run", + "description": "Retrieves a run.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) that was run." + }, + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run to retrieve." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a run.": { + "$ref": "./examples/get_run.json" + } + }, + "summary": "Retrieves a run." + }, + "post": { + "operationId": "Modify_Run", + "description": "Modifies a run.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) that was run." + }, + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run to modify." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/modifyRunRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Modify a run.": { + "$ref": "./examples/modify_run.json" + } + }, + "summary": "Modifies a run." + } + }, + "/threads/{thread_id}/runs/{run_id}/submit_tool_outputs": { + "post": { + "operationId": "Submit_Tool_Outputs_To_Run", + "description": "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the [thread](/docs/api-reference/threads) to which this run belongs." + }, + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run that requires the tool output submission." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/submitToolOutputsRunRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Submit tool outputs to a run.": { + "$ref": "./examples/submit_tool_outputs_to_run.json" + } + }, + "summary": "When a run has the `status: \"requires_action\"` and `required_action." + } + }, + "/threads/{thread_id}/runs/{run_id}/cancel": { + "post": { + "operationId": "Cancel_Run", + "description": "Cancels a run that is `in_progress`.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to which this run belongs." + }, + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run to cancel." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runObject" + } + } + } + } + }, + "x-ms-examples": { + "Cancel a run.": { + "$ref": "./examples/cancel_run.json" + } + }, + "summary": "Cancels a run that is `in_progress`." + } + }, + "/threads/{thread_id}/runs/{run_id}/steps": { + "get": { + "operationId": "List_Run_Steps", + "description": "Returns a list of run steps belonging to a run.", + "parameters": [ + { + "name": "thread_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread the run and run steps belong to." + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run the run steps belong to." + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListRunStepsOrder", + "modelAsString": true, + "values": [ + { + "value": "asc", + "description": "Order results in ascending order" + }, + { + "value": "desc", + "description": "Order results in descending order" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + }, + { + "name": "include[]", + "in": "query", + "description": "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "step_details.tool_calls[*].file_search.results[*].content" + ], + "x-ms-enum": { + "name": "Include", + "modelAsString": true, + "values": [ + { + "value": "step_details.tool_calls[*].file_search.results[*].content" + } + ] + } + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listRunStepsResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all run steps.": { + "$ref": "./examples/list_run_steps.json" + } + }, + "summary": "Returns a list of run steps belonging to a run." + } + }, + "/threads/{thread_id}/runs/{run_id}/steps/{step_id}": { + "get": { + "operationId": "Get_Run_Step", + "description": "Retrieves a run step.", + "parameters": [ + { + "in": "path", + "name": "thread_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the thread to which the run and run step belongs." + }, + { + "in": "path", + "name": "run_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run to which the run step belongs." + }, + { + "in": "path", + "name": "step_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the run step to retrieve." + }, + { + "name": "include[]", + "in": "query", + "description": "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n", + "schema": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "step_details.tool_calls[*].file_search.results[*].content" + ], + "x-ms-enum": { + "name": "Include", + "modelAsString": true, + "values": [ + { + "value": "step_details.tool_calls[*].file_search.results[*].content" + } + ] + } + } + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "description": "api version", + "default": "2024-05-01-preview" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/runStepObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a run step.": { + "$ref": "./examples/get_run_step.json" + } + }, + "summary": "Retrieves a run step." + } + }, + "/vector_stores": { + "get": { + "operationId": "List_Vector_Stores", + "description": "Returns a list of vector stores.", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListVectorStoresOrder", + "modelAsString": true, + "values": [ + { + "value": "asc" + }, + { + "value": "desc" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listVectorStoresResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all vector stores.": { + "$ref": "./examples/list_vector_stores.json" + } + }, + "summary": "Returns a list of vector stores." + }, + "post": { + "operationId": "Create_Vector_Store", + "description": "Create a vector store.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createVectorStoreRequest" + } + } + } + }, + "parameters": [ + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a vector store.": { + "$ref": "./examples/create_vector_store.json" + } + }, + "summary": "Create a vector store." + } + }, + "/vector_stores/{vector_store_id}": { + "get": { + "operationId": "Get_Vector_Store", + "description": "Retrieves a vector store.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the vector store to retrieve." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a vector store.": { + "$ref": "./examples/retrieve_vector_store.json" + } + }, + "summary": "Retrieves a vector store." + }, + "post": { + "operationId": "Modify_Vector_Store", + "description": "Modifies a vector store.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the vector store to modify." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/updateVectorStoreRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreObject" + } + } + } + } + }, + "x-ms-examples": { + "Modify a vector store.": { + "$ref": "./examples/modify_vector_store.json" + } + }, + "summary": "Modifies a vector store." + }, + "delete": { + "operationId": "Delete_Vector_Store", + "description": "Delete a vector store.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the vector store to delete." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deleteVectorStoreResponse" + } + } + } + } + }, + "x-ms-examples": { + "Delete a vector store.": { + "$ref": "./examples/delete_vector_store.json" + } + }, + "summary": "Delete a vector store." + } + }, + "/vector_stores/{vector_store_id}/files": { + "get": { + "operationId": "List_Vector_Store_Files", + "description": "Returns a list of vector store files.", + "parameters": [ + { + "name": "vector_store_id", + "in": "path", + "description": "The ID of the vector store that the files belong to.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListVectorStoreFilesOrder", + "modelAsString": true, + "values": [ + { + "value": "asc" + }, + { + "value": "desc" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "filter", + "in": "query", + "description": "Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.", + "schema": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "failed", + "cancelled" + ], + "x-ms-enum": { + "name": "ListVectorStoreFilesFilter", + "modelAsString": true, + "values": [ + { + "value": "in_progress" + }, + { + "value": "completed" + }, + { + "value": "failed" + }, + { + "value": "cancelled" + } + ] + } + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listVectorStoreFilesResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all vector store files.": { + "$ref": "./examples/list_vector_store_files.json" + } + }, + "summary": "Returns a list of vector store files." + }, + "post": { + "operationId": "Create_Vector_Store_File", + "description": "Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store for which to create a File.\n" + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createVectorStoreFileRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a vector store file.": { + "$ref": "./examples/create_vector_store_file.json" + } + }, + "summary": "Create a vector store file by attaching a File to a vector store." + } + }, + "/vector_stores/{vector_store_id}/files/{file_id}": { + "get": { + "operationId": "Get_Vector_Store_File", + "description": "Retrieves a vector store file.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store that the file belongs to." + }, + { + "in": "path", + "name": "file_id", + "required": true, + "schema": { + "type": "string", + "example": "file-abc123" + }, + "description": "The ID of the file being retrieved." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a vector store file.": { + "$ref": "./examples/retrieve_vector_store_file.json" + } + }, + "summary": "Retrieves a vector store file." + }, + "delete": { + "operationId": "Delete_Vector_Store_File", + "description": "Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the vector store that the file belongs to." + }, + { + "in": "path", + "name": "file_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the file to delete." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deleteVectorStoreFileResponse" + } + } + } + } + }, + "x-ms-examples": { + "Delete a vector store file.": { + "$ref": "./examples/delete_vector_store_file.json" + } + }, + "summary": "Delete a vector store file." + }, + "post": { + "operationId": "Update_Vector_Store_File_Attributes", + "description": "Update attributes on a vector store file.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store the file belongs to." + }, + { + "in": "path", + "name": "file_id", + "required": true, + "schema": { + "type": "string", + "example": "file-abc123" + }, + "description": "The ID of the file to update attributes." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateVectorStoreFileAttributesRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileObject" + } + } + } + } + }, + "summary": "Update attributes on a vector store file." + } + }, + "/vector_stores/{vector_store_id}/files/{file_id}/content": { + "get": { + "operationId": "Retrieve_Vector_Store_File_Content", + "description": "Retrieve the parsed contents of a vector store file.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store." + }, + { + "in": "path", + "name": "file_id", + "required": true, + "schema": { + "type": "string", + "example": "file-abc123" + }, + "description": "The ID of the file within the vector store." + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VectorStoreFileContentResponse" + } + } + } + } + }, + "summary": "Retrieve the parsed contents of a vector store file." + } + }, + "/vector_stores/{vector_store_id}/search": { + "post": { + "operationId": "Search_Vector_Store", + "description": "Search a vector store for relevant chunks based on a query and file attributes filter.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store to search." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VectorStoreSearchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VectorStoreSearchResultsPage" + } + } + } + } + }, + "summary": "Search a vector store for relevant chunks based on a query and file attributes filter." + } + }, + "/vector_stores/{vector_store_id}/file_batches": { + "post": { + "operationId": "Create_Vector_Store_File_Batch", + "description": "Create a vector store file batch.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store for which to create a File Batch.\n" + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createVectorStoreFileBatchRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileBatchObject" + } + } + } + } + }, + "x-ms-examples": { + "Create a vector store file batch.": { + "$ref": "./examples/create_vector_store_file_batch.json" + } + }, + "summary": "Create a vector store file batch." + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}": { + "get": { + "operationId": "Get_Vector_Store_File_Batch", + "description": "Retrieves a vector store file batch.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_abc123" + }, + "description": "The ID of the vector store that the file batch belongs to." + }, + { + "in": "path", + "name": "batch_id", + "required": true, + "schema": { + "type": "string", + "example": "vs_b_abc123" + }, + "description": "The ID of the file batch being retrieved." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileBatchObject" + } + } + } + } + }, + "x-ms-examples": { + "Retrieve a vector store file batch.": { + "$ref": "./examples/retrieve_vector_store_file_batch.json" + } + }, + "summary": "Retrieves a vector store file batch." + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel": { + "post": { + "operationId": "Cancel_Vector_Store_File_Batch", + "description": "Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible.", + "parameters": [ + { + "in": "path", + "name": "vector_store_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the vector store that the file batch belongs to." + }, + { + "in": "path", + "name": "batch_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the file batch to cancel." + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/vectorStoreFileBatchObject" + } + } + } + } + }, + "x-ms-examples": { + "Cancel a vector store file batch.": { + "$ref": "./examples/cancel_vector_store_file_batch.json" + } + }, + "summary": "Cancel a vector store file batch." + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files": { + "get": { + "operationId": "List_Vector_Store_File_Batch_Files", + "description": "Returns a list of vector store files in a batch.", + "parameters": [ + { + "name": "vector_store_id", + "in": "path", + "description": "The ID of the vector store that the files belong to.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "batch_id", + "in": "path", + "description": "The ID of the file batch that the files belong to.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "name": "order", + "in": "query", + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n", + "schema": { + "type": "string", + "default": "desc", + "enum": [ + "asc", + "desc" + ], + "x-ms-enum": { + "name": "ListVectorStoreFilesOrder", + "modelAsString": true, + "values": [ + { + "value": "asc" + }, + { + "value": "desc" + } + ] + } + } + }, + { + "name": "after", + "in": "query", + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "before", + "in": "query", + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n", + "schema": { + "type": "string" + } + }, + { + "name": "filter", + "in": "query", + "description": "Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.", + "schema": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "failed", + "cancelled" + ], + "x-ms-enum": { + "name": "ListVectorStoreFilesFilter", + "modelAsString": true, + "values": [ + { + "value": "in_progress" + }, + { + "value": "completed" + }, + { + "value": "failed" + }, + { + "value": "cancelled" + } + ] + } + } + }, + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2024-05-01-preview", + "default": "2024-05-01-preview" + }, + "description": "api version" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/listVectorStoreFilesResponse" + } + } + } + } + }, + "x-ms-examples": { + "List all vector store files in a batch.": { + "$ref": "./examples/list_vector_store_files.json" + } + }, + "summary": "Returns a list of vector store files in a batch." + } + }, + "/responses": { + "post": { + "operationId": "Create_Response", + "description": "Creates a model response. Provide [text](/docs/guides/text) or\n[image](/docs/guides/images) inputs to generate [text](/docs/guides/text)\nor [JSON](/docs/guides/structured-outputs) outputs. Have the model call\nyour own [custom code](/docs/guides/function-calling) or use built-in\n[tools](/docs/guides/tools) like [web search](/docs/guides/tools-web-search)\nor [file search](/docs/guides/tools-file-search) to use your own data\nas input for the model's response.\n", + "parameters": [ + { + "in": "query", + "name": "api-version", + "required": true, + "schema": { + "type": "string", + "example": "2025-03-01-preview", + "description": "api version", + "default": "2025-03-01-preview" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/createResponse" + }, + "example": { + "model": "gpt-4o", + "input": "How are you?", + "stream": false + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response" + } + }, + "text/event-stream": { + "schema": { + "$ref": "#/components/schemas/responseStreamEvent" + } + } + } + }, + "default": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "Creates a model response." + } + }, + "/responses/{response_id}": { + "get": { + "operationId": "Get_Response", + "description": "Retrieves a model response with the given ID.\n", + "parameters": [ + { + "in": "path", + "name": "response_id", + "required": true, + "schema": { + "type": "string", + "example": "resp_677efb5139a88190b512bc3fef8e535d" + }, + "description": "The ID of the response to retrieve." + }, + { + "in": "query", + "name": "include", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/includable" + } + }, + "description": "Additional fields to include in the response. See the `include`\nparameter for Response creation above for more information.\n" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/response" + } + } + } + }, + "default": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "Retrieves a model response with the given ID." + }, + "delete": { + "operationId": "Delete_Response", + "description": "Deletes a model response with the given ID.\n", + "parameters": [ + { + "in": "path", + "name": "response_id", + "required": true, + "schema": { + "type": "string", + "example": "resp_677efb5139a88190b512bc3fef8e535d" + }, + "description": "The ID of the response to delete." + } + ], + "responses": { + "200": { + "description": "OK" + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "default": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorResponse" + } + } + }, + "headers": { + "apim-request-id": { + "description": "Request ID for troubleshooting purposes", + "schema": { + "type": "string" + } + } + } + } + }, + "summary": "Deletes a model response with the given ID." + } + }, + "/responses/{response_id}/input_items": { + "get": { + "operationId": "Get_Input_Items", + "description": "Returns a list of input items for a given response.", + "parameters": [ + { + "in": "path", + "name": "response_id", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the response to retrieve input items for." + }, + { + "name": "limit", + "in": "query", + "description": "A limit on the number of objects to be returned. Limit can range between\n1 and 100, and the default is 20.\n", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + }, + { + "in": "query", + "name": "order", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "description": "The order to return the input items in. Default is `asc`.\n- `asc`: Return the input items in ascending order.\n- `desc`: Return the input items in descending order.\n" + }, + { + "in": "query", + "name": "after", + "schema": { + "type": "string" + }, + "description": "An item ID to list items after, used in pagination.\n" + }, + { + "in": "query", + "name": "before", + "schema": { + "type": "string" + }, + "description": "An item ID to list items before, used in pagination.\n" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/responseItemList" + } + } + } + } + }, + "summary": "Returns a list of input items for a given response." + } + } + }, + "components": { + "schemas": { + "errorResponse": { + "type": "object", + "properties": { + "error": { + "$ref": "#/components/schemas/error" + } + } + }, + "errorBase": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "error": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/errorBase" + } + ], + "properties": { + "param": { + "type": "string" + }, + "type": { + "type": "string" + }, + "inner_error": { + "$ref": "#/components/schemas/innerError" + } + } + }, + "innerError": { + "description": "Inner error with additional details.", + "type": "object", + "properties": { + "code": { + "$ref": "#/components/schemas/innerErrorCode" + }, + "content_filter_results": { + "$ref": "#/components/schemas/contentFilterPromptResults" + } + } + }, + "innerErrorCode": { + "description": "Error codes for the inner error object.", + "enum": [ + "ResponsibleAIPolicyViolation" + ], + "type": "string", + "x-ms-enum": { + "name": "InnerErrorCode", + "modelAsString": true, + "values": [ + { + "value": "ResponsibleAIPolicyViolation", + "description": "The prompt violated one of more content filter rules." + } + ] + } + }, + "dalleErrorResponse": { + "type": "object", + "properties": { + "error": { + "$ref": "#/components/schemas/dalleError" + } + } + }, + "dalleError": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/errorBase" + } + ], + "properties": { + "param": { + "type": "string" + }, + "type": { + "type": "string" + }, + "inner_error": { + "$ref": "#/components/schemas/dalleInnerError" + } + } + }, + "dalleInnerError": { + "description": "Inner error with additional details.", + "type": "object", + "properties": { + "code": { + "$ref": "#/components/schemas/innerErrorCode" + }, + "content_filter_results": { + "$ref": "#/components/schemas/dalleFilterResults" + }, + "revised_prompt": { + "type": "string", + "description": "The prompt that was used to generate the image, if there was any revision to the prompt." + } + } + }, + "contentFilterCompletionTextSpan": { + "type": "object", + "description": "Describes a span within generated completion text. Offset 0 is the first UTF32 code point of the completion text.", + "properties": { + "completion_start_offset": { + "description": "Offset of the UTF32 code point which begins the span.", + "type": "integer" + }, + "completion_end_offset": { + "description": "Offset of the first UTF32 code point which is excluded from the span. This field is always equal to completion_start_offset for empty spans. This field is always larger than completion_start_offset for non-empty spans.", + "type": "integer" + } + }, + "required": [ + "completion_start_offset", + "completion_end_offset" + ] + }, + "contentFilterResultBase": { + "type": "object", + "properties": { + "filtered": { + "type": "boolean" + } + }, + "required": [ + "filtered" + ] + }, + "contentFilterSeverityResult": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultBase" + }, + { + "properties": { + "severity": { + "type": "string", + "enum": [ + "safe", + "low", + "medium", + "high" + ], + "x-ms-enum": { + "name": "ContentFilterSeverity", + "modelAsString": true, + "values": [ + { + "value": "safe", + "description": "General content or related content in generic or non-harmful contexts." + }, + { + "value": "low", + "description": "Harmful content at a low intensity and risk level." + }, + { + "value": "medium", + "description": "Harmful content at a medium intensity and risk level." + }, + { + "value": "high", + "description": "Harmful content at a high intensity and risk level." + } + ] + } + } + } + } + ], + "required": [ + "severity", + "filtered" + ] + }, + "contentFilterDetectedResult": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultBase" + }, + { + "properties": { + "detected": { + "type": "boolean" + } + } + } + ], + "required": [ + "detected", + "filtered" + ] + }, + "contentFilterDetectedWithCitationResult": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + { + "properties": { + "citation": { + "type": "object", + "properties": { + "URL": { + "type": "string" + }, + "license": { + "type": "string" + } + } + } + } + } + ], + "required": [ + "detected", + "filtered" + ] + }, + "contentFilterDetectedWithCompletionTextSpansResult": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + { + "properties": { + "details": { + "type": "array", + "items": { + "$ref": "#/components/schemas/contentFilterCompletionTextSpan" + } + } + } + } + ], + "required": [ + "detected", + "filtered", + "details" + ] + }, + "contentFilterIdResult": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultBase" + }, + { + "properties": { + "id": { + "type": "string" + } + } + } + ], + "required": [ + "id", + "filtered" + ] + }, + "contentFilterResultsBase": { + "type": "object", + "description": "Information about the content filtering results.", + "properties": { + "sexual": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "violence": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "hate": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "self_harm": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "profanity": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + "custom_blocklists": { + "$ref": "#/components/schemas/contentFilterDetailedResults" + }, + "error": { + "$ref": "#/components/schemas/errorBase" + } + } + }, + "contentFilterPromptResults": { + "type": "object", + "description": "Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultsBase" + }, + { + "properties": { + "jailbreak": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + "indirect_attack": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + } + } + } + ] + }, + "contentFilterChoiceResults": { + "type": "object", + "description": "Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about third party text and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultsBase" + }, + { + "properties": { + "protected_material_text": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + } + } + }, + { + "properties": { + "protected_material_code": { + "$ref": "#/components/schemas/contentFilterDetectedWithCitationResult" + } + } + }, + { + "properties": { + "ungrounded_material": { + "$ref": "#/components/schemas/contentFilterDetectedWithCompletionTextSpansResult" + } + } + } + ] + }, + "contentFilterDetailedResults": { + "type": "object", + "description": "Content filtering results with a detail of content filter ids for the filtered segments.", + "allOf": [ + { + "$ref": "#/components/schemas/contentFilterResultBase" + }, + { + "properties": { + "details": { + "items": { + "$ref": "#/components/schemas/contentFilterIdResult" + }, + "type": "array" + } + } + } + ], + "required": [ + "filtered", + "details" + ] + }, + "promptFilterResult": { + "type": "object", + "description": "Content filtering results for a single prompt in the request.", + "properties": { + "prompt_index": { + "type": "integer" + }, + "content_filter_results": { + "$ref": "#/components/schemas/contentFilterPromptResults" + } + } + }, + "promptFilterResults": { + "type": "array", + "description": "Content filtering results for zero or more prompts in the request. In a streaming request, results for different prompts may arrive at different times or in different orders.", + "items": { + "$ref": "#/components/schemas/promptFilterResult" + } + }, + "dalleContentFilterResults": { + "type": "object", + "description": "Information about the content filtering results.", + "properties": { + "sexual": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "violence": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "hate": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + }, + "self_harm": { + "$ref": "#/components/schemas/contentFilterSeverityResult" + } + } + }, + "dalleFilterResults": { + "type": "object", + "description": "Information about the content filtering category (hate, sexual, violence, self_harm), if it has been detected, as well as the severity level (very_low, low, medium, high-scale that determines the intensity and risk level of harmful content) and if it has been filtered or not. Information about jailbreak content and profanity, if it has been detected, and if it has been filtered or not. And information about customer block list, if it has been filtered and its id.", + "allOf": [ + { + "$ref": "#/components/schemas/dalleContentFilterResults" + }, + { + "properties": { + "profanity": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + "jailbreak": { + "$ref": "#/components/schemas/contentFilterDetectedResult" + }, + "custom_blocklists": { + "$ref": "#/components/schemas/contentFilterDetailedResults" + } + } + } + ] + }, + "chatCompletionsRequestCommon": { + "type": "object", + "properties": { + "temperature": { + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.", + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true + }, + "top_p": { + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\nWe generally recommend altering this or `temperature` but not both.", + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true + }, + "stream": { + "description": "If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message.", + "type": "boolean", + "nullable": true, + "default": false + }, + "stop": { + "description": "Up to 4 sequences where the API will stop generating further tokens.", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "array", + "items": { + "type": "string", + "nullable": false + }, + "minItems": 1, + "maxItems": 4, + "description": "Array minimum size of 1 and maximum of 4" + } + ], + "default": null + }, + "max_tokens": { + "description": "The maximum number of tokens allowed for the generated answer. By default, the number of tokens the model can return will be (4096 - prompt tokens). This is not compatible with o1 series models.", + "type": "integer", + "default": 4096 + }, + "max_completion_tokens": { + "description": "An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. This is only supported in o1 series models. Will expand the support to other models in future API release.", + "type": "integer", + "nullable": true + }, + "presence_penalty": { + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2 + }, + "frequency_penalty": { + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2 + }, + "logit_bias": { + "description": "Modify the likelihood of specified tokens appearing in the completion. Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.", + "type": "object", + "nullable": true + }, + "store": { + "description": "Whether or not to store the output of this chat completion request for use in our model distillation or evaluation products.", + "type": "boolean", + "nullable": true + }, + "metadata": { + "description": "Developer-defined tags and values used for filtering completions in the stored completions dashboard.", + "type": "object", + "nullable": true, + "additionalProperties": { + "type": "string" + } + }, + "user": { + "description": "A unique identifier representing your end-user, which can help Azure OpenAI to monitor and detect abuse.", + "type": "string", + "example": "user-1234", + "nullable": false + } + } + }, + "createCompletionRequest": { + "type": "object", + "properties": { + "prompt": { + "description": "The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.\n\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.\n", + "default": "<|endoftext|>", + "nullable": true, + "oneOf": [ + { + "type": "string", + "default": "", + "example": "This is a test." + }, + { + "type": "array", + "items": { + "type": "string", + "default": "", + "example": "This is a test." + } + } + ] + }, + "best_of": { + "type": "integer", + "default": 1, + "minimum": 0, + "maximum": 20, + "nullable": true, + "description": "Generates `best_of` completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\n\nWhen used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + }, + "echo": { + "type": "boolean", + "default": false, + "nullable": true, + "description": "Echo back the prompt in addition to the completion\n" + }, + "frequency_penalty": { + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2, + "nullable": true, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n" + }, + "logit_bias": { + "type": "object", + "x-oaiTypeLabel": "map", + "default": null, + "nullable": true, + "additionalProperties": { + "type": "integer" + }, + "description": "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](https://platform.openai.com/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n\nAs an example, you can pass `{\"50256\": -100}` to prevent the <|endoftext|> token from being generated.\n" + }, + "logprobs": { + "type": "integer", + "minimum": 0, + "maximum": 5, + "default": null, + "nullable": true, + "description": "Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.\n\nThe maximum value for `logprobs` is 5.\n" + }, + "max_tokens": { + "type": "integer", + "minimum": 0, + "default": 16, + "example": 16, + "nullable": true, + "description": "The maximum number of [tokens](https://platform.openai.com/tokenizer) that can be generated in the completion.\n\nThe token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n" + }, + "n": { + "type": "integer", + "minimum": 1, + "maximum": 128, + "default": 1, + "example": 1, + "nullable": true, + "description": "How many completions to generate for each prompt.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + }, + "presence_penalty": { + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2, + "nullable": true, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n" + }, + "seed": { + "type": "integer", + "minimum": -9223372036854775808, + "maximum": 9223372036854775807, + "nullable": true, + "description": "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\n\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + }, + "stop": { + "description": "Up to 4 sequences where the API will stop generating further tokens. The returned text will not contain the stop sequence.\n", + "default": null, + "nullable": true, + "oneOf": [ + { + "type": "string", + "default": "<|endoftext|>", + "example": "\n", + "nullable": true + }, + { + "type": "array", + "minItems": 1, + "maxItems": 4, + "items": { + "type": "string", + "example": "[\"\\n\"]" + } + } + ] + }, + "stream": { + "description": "Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n", + "type": "boolean", + "nullable": true, + "default": false + }, + "suffix": { + "description": "The suffix that comes after a completion of inserted text.\n\nThis parameter is only supported for `gpt-3.5-turbo-instruct`.\n", + "default": null, + "nullable": true, + "type": "string", + "example": "test." + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }, + "user": { + "type": "string", + "example": "user-1234", + "description": "A unique identifier representing your end-user, which can help to monitor and detect abuse.\n" + } + }, + "required": [ + "prompt" + ] + }, + "createCompletionResponse": { + "type": "object", + "description": "Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).\n", + "properties": { + "id": { + "type": "string", + "description": "A unique identifier for the completion." + }, + "choices": { + "type": "array", + "description": "The list of completion choices the model generated for the input prompt.", + "items": { + "type": "object", + "required": [ + "finish_reason", + "index", + "logprobs", + "text" + ], + "properties": { + "finish_reason": { + "type": "string", + "description": "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\nor `content_filter` if content was omitted due to a flag from our content filters.\n", + "enum": [ + "stop", + "length", + "content_filter" + ] + }, + "index": { + "type": "integer" + }, + "logprobs": { + "type": "object", + "nullable": true, + "properties": { + "text_offset": { + "type": "array", + "items": { + "type": "integer" + } + }, + "token_logprobs": { + "type": "array", + "items": { + "type": "number" + } + }, + "tokens": { + "type": "array", + "items": { + "type": "string" + } + }, + "top_logprobs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "number" + } + } + } + } + }, + "text": { + "type": "string" + }, + "content_filter_results": { + "$ref": "#/components/schemas/contentFilterChoiceResults" + } + } + } + }, + "created": { + "type": "integer", + "description": "The Unix timestamp (in seconds) of when the completion was created." + }, + "model": { + "type": "string", + "description": "The model used for completion." + }, + "prompt_filter_results": { + "$ref": "#/components/schemas/promptFilterResults" + }, + "system_fingerprint": { + "type": "string", + "description": "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }, + "object": { + "type": "string", + "description": "The object type, which is always \"text_completion\"", + "enum": [ + "text_completion" + ] + }, + "usage": { + "$ref": "#/components/schemas/completionUsage" + } + }, + "required": [ + "id", + "object", + "created", + "model", + "choices" + ] + }, + "createChatCompletionRequest": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/chatCompletionsRequestCommon" + }, + { + "properties": { + "messages": { + "description": "A list of messages comprising the conversation so far. [Example Python code](https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb).", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/chatCompletionRequestMessage" + } + }, + "data_sources": { + "type": "array", + "description": " The configuration entries for Azure OpenAI chat extensions that use them.\n This additional specification is only compatible with Azure OpenAI.", + "items": { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + } + }, + "reasoning_effort": { + "type": "string", + "description": "**o1 models only** \n\n Constrains effort on reasoning for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n\nCurrently supported values are `low`, `medium`, and `high`. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.", + "enum": [ + "low", + "medium", + "high" + ], + "default": "medium" + }, + "frequency_penalty": { + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2, + "nullable": true, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n" + }, + "logit_bias": { + "type": "object", + "default": null, + "nullable": true, + "additionalProperties": { + "type": "integer" + }, + "description": "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n" + }, + "logprobs": { + "description": "Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the `content` of `message`.", + "type": "boolean", + "default": false, + "nullable": true + }, + "top_logprobs": { + "description": "An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. `logprobs` must be set to `true` if this parameter is used.", + "type": "integer", + "minimum": 0, + "maximum": 20, + "nullable": true + }, + "max_tokens": { + "description": "The maximum number of [tokens](/tokenizer) that can be generated in the chat completion.\n\nThe total length of input tokens and generated tokens is limited by the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n", + "type": "integer", + "nullable": true + }, + "n": { + "type": "integer", + "minimum": 1, + "maximum": 128, + "default": 1, + "example": 1, + "nullable": true, + "description": "How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs." + }, + "presence_penalty": { + "type": "number", + "default": 0, + "minimum": -2, + "maximum": 2, + "nullable": true, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n" + }, + "modalities": { + "$ref": "#/components/schemas/ChatCompletionModalities" + }, + "prediction": { + "nullable": true, + "x-oaiExpandable": true, + "description": "Configuration for a [Predicted Output](/docs/guides/predicted-outputs), which can greatly improve response times when large parts of the model response are known ahead of time. This is most common when you are regenerating a file with only minor changes to most of the content.", + "oneOf": [ + { + "$ref": "#/components/schemas/PredictionContent" + } + ] + }, + "audio": { + "type": "object", + "description": "Parameters for audio output. Required when audio output is requested with\n`modalities: [\"audio\"]`. [Learn more](/docs/guides/audio).\n", + "required": [ + "voice", + "format" + ], + "x-oaiExpandable": true, + "properties": { + "voice": { + "type": "string", + "enum": [ + "alloy", + "echo", + "fable", + "onyx", + "nova", + "shimmer" + ], + "description": "Specifies the voice type. Supported voices are `alloy`, `echo`, \n`fable`, `onyx`, `nova`, and `shimmer`.\n" + }, + "format": { + "type": "string", + "enum": [ + "wav", + "mp3", + "flac", + "opus", + "pcm16" + ], + "description": "Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,\n`opus`, or `pcm16`. \n" + } + } + }, + "parallel_tool_calls": { + "$ref": "#/components/schemas/ParallelToolCalls", + "nullable": true + }, + "response_format": { + "description": "An object specifying the format that the model must output. Compatible with [GPT-4o](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4o mini](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models), [GPT-4 Turbo](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4-and-gpt-4-turbo-models) and all [GPT-3.5](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-35) Turbo models newer than `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which guarantees the model will match your supplied JSON schema.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which guarantees the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.\n", + "oneOf": [ + { + "$ref": "#/components/schemas/ResponseFormatText" + }, + { + "$ref": "#/components/schemas/ResponseFormatJsonObject" + }, + { + "$ref": "#/components/schemas/ResponseFormatJsonSchema" + } + ], + "x-oaiExpandable": true + }, + "seed": { + "type": "integer", + "minimum": -9223372036854775808, + "maximum": 9223372036854775807, + "nullable": true, + "description": "This feature is in Beta.\nIf specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + }, + "stop": { + "description": "Up to 4 sequences where the API will stop generating further tokens.\n", + "default": null, + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "array", + "minItems": 1, + "maxItems": 4, + "items": { + "type": "string" + } + } + ] + }, + "stream": { + "description": "If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n", + "type": "boolean", + "nullable": true, + "default": false + }, + "stream_options": { + "$ref": "#/components/schemas/chatCompletionStreamOptions", + "nullable": true + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }, + "tools": { + "type": "array", + "description": "A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.\n", + "items": { + "$ref": "#/components/schemas/chatCompletionTool" + } + }, + "tool_choice": { + "$ref": "#/components/schemas/chatCompletionToolChoiceOption" + }, + "function_call": { + "deprecated": true, + "description": "Deprecated in favor of `tool_choice`.\n\nControls which (if any) function is called by the model.\n`none` means the model will not call a function and instead generates a message.\n`auto` means the model can pick between generating a message or calling a function.\nSpecifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n\n`none` is the default when no functions are present. `auto` is the default if functions are present.\n", + "oneOf": [ + { + "type": "string", + "description": "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n", + "enum": [ + "none", + "auto" + ] + }, + { + "$ref": "#/components/schemas/chatCompletionFunctionCallOption" + } + ], + "x-oaiExpandable": true + }, + "functions": { + "deprecated": true, + "description": "Deprecated in favor of `tools`.\n\nA list of functions the model may generate JSON inputs for.\n", + "type": "array", + "minItems": 1, + "maxItems": 128, + "items": { + "$ref": "#/components/schemas/chatCompletionFunctions" + } + }, + "user": { + "type": "string", + "example": "user-1234", + "description": "A unique identifier representing your end-user, which can help to monitor and detect abuse.\n" + }, + "user_security_context": { + "$ref": "#/components/schemas/userSecurityContext" + } + }, + "required": [ + "messages" + ] + } + ] + }, + "userSecurityContext": { + "type": "object", + "description": "User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. These fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. [Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud.", + "properties": { + "application_name": { + "type": "string", + "description": "The name of the application. Sensitive personal information should not be included in this field." + }, + "end_user_id": { + "type": "string", + "format": "uuid", + "description": "This identifier is the Microsoft Entra ID (formerly Azure Active Directory) user object ID used to authenticate end-users within the generative AI application. Sensitive personal information should not be included in this field." + }, + "end_user_tenant_id": { + "type": "string", + "format": "uuid", + "description": "The Microsoft 365 tenant ID the end user belongs to. It's required when the generative AI application is multi tenant." + }, + "source_ip": { + "type": "string", + "description": "Captures the original client's IP address, accepting both IPv4 and IPv6 formats." + } + } + }, + "chatCompletionFunctions": { + "type": "object", + "deprecated": true, + "properties": { + "description": { + "type": "string", + "description": "A description of what the function does, used by the model to choose when and how to call the function." + }, + "name": { + "type": "string", + "description": "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "parameters": { + "$ref": "#/components/schemas/FunctionParameters" + } + }, + "required": [ + "name" + ] + }, + "chatCompletionFunctionCallOption": { + "type": "object", + "description": "Specifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "name" + ] + }, + "chatCompletionFunctionParameters": { + "type": "object", + "description": "The parameters the functions accepts, described as a JSON Schema object. See the [guide/](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.", + "additionalProperties": true + }, + "chatCompletionRequestMessage": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChatCompletionRequestDeveloperMessage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestSystemMessage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestUserMessage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestAssistantMessage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestToolMessage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestFunctionMessage" + } + ] + }, + "ChatCompletionRequestDeveloperMessage": { + "type": "object", + "title": "Developer message", + "description": "Developer-provided instructions that the model should follow, regardless of messages sent by the user.\nWith o1 models and newer, `developer` messages replace the previous `system` messages.", + "properties": { + "content": { + "description": "The contents of the developer message.", + "oneOf": [ + { + "type": "string", + "description": "The contents of the developer message.", + "title": "Text content" + }, + { + "type": "array", + "description": "An array of content parts with a defined type. For developer messages, only type `text` is supported.", + "title": "Array of content parts", + "items": { + "$ref": "#/components/schemas/chatCompletionRequestDeveloperMessageContentPart" + }, + "minItems": 1 + } + ] + }, + "role": { + "type": "string", + "enum": [ + "developer" + ], + "description": "The role of the messages author, in this case `developer`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "required": [ + "content", + "role" + ] + }, + "chatCompletionRequestSystemMessage": { + "type": "object", + "title": "System message", + "properties": { + "content": { + "description": "The contents of the system message.", + "oneOf": [ + { + "type": "string", + "description": "The contents of the system message.", + "title": "Text content" + }, + { + "type": "array", + "description": "An array of content parts with a defined type. For system messages, only type `text` is supported.", + "title": "Array of content parts", + "items": { + "$ref": "#/components/schemas/chatCompletionRequestSystemMessageContentPart" + }, + "minItems": 1 + } + ] + }, + "role": { + "type": "string", + "enum": [ + "system" + ], + "description": "The role of the messages author, in this case `system`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "required": [ + "content", + "role" + ] + }, + "chatCompletionRequestUserMessage": { + "type": "object", + "title": "User message", + "properties": { + "content": { + "description": "The contents of the user message.\n", + "oneOf": [ + { + "type": "string", + "description": "The text contents of the message.", + "title": "Text content" + }, + { + "type": "array", + "description": "An array of content parts with a defined type, each can be of type `text` or `image_url` when passing in images. You can pass multiple images by adding multiple `image_url` content parts. Image input is only supported when using the `gpt-4o` model.", + "title": "Array of content parts", + "items": { + "$ref": "#/components/schemas/chatCompletionRequestUserMessageContentPart" + }, + "minItems": 1 + } + ], + "x-oaiExpandable": true + }, + "role": { + "type": "string", + "enum": [ + "user" + ], + "description": "The role of the messages author, in this case `user`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "required": [ + "content", + "role" + ] + }, + "chatCompletionRequestAssistantMessage": { + "type": "object", + "title": "Assistant message", + "properties": { + "content": { + "nullable": true, + "oneOf": [ + { + "type": "string", + "description": "The contents of the assistant message.", + "title": "Text content" + }, + { + "type": "array", + "description": "An array of content parts with a defined type. Can be one or more of type `text`, or exactly one of type `refusal`.", + "title": "Array of content parts", + "items": { + "$ref": "#/components/schemas/chatCompletionRequestAssistantMessageContentPart" + }, + "minItems": 1 + } + ], + "description": "The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.\n" + }, + "refusal": { + "nullable": true, + "type": "string", + "description": "The refusal message by the assistant." + }, + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the messages author, in this case `assistant`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }, + "tool_calls": { + "$ref": "#/components/schemas/chatCompletionMessageToolCalls" + }, + "function_call": { + "type": "object", + "deprecated": true, + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "nullable": true, + "properties": { + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "arguments", + "name" + ] + } + }, + "required": [ + "role" + ] + }, + "chatCompletionRequestToolMessage": { + "type": "object", + "title": "Tool message", + "properties": { + "role": { + "type": "string", + "enum": [ + "tool" + ], + "description": "The role of the messages author, in this case `tool`." + }, + "content": { + "oneOf": [ + { + "type": "string", + "description": "The contents of the tool message.", + "title": "Text content" + }, + { + "type": "array", + "description": "An array of content parts with a defined type. For tool messages, only type `text` is supported.", + "title": "Array of content parts", + "items": { + "$ref": "#/components/schemas/chatCompletionRequestToolMessageContentPart" + }, + "minItems": 1 + } + ], + "description": "The contents of the tool message." + }, + "tool_call_id": { + "type": "string", + "description": "Tool call that this message is responding to." + } + }, + "required": [ + "role", + "content", + "tool_call_id" + ] + }, + "chatCompletionRequestFunctionMessage": { + "type": "object", + "title": "Function message", + "deprecated": true, + "properties": { + "role": { + "type": "string", + "enum": [ + "function" + ], + "description": "The role of the messages author, in this case `function`." + }, + "content": { + "nullable": true, + "type": "string", + "description": "The contents of the function message." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "role", + "content", + "name" + ] + }, + "chatCompletionRequestDeveloperMessageContentPart": { + "oneOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + } + ] + }, + "chatCompletionRequestSystemMessageContentPart": { + "oneOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + } + ] + }, + "chatCompletionRequestUserMessageContentPart": { + "oneOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartImage" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartAudio" + } + ] + }, + "chatCompletionRequestAssistantMessageContentPart": { + "oneOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + }, + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartRefusal" + } + ] + }, + "chatCompletionRequestToolMessageContentPart": { + "oneOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + } + ] + }, + "chatCompletionRequestMessageContentPartText": { + "type": "object", + "title": "Text content part", + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ], + "description": "The type of the content part." + }, + "text": { + "type": "string", + "description": "The text content." + } + }, + "required": [ + "type", + "text" + ] + }, + "chatCompletionRequestMessageContentPartAudio": { + "type": "object", + "title": "Audio content part", + "description": "Learn about [audio inputs](/docs/guides/audio).\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "input_audio" + ], + "description": "The type of the content part. Always `input_audio`." + }, + "input_audio": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "Base64 encoded audio data." + }, + "format": { + "type": "string", + "enum": [ + "wav", + "mp3" + ], + "description": "The format of the encoded audio data. Currently supports \"wav\" and \"mp3\".\n" + } + }, + "required": [ + "data", + "format" + ] + } + }, + "required": [ + "type", + "input_audio" + ] + }, + "chatCompletionRequestMessageContentPartImage": { + "type": "object", + "title": "Image content part", + "properties": { + "type": { + "type": "string", + "enum": [ + "image_url" + ], + "description": "The type of the content part." + }, + "image_url": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "Either a URL of the image or the base64 encoded image data.", + "format": "uri" + }, + "detail": { + "type": "string", + "description": "Specifies the detail level of the image. Learn more in the [Vision guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/gpt-with-vision?tabs=rest%2Csystem-assigned%2Cresource#detail-parameter-settings-in-image-processing-low-high-auto).", + "enum": [ + "auto", + "low", + "high" + ], + "default": "auto" + } + }, + "required": [ + "url" + ] + } + }, + "required": [ + "type", + "image_url" + ] + }, + "chatCompletionRequestMessageContentPartRefusal": { + "type": "object", + "title": "Refusal content part", + "properties": { + "type": { + "type": "string", + "enum": [ + "refusal" + ], + "description": "The type of the content part." + }, + "refusal": { + "type": "string", + "description": "The refusal message generated by the model." + } + }, + "required": [ + "type", + "refusal" + ] + }, + "azureChatExtensionConfiguration": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/azureChatExtensionType" + } + }, + "description": " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI.", + "discriminator": { + "propertyName": "type", + "mapping": { + "azure_search": "#/components/schemas/azureSearchChatExtensionConfiguration", + "azure_cosmos_db": "#/components/schemas/azureCosmosDBChatExtensionConfiguration", + "elasticsearch": "#/components/schemas/elasticsearchChatExtensionConfiguration", + "mongo_db": "#/components/schemas/mongoDBChatExtensionConfiguration", + "pinecone": "#/components/schemas/pineconeChatExtensionConfiguration" + } + } + }, + "azureChatExtensionType": { + "type": "string", + "description": " A representation of configuration data for a single Azure OpenAI chat extension. This will be used by a chat\n completions request that should use Azure OpenAI chat extensions to augment the response behavior.\n The use of this configuration is compatible only with Azure OpenAI.", + "enum": [ + "azure_search", + "azure_cosmos_db", + "elasticsearch", + "mongo_db", + "pinecone" + ], + "x-ms-enum": { + "name": "AzureChatExtensionType", + "modelAsString": true, + "values": [ + { + "name": "azureSearch", + "value": "azure_search", + "description": "Represents the use of Azure Search as an Azure OpenAI chat extension." + }, + { + "name": "azureCosmosDB", + "value": "azure_cosmos_db", + "description": "Represents the use of Azure Cosmos DB as an Azure OpenAI chat extension." + }, + { + "name": "elasticsearch", + "value": "elasticsearch", + "description": "Represents the use of Elasticsearch® index as an Azure OpenAI chat extension." + }, + { + "name": "mongoDB", + "value": "mongo_db", + "description": "Represents the use of Mongo DB as an Azure OpenAI chat extension." + }, + { + "name": "pinecone", + "value": "pinecone", + "description": "Represents the use of Pinecone index as an Azure OpenAI chat extension." + } + ] + } + }, + "azureSearchChatExtensionConfiguration": { + "required": [ + "parameters" + ], + "description": "A specific representation of configurable options for Azure Search when using it as an Azure OpenAI chat\nextension.", + "allOf": [ + { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + }, + { + "properties": { + "parameters": { + "$ref": "#/components/schemas/azureSearchChatExtensionParameters" + } + } + } + ], + "x-ms-discriminator-value": "azure_search" + }, + "azureSearchChatExtensionParameters": { + "required": [ + "authentication", + "endpoint", + "index_name" + ], + "type": "object", + "properties": { + "authentication": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataApiKeyAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/onYourDataAccessTokenAuthenticationOptions" + } + ] + }, + "top_n_documents": { + "type": "integer", + "description": "The configured top number of documents to feature for the configured query.", + "format": "int32" + }, + "max_search_queries": { + "type": "integer", + "description": "The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.", + "format": "int32" + }, + "allow_partial_result": { + "type": "boolean", + "default": false, + "description": "If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of indexed data." + }, + "strictness": { + "maximum": 5, + "minimum": 1, + "type": "integer", + "description": "The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.", + "format": "int32" + }, + "endpoint": { + "type": "string", + "description": "The absolute endpoint path for the Azure Search resource to use.", + "format": "uri" + }, + "index_name": { + "type": "string", + "description": "The name of the index to use as available in the referenced Azure Search resource." + }, + "fields_mapping": { + "$ref": "#/components/schemas/azureSearchIndexFieldMappingOptions" + }, + "query_type": { + "$ref": "#/components/schemas/azureSearchQueryType" + }, + "semantic_configuration": { + "type": "string", + "description": "The additional semantic configuration for the query." + }, + "filter": { + "type": "string", + "description": "Search filter." + }, + "embedding_dependency": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataIntegratedVectorizationSource" + } + ] + }, + "include_contexts": { + "type": "array", + "description": "The included properties of the output context. If not specified, the default value is `citations` and `intent`.", + "items": { + "$ref": "#/components/schemas/onYourDataContextProperty" + } + } + }, + "description": "Parameters for Azure Search when used as an Azure OpenAI chat extension." + }, + "azureSearchIndexFieldMappingOptions": { + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "description": "The names of index fields that should be treated as content.", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + }, + "vector_fields": { + "type": "array", + "description": "The names of fields that represent vector data.", + "items": { + "type": "string" + } + }, + "image_vector_fields": { + "type": "array", + "description": "The names of fields that represent image vector data.", + "items": { + "type": "string" + } + } + }, + "description": "Optional settings to control how fields are processed when using a configured Azure Search resource." + }, + "azureSearchQueryType": { + "type": "string", + "description": "The type of Azure Search retrieval query that should be executed when using it as an Azure OpenAI chat extension.", + "enum": [ + "simple", + "semantic", + "vector", + "vector_simple_hybrid", + "vector_semantic_hybrid" + ], + "x-ms-enum": { + "name": "AzureSearchQueryType", + "modelAsString": true, + "values": [ + { + "name": "simple", + "value": "simple", + "description": "Represents the default, simple query parser." + }, + { + "name": "semantic", + "value": "semantic", + "description": "Represents the semantic query parser for advanced semantic modeling." + }, + { + "name": "vector", + "value": "vector", + "description": "Represents vector search over computed data." + }, + { + "name": "vectorSimpleHybrid", + "value": "vector_simple_hybrid", + "description": "Represents a combination of the simple query strategy with vector data." + }, + { + "name": "vectorSemanticHybrid", + "value": "vector_semantic_hybrid", + "description": "Represents a combination of semantic search and vector data querying." + } + ] + } + }, + "azureCosmosDBChatExtensionConfiguration": { + "required": [ + "parameters" + ], + "description": "A specific representation of configurable options for Azure Cosmos DB when using it as an Azure OpenAI chat\nextension.", + "allOf": [ + { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + }, + { + "properties": { + "parameters": { + "$ref": "#/components/schemas/azureCosmosDBChatExtensionParameters" + } + } + } + ], + "x-ms-discriminator-value": "azure_cosmos_db" + }, + "azureCosmosDBChatExtensionParameters": { + "required": [ + "authentication", + "container_name", + "database_name", + "embedding_dependency", + "fields_mapping", + "index_name" + ], + "type": "object", + "properties": { + "authentication": { + "$ref": "#/components/schemas/onYourDataConnectionStringAuthenticationOptions" + }, + "top_n_documents": { + "type": "integer", + "description": "The configured top number of documents to feature for the configured query.", + "format": "int32" + }, + "max_search_queries": { + "type": "integer", + "description": "The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.", + "format": "int32" + }, + "allow_partial_result": { + "type": "boolean", + "default": false, + "description": "If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of indexed data." + }, + "strictness": { + "maximum": 5, + "minimum": 1, + "type": "integer", + "description": "The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.", + "format": "int32" + }, + "database_name": { + "type": "string", + "description": "The MongoDB vCore database name to use with Azure Cosmos DB." + }, + "container_name": { + "type": "string", + "description": "The name of the Azure Cosmos DB resource container." + }, + "index_name": { + "type": "string", + "description": "The MongoDB vCore index name to use with Azure Cosmos DB." + }, + "fields_mapping": { + "$ref": "#/components/schemas/azureCosmosDBFieldMappingOptions" + }, + "embedding_dependency": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + } + ] + }, + "include_contexts": { + "type": "array", + "description": "The included properties of the output context. If not specified, the default value is `citations` and `intent`.", + "items": { + "$ref": "#/components/schemas/onYourDataContextProperty" + } + } + }, + "description": "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Azure Cosmos DB for\nMongoDB vCore." + }, + "azureCosmosDBFieldMappingOptions": { + "required": [ + "content_fields", + "vector_fields" + ], + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "description": "The names of index fields that should be treated as content.", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + }, + "vector_fields": { + "type": "array", + "description": "The names of fields that represent vector data.", + "items": { + "type": "string" + } + } + }, + "description": "Optional settings to control how fields are processed when using a configured Azure Cosmos DB resource." + }, + "elasticsearchChatExtensionConfiguration": { + "required": [ + "parameters" + ], + "description": "A specific representation of configurable options for Elasticsearch when using it as an Azure OpenAI chat\nextension.", + "allOf": [ + { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + }, + { + "properties": { + "parameters": { + "$ref": "#/components/schemas/elasticsearchChatExtensionParameters" + } + } + } + ], + "x-ms-discriminator-value": "elasticsearch" + }, + "elasticsearchChatExtensionParameters": { + "required": [ + "authentication", + "endpoint", + "index_name" + ], + "type": "object", + "properties": { + "authentication": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions" + } + ] + }, + "top_n_documents": { + "type": "integer", + "description": "The configured top number of documents to feature for the configured query.", + "format": "int32" + }, + "max_search_queries": { + "type": "integer", + "description": "The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.", + "format": "int32" + }, + "allow_partial_result": { + "type": "boolean", + "default": false, + "description": "If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of indexed data." + }, + "strictness": { + "maximum": 5, + "minimum": 1, + "type": "integer", + "description": "The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.", + "format": "int32" + }, + "endpoint": { + "type": "string", + "description": "The endpoint of Elasticsearch®.", + "format": "uri" + }, + "index_name": { + "type": "string", + "description": "The index name of Elasticsearch®." + }, + "fields_mapping": { + "$ref": "#/components/schemas/elasticsearchIndexFieldMappingOptions" + }, + "query_type": { + "$ref": "#/components/schemas/elasticsearchQueryType" + }, + "embedding_dependency": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataModelIdVectorizationSource" + } + ] + }, + "include_contexts": { + "type": "array", + "description": "The included properties of the output context. If not specified, the default value is `citations` and `intent`.", + "items": { + "$ref": "#/components/schemas/onYourDataContextProperty" + } + } + }, + "description": "Parameters to use when configuring Elasticsearch® as an Azure OpenAI chat extension. " + }, + "elasticsearchIndexFieldMappingOptions": { + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "description": "The names of index fields that should be treated as content.", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + }, + "vector_fields": { + "type": "array", + "description": "The names of fields that represent vector data.", + "items": { + "type": "string" + } + } + }, + "description": "Optional settings to control how fields are processed when using a configured Elasticsearch® resource." + }, + "elasticsearchQueryType": { + "type": "string", + "description": "The type of Elasticsearch® retrieval query that should be executed when using it as an Azure OpenAI chat extension.", + "enum": [ + "simple", + "vector" + ], + "x-ms-enum": { + "name": "ElasticsearchQueryType", + "modelAsString": true, + "values": [ + { + "name": "simple", + "value": "simple", + "description": "Represents the default, simple query parser." + }, + { + "name": "vector", + "value": "vector", + "description": "Represents vector search over computed data." + } + ] + } + }, + "mongoDBChatExtensionConfiguration": { + "required": [ + "parameters" + ], + "description": "A specific representation of configurable options for Mongo DB when using it as an Azure OpenAI chat\nextension.", + "allOf": [ + { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + }, + { + "properties": { + "parameters": { + "$ref": "#/components/schemas/mongoDBChatExtensionParameters" + } + } + } + ], + "x-ms-discriminator-value": "mongo_db" + }, + "mongoDBChatExtensionParameters": { + "required": [ + "authentication", + "app_name", + "collection_name", + "database_name", + "endpoint", + "embedding_dependency", + "fields_mapping", + "index_name" + ], + "type": "object", + "properties": { + "authentication": { + "$ref": "#/components/schemas/onYourDataUsernameAndPasswordAuthenticationOptions" + }, + "top_n_documents": { + "type": "integer", + "description": "The configured top number of documents to feature for the configured query.", + "format": "int32" + }, + "max_search_queries": { + "type": "integer", + "description": "The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.", + "format": "int32" + }, + "allow_partial_result": { + "type": "boolean", + "default": false, + "description": "If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of indexed data." + }, + "strictness": { + "maximum": 5, + "minimum": 1, + "type": "integer", + "description": "The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.", + "format": "int32" + }, + "endpoint": { + "type": "string", + "description": "The name of the Mongo DB cluster endpoint." + }, + "database_name": { + "type": "string", + "description": "The name of the Mongo DB database." + }, + "collection_name": { + "type": "string", + "description": "The name of the Mongo DB Collection." + }, + "app_name": { + "type": "string", + "description": "The name of the Mongo DB Application." + }, + "index_name": { + "type": "string", + "description": "The The name of the Mongo DB index." + }, + "fields_mapping": { + "$ref": "#/components/schemas/mongoDBFieldMappingOptions" + }, + "embedding_dependency": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + } + ] + }, + "include_contexts": { + "type": "array", + "description": "The included properties of the output context. If not specified, the default value is `citations` and `intent`.", + "items": { + "$ref": "#/components/schemas/onYourDataContextProperty" + } + } + }, + "description": "Parameters to use when configuring Azure OpenAI On Your Data chat extensions when using Mongo DB." + }, + "mongoDBFieldMappingOptions": { + "required": [ + "content_fields", + "vector_fields" + ], + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "description": "The names of index fields that should be treated as content.", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + }, + "vector_fields": { + "type": "array", + "description": "The names of fields that represent vector data.", + "items": { + "type": "string" + } + } + }, + "description": "Optional settings to control how fields are processed when using a configured Mongo DB resource." + }, + "pineconeChatExtensionConfiguration": { + "required": [ + "parameters" + ], + "description": "A specific representation of configurable options for Pinecone when using it as an Azure OpenAI chat\nextension.", + "allOf": [ + { + "$ref": "#/components/schemas/azureChatExtensionConfiguration" + }, + { + "properties": { + "parameters": { + "$ref": "#/components/schemas/pineconeChatExtensionParameters" + } + } + } + ], + "x-ms-discriminator-value": "pinecone" + }, + "pineconeChatExtensionParameters": { + "required": [ + "authentication", + "embedding_dependency", + "environment", + "fields_mapping", + "index_name" + ], + "type": "object", + "properties": { + "authentication": { + "$ref": "#/components/schemas/onYourDataApiKeyAuthenticationOptions" + }, + "top_n_documents": { + "type": "integer", + "description": "The configured top number of documents to feature for the configured query.", + "format": "int32" + }, + "max_search_queries": { + "type": "integer", + "description": "The max number of rewritten queries should be send to search provider for one user message. If not specified, the system will decide the number of queries to send.", + "format": "int32" + }, + "allow_partial_result": { + "type": "boolean", + "default": false, + "description": "If specified as true, the system will allow partial search results to be used and the request fails if all the queries fail. If not specified, or specified as false, the request will fail if any search query fails." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of indexed data." + }, + "strictness": { + "maximum": 5, + "minimum": 1, + "type": "integer", + "description": "The configured strictness of the search relevance filtering. The higher of strictness, the higher of the precision but lower recall of the answer.", + "format": "int32" + }, + "environment": { + "type": "string", + "description": "The environment name of Pinecone." + }, + "index_name": { + "type": "string", + "description": "The name of the Pinecone database index." + }, + "fields_mapping": { + "$ref": "#/components/schemas/pineconeFieldMappingOptions" + }, + "embedding_dependency": { + "$ref": "#/components/schemas/onYourDataDeploymentNameVectorizationSource" + }, + "include_contexts": { + "type": "array", + "description": "The included properties of the output context. If not specified, the default value is `citations` and `intent`.", + "items": { + "$ref": "#/components/schemas/onYourDataContextProperty" + } + } + }, + "description": "Parameters for configuring Azure OpenAI Pinecone chat extensions." + }, + "pineconeFieldMappingOptions": { + "required": [ + "content_fields" + ], + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "description": "The names of index fields that should be treated as content.", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + } + }, + "description": "Optional settings to control how fields are processed when using a configured Pinecone resource." + }, + "onYourDataAuthenticationOptions": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/onYourDataAuthenticationType" + } + }, + "description": "The authentication options for Azure OpenAI On Your Data.", + "discriminator": { + "propertyName": "type", + "mapping": { + "api_key": "#/components/schemas/onYourDataApiKeyAuthenticationOptions", + "connection_string": "#/components/schemas/onYourDataConnectionStringAuthenticationOptions", + "key_and_key_id": "#/components/schemas/onYourDataKeyAndKeyIdAuthenticationOptions", + "encoded_api_key": "#/components/schemas/onYourDataEncodedApiKeyAuthenticationOptions", + "access_token": "#/components/schemas/onYourDataAccessTokenAuthenticationOptions", + "system_assigned_managed_identity": "#/components/schemas/onYourDataSystemAssignedManagedIdentityAuthenticationOptions", + "user_assigned_managed_identity": "#/components/schemas/onYourDataUserAssignedManagedIdentityAuthenticationOptions", + "username_and_password": "#/components/schemas/onYourDataUsernameAndPasswordAuthenticationOptions" + } + } + }, + "onYourDataContextProperty": { + "type": "string", + "description": "The context property.", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ], + "x-ms-enum": { + "name": "OnYourDataContextProperty", + "modelAsString": true, + "values": [ + { + "name": "citations", + "value": "citations", + "description": "The `citations` property." + }, + { + "name": "intent", + "value": "intent", + "description": "The `intent` property." + }, + { + "name": "allRetrievedDocuments", + "value": "all_retrieved_documents", + "description": "The `all_retrieved_documents` property." + } + ] + } + }, + "onYourDataAuthenticationType": { + "type": "string", + "description": "The authentication types supported with Azure OpenAI On Your Data.", + "enum": [ + "api_key", + "connection_string", + "key_and_key_id", + "encoded_api_key", + "access_token", + "system_assigned_managed_identity", + "user_assigned_managed_identity", + "username_and_password" + ], + "x-ms-enum": { + "name": "OnYourDataAuthenticationType", + "modelAsString": true, + "values": [ + { + "name": "apiKey", + "value": "api_key", + "description": "Authentication via API key." + }, + { + "name": "connectionString", + "value": "connection_string", + "description": "Authentication via connection string." + }, + { + "name": "keyAndKeyId", + "value": "key_and_key_id", + "description": "Authentication via key and key ID pair." + }, + { + "name": "encodedApiKey", + "value": "encoded_api_key", + "description": "Authentication via encoded API key." + }, + { + "name": "accessToken", + "value": "access_token", + "description": "Authentication via access token." + }, + { + "name": "systemAssignedManagedIdentity", + "value": "system_assigned_managed_identity", + "description": "Authentication via system-assigned managed identity." + }, + { + "name": "userAssignedManagedIdentity", + "value": "user_assigned_managed_identity", + "description": "Authentication via user-assigned managed identity." + }, + { + "name": "usernameAndPassword", + "value": "username_and_password", + "description": "Authentication via username and password." + } + ] + } + }, + "onYourDataApiKeyAuthenticationOptions": { + "required": [ + "key" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using an API key.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "key": { + "type": "string", + "description": "The API key to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "api_key" + }, + "onYourDataConnectionStringAuthenticationOptions": { + "required": [ + "connection_string" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using a connection string.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "connection_string": { + "type": "string", + "description": "The connection string to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "connection_string" + }, + "onYourDataKeyAndKeyIdAuthenticationOptions": { + "required": [ + "key", + "key_id" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using an Elasticsearch key and key ID pair.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "key": { + "type": "string", + "description": "The Elasticsearch key to use for authentication." + }, + "key_id": { + "type": "string", + "description": "The Elasticsearch key ID to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "key_and_key_id" + }, + "onYourDataEncodedApiKeyAuthenticationOptions": { + "required": [ + "encoded_api_key" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using an Elasticsearch encoded API key.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "encoded_api_key": { + "type": "string", + "description": "The Elasticsearch encoded API key to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "encoded_api_key" + }, + "onYourDataAccessTokenAuthenticationOptions": { + "required": [ + "access_token" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using access token.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "access_token": { + "type": "string", + "description": "The access token to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "access_token" + }, + "onYourDataSystemAssignedManagedIdentityAuthenticationOptions": { + "description": "The authentication options for Azure OpenAI On Your Data when using a system-assigned managed identity.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + } + ], + "x-ms-discriminator-value": "system_assigned_managed_identity" + }, + "onYourDataUserAssignedManagedIdentityAuthenticationOptions": { + "required": [ + "managed_identity_resource_id" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using a user-assigned managed identity.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "managed_identity_resource_id": { + "type": "string", + "description": "The resource ID of the user-assigned managed identity to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "user_assigned_managed_identity" + }, + "onYourDataUsernameAndPasswordAuthenticationOptions": { + "required": [ + "username", + "password" + ], + "description": "The authentication options for Azure OpenAI On Your Data when using a username and a password.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataAuthenticationOptions" + }, + { + "properties": { + "username": { + "type": "string", + "description": "The username to use for authentication." + }, + "password": { + "type": "string", + "description": "The password. to use for authentication." + } + } + } + ], + "x-ms-discriminator-value": "username_and_password" + }, + "onYourDataVectorizationSource": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/onYourDataVectorizationSourceType" + } + }, + "description": "An abstract representation of a vectorization source for Azure OpenAI On Your Data with vector search.", + "discriminator": { + "propertyName": "type", + "mapping": { + "endpoint": "#/components/schemas/onYourDataEndpointVectorizationSource", + "deployment_name": "#/components/schemas/onYourDataDeploymentNameVectorizationSource", + "integrated": "#/components/schemas/onYourDataIntegratedVectorizationSource", + "model_id": "#/components/schemas/onYourDataModelIdVectorizationSource" + } + } + }, + "onYourDataVectorizationSourceType": { + "type": "string", + "description": "Represents the available sources Azure OpenAI On Your Data can use to configure vectorization of data for use with\nvector search.", + "enum": [ + "endpoint", + "deployment_name", + "integrated", + "model_id" + ], + "x-ms-enum": { + "name": "OnYourDataVectorizationSourceType", + "modelAsString": true, + "values": [ + { + "name": "endpoint", + "value": "endpoint", + "description": "Represents vectorization performed by public service calls to an Azure OpenAI embedding model." + }, + { + "name": "deploymentName", + "value": "deployment_name", + "description": "Represents an Ada model deployment name to use. This model deployment must be in the same Azure OpenAI resource, but\nOn Your Data will use this model deployment via an internal call rather than a public one, which enables vector\nsearch even in private networks." + }, + { + "name": "integrated", + "value": "integrated", + "description": "Represents the integrated vectorizer defined within the search resource." + }, + { + "name": "modelId", + "value": "model_id", + "description": "Represents a specific embedding model ID as defined in the search service.\nCurrently only supported by Elasticsearch®." + } + ] + } + }, + "onYourDataEndpointVectorizationSource": { + "required": [ + "authentication", + "endpoint" + ], + "description": "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a public Azure OpenAI endpoint call for embeddings.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataVectorizationSource" + }, + { + "properties": { + "endpoint": { + "type": "string", + "description": "Specifies the resource endpoint URL from which embeddings should be retrieved. It should be in the format of https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/embeddings. The api-version query parameter is not allowed.", + "format": "uri" + }, + "authentication": { + "oneOf": [ + { + "$ref": "#/components/schemas/onYourDataApiKeyAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/onYourDataAccessTokenAuthenticationOptions" + } + ] + }, + "dimensions": { + "type": "integer", + "description": "The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models.", + "format": "int32" + } + } + } + ], + "x-ms-discriminator-value": "endpoint" + }, + "onYourDataDeploymentNameVectorizationSource": { + "required": [ + "deployment_name" + ], + "description": "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non an internal embeddings model deployment name in the same Azure OpenAI resource.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataVectorizationSource" + }, + { + "properties": { + "deployment_name": { + "type": "string", + "description": "Specifies the name of the model deployment to use for vectorization. This model deployment must be in the same Azure OpenAI resource, but On Your Data will use this model deployment via an internal call rather than a public one, which enables vector search even in private networks." + }, + "dimensions": { + "type": "integer", + "description": "The number of dimensions the embeddings should have. Only supported in `text-embedding-3` and later models.", + "format": "int32" + } + } + } + ], + "x-ms-discriminator-value": "deployment_name" + }, + "onYourDataIntegratedVectorizationSource": { + "description": "Represents the integrated vectorizer defined within the search resource.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataVectorizationSource" + } + ], + "x-ms-discriminator-value": "integrated" + }, + "onYourDataModelIdVectorizationSource": { + "required": [ + "model_id" + ], + "description": "The details of a a vectorization source, used by Azure OpenAI On Your Data when applying vector search, that is based\non a search service model ID. Currently only supported by Elasticsearch®.", + "allOf": [ + { + "$ref": "#/components/schemas/onYourDataVectorizationSource" + }, + { + "properties": { + "model_id": { + "type": "string", + "description": "Specifies the model ID to use for vectorization. This model ID must be defined in the search service." + } + } + } + ], + "x-ms-discriminator-value": "model_id" + }, + "azureChatExtensionsMessageContext": { + "type": "object", + "properties": { + "citations": { + "type": "array", + "description": "The data source retrieval result, used to generate the assistant message in the response.", + "items": { + "$ref": "#/components/schemas/citation" + }, + "x-ms-identifiers": [] + }, + "intent": { + "type": "string", + "description": "The detected intent from the chat history, used to pass to the next turn to carry over the context." + }, + "all_retrieved_documents": { + "type": "array", + "description": "All the retrieved documents.", + "items": { + "$ref": "#/components/schemas/retrievedDocument" + } + } + }, + "description": " A representation of the additional context information available when Azure OpenAI chat extensions are involved\n in the generation of a corresponding chat completions response. This context information is only populated when\n using an Azure OpenAI request configured to use a matching extension." + }, + "citation": { + "required": [ + "content" + ], + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "The content of the citation." + }, + "title": { + "type": "string", + "description": "The title of the citation." + }, + "url": { + "type": "string", + "description": "The URL of the citation." + }, + "filepath": { + "type": "string", + "description": "The file path of the citation." + }, + "chunk_id": { + "type": "string", + "description": "The chunk ID of the citation." + }, + "rerank_score": { + "type": "number", + "description": "The rerank score of the retrieved document.", + "format": "double" + } + }, + "description": "citation information for a chat completions response message." + }, + "retrievedDocument": { + "type": "object", + "description": "The retrieved document.", + "required": [ + "search_queries", + "data_source_index", + "original_search_score" + ], + "allOf": [ + { + "$ref": "#/components/schemas/citation" + }, + { + "properties": { + "search_queries": { + "type": "array", + "description": "The search queries used to retrieve the document.", + "items": { + "type": "string" + } + }, + "data_source_index": { + "type": "integer", + "description": "The index of the data source.", + "format": "int32" + }, + "original_search_score": { + "type": "number", + "description": "The original search score of the retrieved document.", + "format": "double" + }, + "filter_reason": { + "$ref": "#/components/schemas/filterReason", + "description": "Represents the rationale for filtering the document. If the document does not undergo filtering, this field will remain unset." + } + } + } + ] + }, + "filterReason": { + "type": "string", + "description": "The filtering reason of the retrieved document.", + "enum": [ + "score", + "rerank" + ], + "x-ms-enum": { + "name": "FilterReason", + "modelAsString": true, + "values": [ + { + "name": "score", + "value": "score", + "description": "The document is filtered by original search score threshold defined by `strictness` configure." + }, + { + "name": "rerank", + "value": "rerank", + "description": "The document is not filtered by original search score threshold, but is filtered by rerank score and `top_n_documents` configure." + } + ] + } + }, + "chatCompletionMessageToolCall": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "$ref": "#/components/schemas/toolCallType" + }, + "function": { + "type": "object", + "description": "The function that the model called.", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + } + }, + "required": [ + "name", + "arguments" + ] + } + }, + "required": [ + "id", + "type", + "function" + ] + }, + "toolCallType": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool call, in this case `function`.", + "x-ms-enum": { + "name": "ToolCallType", + "modelAsString": true, + "values": [ + { + "value": "function", + "description": "The tool call type is function." + } + ] + } + }, + "chatCompletionRequestMessageTool": { + "allOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessage" + }, + { + "type": "object", + "nullable": true, + "properties": { + "tool_call_id": { + "type": "string", + "description": "Tool call that this message is responding to." + }, + "content": { + "type": "string", + "description": "The contents of the message.", + "nullable": true + } + } + } + ], + "required": [ + "tool_call_id", + "content" + ] + }, + "chatCompletionRequestMessageFunction": { + "allOf": [ + { + "$ref": "#/components/schemas/chatCompletionRequestMessage" + }, + { + "type": "object", + "description": "Deprecated. Message that represents a function.", + "nullable": true, + "properties": { + "role": { + "type": "string", + "enum": [ + "function" + ], + "description": "The role of the messages author, in this case `function`." + }, + "name": { + "type": "string", + "description": "The contents of the message." + }, + "content": { + "type": "string", + "description": "The contents of the message.", + "nullable": true + } + } + } + ], + "required": [ + "function_call_id", + "content" + ] + }, + "createChatCompletionResponse": { + "type": "object", + "description": "Represents a chat completion response returned by model, based on the provided input.", + "properties": { + "id": { + "type": "string", + "description": "A unique identifier for the chat completion." + }, + "prompt_filter_results": { + "$ref": "#/components/schemas/promptFilterResults" + }, + "choices": { + "type": "array", + "description": "A list of chat completion choices. Can be more than one if `n` is greater than 1.", + "items": { + "type": "object", + "required": [ + "finish_reason", + "index", + "message", + "logprobs" + ], + "properties": { + "finish_reason": { + "type": "string", + "description": "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n", + "enum": [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call" + ] + }, + "index": { + "type": "integer", + "description": "The index of the choice in the list of choices." + }, + "message": { + "$ref": "#/components/schemas/chatCompletionResponseMessage" + }, + "content_filter_results": { + "$ref": "#/components/schemas/contentFilterChoiceResults" + }, + "logprobs": { + "description": "Log probability information for the choice.", + "type": "object", + "nullable": true, + "properties": { + "content": { + "description": "A list of message content tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + }, + "refusal": { + "description": "A list of message refusal tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + } + }, + "required": [ + "content", + "refusal" + ] + } + } + } + }, + "created": { + "type": "integer", + "description": "The Unix timestamp (in seconds) of when the chat completion was created." + }, + "model": { + "type": "string", + "description": "The model used for the chat completion." + }, + "system_fingerprint": { + "type": "string", + "description": "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }, + "object": { + "type": "string", + "description": "The object type, which is always `chat.completion`.", + "enum": [ + "chat.completion" + ] + }, + "usage": { + "$ref": "#/components/schemas/completionUsage" + } + }, + "required": [ + "choices", + "created", + "id", + "model", + "object" + ] + }, + "createChatCompletionStreamResponse": { + "type": "object", + "description": "Represents a streamed chunk of a chat completion response returned by model, based on the provided input.", + "properties": { + "id": { + "type": "string", + "description": "A unique identifier for the chat completion. Each chunk has the same ID." + }, + "choices": { + "type": "array", + "description": "A list of chat completion choices. Can contain more than one elements if `n` is greater than 1.\n", + "items": { + "type": "object", + "required": [ + "delta", + "finish_reason", + "index" + ], + "properties": { + "delta": { + "$ref": "#/components/schemas/chatCompletionStreamResponseDelta" + }, + "logprobs": { + "description": "Log probability information for the choice.", + "type": "object", + "nullable": true, + "properties": { + "content": { + "description": "A list of message content tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + }, + "refusal": { + "description": "A list of message refusal tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + } + }, + "required": [ + "content", + "refusal" + ] + }, + "finish_reason": { + "type": "string", + "description": "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n", + "enum": [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call" + ], + "nullable": true + }, + "index": { + "type": "integer", + "description": "The index of the choice in the list of choices." + } + } + } + }, + "created": { + "type": "integer", + "description": "The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp." + }, + "model": { + "type": "string", + "description": "The model to generate the completion." + }, + "system_fingerprint": { + "type": "string", + "description": "This fingerprint represents the backend configuration that the model runs with.\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + }, + "object": { + "type": "string", + "description": "The object type, which is always `chat.completion.chunk`.", + "enum": [ + "chat.completion.chunk" + ], + "required": [ + "prompt_tokens", + "completion_tokens", + "total_tokens" + ] + } + }, + "required": [ + "choices", + "created", + "id", + "model", + "object" + ] + }, + "chatCompletionStreamResponseDelta": { + "type": "object", + "description": "A chat completion delta generated by streamed model responses.", + "properties": { + "content": { + "type": "string", + "description": "The contents of the chunk message.", + "nullable": true + }, + "function_call": { + "deprecated": true, + "type": "object", + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "properties": { + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + } + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionMessageToolCallChunk" + } + }, + "role": { + "type": "string", + "enum": [ + "system", + "user", + "assistant", + "tool" + ], + "description": "The role of the author of this message." + }, + "refusal": { + "type": "string", + "description": "The refusal message generated by the model.", + "nullable": true + } + } + }, + "chatCompletionMessageToolCallChunk": { + "type": "object", + "properties": { + "index": { + "type": "integer" + }, + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + } + } + } + }, + "required": [ + "index" + ] + }, + "chatCompletionStreamOptions": { + "description": "Options for streaming response. Only set this when you set `stream: true`.\n", + "type": "object", + "nullable": true, + "default": null, + "properties": { + "include_usage": { + "type": "boolean", + "description": "If set, an additional chunk will be streamed before the `data: [DONE]` message. The `usage` field on this chunk shows the token usage statistics for the entire request, and the `choices` field will always be an empty array. All other chunks will also include a `usage` field, but with a null value.\n" + } + } + }, + "chatCompletionChoiceLogProbs": { + "description": "Log probability information for the choice.", + "type": "object", + "nullable": true, + "properties": { + "content": { + "description": "A list of message content tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + }, + "refusal": { + "description": "A list of message refusal tokens with log probability information.", + "type": "array", + "items": { + "$ref": "#/components/schemas/chatCompletionTokenLogprob" + }, + "nullable": true + } + }, + "required": [ + "content" + ] + }, + "chatCompletionTokenLogprob": { + "type": "object", + "properties": { + "token": { + "description": "The token.", + "type": "string" + }, + "logprob": { + "description": "The log probability of this token.", + "type": "number" + }, + "bytes": { + "description": "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.", + "type": "array", + "items": { + "type": "integer" + }, + "nullable": true + }, + "top_logprobs": { + "description": "List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned.", + "type": "array", + "items": { + "type": "object", + "properties": { + "token": { + "description": "The token.", + "type": "string" + }, + "logprob": { + "description": "The log probability of this token.", + "type": "number" + }, + "bytes": { + "description": "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token.", + "type": "array", + "items": { + "type": "integer" + }, + "nullable": true + } + }, + "required": [ + "token", + "logprob", + "bytes" + ] + } + } + }, + "required": [ + "token", + "logprob", + "bytes", + "top_logprobs" + ] + }, + "chatCompletionResponseMessage": { + "type": "object", + "description": "A chat completion message generated by the model.", + "properties": { + "role": { + "$ref": "#/components/schemas/chatCompletionResponseMessageRole" + }, + "refusal": { + "type": "string", + "description": "The refusal message generated by the model.", + "nullable": true + }, + "content": { + "type": "string", + "description": "The contents of the message.", + "nullable": true + }, + "tool_calls": { + "type": "array", + "description": "The tool calls generated by the model, such as function calls.", + "items": { + "$ref": "#/components/schemas/chatCompletionMessageToolCall" + } + }, + "function_call": { + "$ref": "#/components/schemas/chatCompletionFunctionCall" + }, + "audio": { + "type": "object", + "description": "If the audio output modality is requested, this object contains data\nabout the audio response from the model. [Learn more](/docs/guides/audio).\n", + "x-oaiExpandable": true, + "required": [ + "id", + "expires_at", + "data", + "transcript" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this audio response." + }, + "expires_at": { + "type": "integer", + "description": "The Unix timestamp (in seconds) for when this audio response will\nno longer be accessible on the server for use in multi-turn\nconversations.\n" + }, + "data": { + "type": "string", + "description": "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request.\n" + }, + "transcript": { + "type": "string", + "description": "Transcript of the audio generated by the model." + } + } + }, + "context": { + "$ref": "#/components/schemas/azureChatExtensionsMessageContext" + } + }, + "required": [ + "role", + "content", + "refusal" + ] + }, + "chatCompletionResponseMessageRole": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the author of the response message." + }, + "chatCompletionToolChoiceOption": { + "description": "Controls which (if any) tool is called by the model. `none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools. Specifying a particular tool via `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool. `none` is the default when no tools are present. `auto` is the default if tools are present.", + "oneOf": [ + { + "type": "string", + "description": "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.", + "enum": [ + "none", + "auto", + "required" + ] + }, + { + "$ref": "#/components/schemas/chatCompletionNamedToolChoice" + } + ] + }, + "chatCompletionNamedToolChoice": { + "type": "object", + "description": "Specifies a tool the model should use. Use to force the model to call a specific function.", + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "type", + "function" + ] + }, + "ParallelToolCalls": { + "type": "boolean", + "description": "Whether to enable parallel function calling during tool use.", + "default": true + }, + "PredictionContent": { + "type": "object", + "title": "Static Content", + "description": "Static predicted output content, such as the content of a text file that is being regenerated.", + "required": [ + "content", + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "The type of the predicted content you want to provide. This type is currently always `content`.", + "enum": [ + "content" + ] + }, + "content": { + "x-oaiExpandable": true, + "description": "The content that should be matched when generating a model response. If generated tokens would match this content, the entire model response can be returned much more quickly.", + "oneOf": [ + { + "type": "string", + "title": "Text Content", + "description": "The content used for a Predicted Output. This is often the text of a file you are regenerating with minor changes." + }, + { + "type": "array", + "title": "Array of content parts", + "description": "An array of content parts with a defined type. Supported options differ based on the [model](/docs/models) being used to generate the response. Can contain text inputs.", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/chatCompletionRequestMessageContentPartText" + } + } + ] + } + } + }, + "chatCompletionMessageToolCalls": { + "type": "array", + "description": "The tool calls generated by the model, such as function calls.", + "items": { + "$ref": "#/components/schemas/chatCompletionMessageToolCall" + } + }, + "ChatCompletionModalities": { + "type": "array", + "description": "Output types that you would like the model to generate for this request.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to [generate audio](/docs/guides/audio). To\nrequest that this model generate both text and audio responses, you can\nuse:\n\n`[\"text\", \"audio\"]`\n", + "items": { + "type": "string", + "enum": [ + "text", + "audio" + ] + } + }, + "chatCompletionFunctionCall": { + "type": "object", + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + } + }, + "required": [ + "name", + "arguments" + ] + }, + "completionUsage": { + "type": "object", + "description": "Usage statistics for the completion request.", + "properties": { + "prompt_tokens": { + "type": "integer", + "description": "Number of tokens in the prompt." + }, + "completion_tokens": { + "type": "integer", + "description": "Number of tokens in the generated completion." + }, + "total_tokens": { + "type": "integer", + "description": "Total number of tokens used in the request (prompt + completion)." + }, + "prompt_tokens_details": { + "type": "object", + "description": "Details of the prompt tokens.", + "properties": { + "audio_tokens": { + "type": "integer", + "description": "Audio input tokens present in the prompt." + }, + "cached_tokens": { + "type": "integer", + "description": "The number of cached prompt tokens." + } + } + }, + "completion_tokens_details": { + "type": "object", + "description": "Breakdown of tokens used in a completion.", + "properties": { + "accepted_prediction_tokens": { + "type": "integer", + "description": "When using Predicted Outputs, the number of tokens in the prediction that appeared in the completion." + }, + "audio_tokens": { + "type": "integer", + "description": "Audio input tokens generated by the model." + }, + "reasoning_tokens": { + "type": "integer", + "description": "Tokens generated by the model for reasoning." + }, + "rejected_prediction_tokens": { + "type": "integer", + "description": "When using Predicted Outputs, the number of tokens in the prediction that did not appear in the completion. However, like reasoning tokens, these tokens are still counted in the total completion tokens for purposes of billing, output, and context window limits." + } + } + } + }, + "required": [ + "prompt_tokens", + "completion_tokens", + "total_tokens" + ] + }, + "chatCompletionTool": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "$ref": "#/components/schemas/FunctionObject" + } + }, + "required": [ + "type", + "function" + ] + }, + "FunctionParameters": { + "type": "object", + "description": "The parameters the functions accepts, described as a JSON Schema object. See the guide](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format. \n\nOmitting `parameters` defines a function with an empty parameter list.", + "additionalProperties": true + }, + "FunctionObject": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description of what the function does, used by the model to choose when and how to call the function." + }, + "name": { + "type": "string", + "description": "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "parameters": { + "$ref": "#/components/schemas/FunctionParameters" + }, + "strict": { + "type": "boolean", + "nullable": true, + "default": false, + "description": "Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](docs/guides/function-calling)." + } + }, + "required": [ + "name" + ] + }, + "ResponseFormatText": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of response format being defined: `text`", + "enum": [ + "text" + ] + } + }, + "required": [ + "type" + ] + }, + "ResponseFormatJsonObject": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of response format being defined: `json_object`", + "enum": [ + "json_object" + ] + } + }, + "required": [ + "type" + ] + }, + "ResponseFormatJsonSchemaSchema": { + "type": "object", + "description": "The schema for the response format, described as a JSON Schema object.", + "additionalProperties": true + }, + "ResponseFormatJsonSchema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of response format being defined: `json_schema`", + "enum": [ + "json_schema" + ] + }, + "json_schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description of what the response format is for, used by the model to determine how to respond in the format." + }, + "name": { + "type": "string", + "description": "The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "schema": { + "$ref": "#/components/schemas/ResponseFormatJsonSchemaSchema" + }, + "strict": { + "type": "boolean", + "nullable": true, + "default": false, + "description": "Whether to enable strict schema adherence when generating the output. If set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of JSON Schema is supported when `strict` is `true`." + } + }, + "required": [ + "schema", + "name" + ] + } + }, + "required": [ + "type", + "json_schema" + ] + }, + "chatCompletionChoiceCommon": { + "type": "object", + "properties": { + "index": { + "type": "integer" + }, + "finish_reason": { + "type": "string" + } + } + }, + "createTranslationRequest": { + "type": "object", + "description": "Translation request.", + "properties": { + "file": { + "type": "string", + "description": "The audio file to translate.", + "format": "binary" + }, + "prompt": { + "type": "string", + "description": "An optional text to guide the model's style or continue a previous audio segment. The prompt should be in English." + }, + "response_format": { + "$ref": "#/components/schemas/audioResponseFormat" + }, + "temperature": { + "type": "number", + "default": 0, + "description": "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit." + } + }, + "required": [ + "file" + ] + }, + "audioResponse": { + "description": "Translation or transcription response when response_format was json", + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "Translated or transcribed text." + } + }, + "required": [ + "text" + ] + }, + "audioVerboseResponse": { + "description": "Translation or transcription response when response_format was verbose_json", + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/audioResponse" + }, + { + "properties": { + "task": { + "type": "string", + "description": "Type of audio task.", + "enum": [ + "transcribe", + "translate" + ], + "x-ms-enum": { + "modelAsString": true + } + }, + "language": { + "type": "string", + "description": "Language." + }, + "duration": { + "type": "number", + "description": "Duration." + }, + "segments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/audioSegment" + } + }, + "words": { + "type": "array", + "items": { + "$ref": "#/components/schemas/audioWord" + } + } + } + } + ], + "required": [ + "text" + ] + }, + "audioResponseFormat": { + "title": "AudioResponseFormat", + "description": "Defines the format of the output.", + "enum": [ + "json", + "text", + "srt", + "verbose_json", + "vtt" + ], + "type": "string", + "x-ms-enum": { + "modelAsString": true + } + }, + "createTranscriptionRequest": { + "type": "object", + "description": "Transcription request.", + "properties": { + "file": { + "type": "string", + "description": "The audio file object to transcribe.", + "format": "binary" + }, + "prompt": { + "type": "string", + "description": "An optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language." + }, + "response_format": { + "$ref": "#/components/schemas/audioResponseFormat" + }, + "temperature": { + "type": "number", + "default": 0, + "description": "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use log probability to automatically increase the temperature until certain thresholds are hit." + }, + "language": { + "type": "string", + "description": "The language of the input audio. Supplying the input language in ISO-639-1 format will improve accuracy and latency." + }, + "timestamp_granularities[]": { + "type": "array", + "description": "The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.", + "items": { + "type": "string", + "enum": [ + "word", + "segment" + ] + }, + "default": [ + "segment" + ] + } + }, + "required": [ + "file" + ] + }, + "audioSegment": { + "type": "object", + "description": "Transcription or translation segment.", + "properties": { + "id": { + "type": "integer", + "description": "Segment identifier." + }, + "seek": { + "type": "number", + "description": "Offset of the segment." + }, + "start": { + "type": "number", + "description": "Segment start offset." + }, + "end": { + "type": "number", + "description": "Segment end offset." + }, + "text": { + "type": "string", + "description": "Segment text." + }, + "tokens": { + "type": "array", + "items": { + "type": "number", + "nullable": false + }, + "description": "Tokens of the text." + }, + "temperature": { + "type": "number", + "description": "Temperature." + }, + "avg_logprob": { + "type": "number", + "description": "Average log probability." + }, + "compression_ratio": { + "type": "number", + "description": "Compression ratio." + }, + "no_speech_prob": { + "type": "number", + "description": "Probability of 'no speech'." + } + } + }, + "audioWord": { + "type": "object", + "description": "Transcription or translation word.", + "properties": { + "word": { + "type": "string", + "description": "Word" + }, + "start": { + "type": "number", + "description": "Word start offset." + }, + "end": { + "type": "number", + "description": "Word end offset." + } + } + }, + "createSpeechRequest": { + "type": "object", + "description": "Speech request.", + "properties": { + "input": { + "type": "string", + "description": "The text to synthesize audio for. The maximum length is 4096 characters.", + "maxLength": 4096 + }, + "voice": { + "type": "string", + "description": "The voice to use for speech synthesis.", + "enum": [ + "alloy", + "echo", + "fable", + "onyx", + "nova", + "shimmer" + ] + }, + "response_format": { + "type": "string", + "description": "The format to synthesize the audio in.", + "enum": [ + "mp3", + "opus", + "aac", + "flac", + "wav", + "pcm" + ] + }, + "speed": { + "description": "The speed of the synthesize audio. Select a value from `0.25` to `4.0`. `1.0` is the default.", + "type": "number", + "default": 1.0, + "minimum": 0.25, + "maximum": 4.0 + } + }, + "required": [ + "input", + "voice" + ] + }, + "imageQuality": { + "description": "The quality of the image that will be generated.", + "type": "string", + "enum": [ + "standard", + "hd" + ], + "default": "standard", + "x-ms-enum": { + "name": "Quality", + "modelAsString": true, + "values": [ + { + "value": "standard", + "description": "Standard quality creates images with standard quality.", + "name": "Standard" + }, + { + "value": "hd", + "description": "HD quality creates images with finer details and greater consistency across the image.", + "name": "HD" + } + ] + } + }, + "imagesResponseFormat": { + "description": "The format in which the generated images are returned.", + "type": "string", + "enum": [ + "url", + "b64_json" + ], + "default": "url", + "x-ms-enum": { + "name": "ImagesResponseFormat", + "modelAsString": true, + "values": [ + { + "value": "url", + "description": "The URL that provides temporary access to download the generated images.", + "name": "Url" + }, + { + "value": "b64_json", + "description": "The generated images are returned as base64 encoded string.", + "name": "Base64Json" + } + ] + } + }, + "imageSize": { + "description": "The size of the generated images.", + "type": "string", + "enum": [ + "256x256", + "512x512", + "1792x1024", + "1024x1792", + "1024x1024" + ], + "default": "1024x1024", + "x-ms-enum": { + "name": "Size", + "modelAsString": true, + "values": [ + { + "value": "256x256", + "description": "The desired size of the generated image is 256x256 pixels. Only supported for dall-e-2.", + "name": "Size256x256" + }, + { + "value": "512x512", + "description": "The desired size of the generated image is 512x512 pixels. Only supported for dall-e-2.", + "name": "Size512x512" + }, + { + "value": "1792x1024", + "description": "The desired size of the generated image is 1792x1024 pixels. Only supported for dall-e-3.", + "name": "Size1792x1024" + }, + { + "value": "1024x1792", + "description": "The desired size of the generated image is 1024x1792 pixels. Only supported for dall-e-3.", + "name": "Size1024x1792" + }, + { + "value": "1024x1024", + "description": "The desired size of the generated image is 1024x1024 pixels.", + "name": "Size1024x1024" + } + ] + } + }, + "imageStyle": { + "description": "The style of the generated images.", + "type": "string", + "enum": [ + "vivid", + "natural" + ], + "default": "vivid", + "x-ms-enum": { + "name": "Style", + "modelAsString": true, + "values": [ + { + "value": "vivid", + "description": "Vivid creates images that are hyper-realistic and dramatic.", + "name": "Vivid" + }, + { + "value": "natural", + "description": "Natural creates images that are more natural and less hyper-realistic.", + "name": "Natural" + } + ] + } + }, + "imageGenerationsRequest": { + "type": "object", + "properties": { + "prompt": { + "description": "A text description of the desired image(s). The maximum length is 4000 characters.", + "type": "string", + "format": "string", + "example": "a corgi in a field", + "minLength": 1 + }, + "n": { + "description": "The number of images to generate.", + "type": "integer", + "minimum": 1, + "maximum": 1, + "default": 1 + }, + "size": { + "$ref": "#/components/schemas/imageSize" + }, + "response_format": { + "$ref": "#/components/schemas/imagesResponseFormat" + }, + "user": { + "description": "A unique identifier representing your end-user, which can help to monitor and detect abuse.", + "type": "string", + "format": "string", + "example": "user123456" + }, + "quality": { + "$ref": "#/components/schemas/imageQuality" + }, + "style": { + "$ref": "#/components/schemas/imageStyle" + } + }, + "required": [ + "prompt" + ] + }, + "generateImagesResponse": { + "type": "object", + "properties": { + "created": { + "type": "integer", + "format": "unixtime", + "description": "The unix timestamp when the operation was created.", + "example": "1676540381" + }, + "data": { + "type": "array", + "description": "The result data of the operation, if successful", + "items": { + "$ref": "#/components/schemas/imageResult" + } + } + }, + "required": [ + "created", + "data" + ] + }, + "imageResult": { + "type": "object", + "description": "The image url or encoded image if successful, and an error otherwise.", + "properties": { + "url": { + "type": "string", + "description": "The image url.", + "example": "https://www.contoso.com" + }, + "b64_json": { + "type": "string", + "description": "The base64 encoded image" + }, + "content_filter_results": { + "$ref": "#/components/schemas/dalleContentFilterResults" + }, + "revised_prompt": { + "type": "string", + "description": "The prompt that was used to generate the image, if there was any revision to the prompt." + }, + "prompt_filter_results": { + "$ref": "#/components/schemas/dalleFilterResults" + } + } + }, + "line": { + "type": "object", + "description": "A content line object consisting of an adjacent sequence of content elements, such as words and selection marks.", + "properties": { + "text": { + "type": "string" + }, + "spans": { + "type": "array", + "description": "An array of spans that represent detected objects and its bounding box information.", + "items": { + "$ref": "#/components/schemas/span" + } + } + }, + "required": [ + "text", + "spans" + ] + }, + "span": { + "type": "object", + "description": "A span object that represents a detected object and its bounding box information.", + "properties": { + "text": { + "type": "string", + "description": "The text content of the span that represents the detected object." + }, + "offset": { + "type": "integer", + "description": "The character offset within the text where the span begins. This offset is defined as the position of the first character of the span, counting from the start of the text as Unicode codepoints." + }, + "length": { + "type": "integer", + "description": "The length of the span in characters, measured in Unicode codepoints." + }, + "polygon": { + "type": "array", + "description": "An array of objects representing points in the polygon that encloses the detected object.", + "items": { + "type": "object", + "properties": { + "x": { + "type": "number", + "description": "The x-coordinate of the point." + }, + "y": { + "type": "number", + "description": "The y-coordinate of the point." + } + } + } + } + }, + "required": [ + "text", + "offset", + "length", + "polygon" + ] + }, + "runCompletionUsage": { + "type": "object", + "description": "Usage statistics related to the run. This value will be `null` if the run is not in a terminal state (i.e. `in_progress`, `queued`, etc.).", + "properties": { + "completion_tokens": { + "type": "integer", + "description": "Number of completion tokens used over the course of the run." + }, + "prompt_tokens": { + "type": "integer", + "description": "Number of prompt tokens used over the course of the run." + }, + "total_tokens": { + "type": "integer", + "description": "Total number of tokens used (prompt + completion)." + } + }, + "required": [ + "prompt_tokens", + "completion_tokens", + "total_tokens" + ], + "nullable": true + }, + "runStepCompletionUsage": { + "type": "object", + "description": "Usage statistics related to the run step. This value will be `null` while the run step's status is `in_progress`.", + "properties": { + "completion_tokens": { + "type": "integer", + "description": "Number of completion tokens used over the course of the run step." + }, + "prompt_tokens": { + "type": "integer", + "description": "Number of prompt tokens used over the course of the run step." + }, + "total_tokens": { + "type": "integer", + "description": "Total number of tokens used (prompt + completion)." + } + }, + "required": [ + "prompt_tokens", + "completion_tokens", + "total_tokens" + ], + "nullable": true + }, + "assistantsApiResponseFormatOption": { + "description": "Specifies the format that the model must output. Compatible with GPT-4o, GPT-4 Turbo, and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the Structured Outputs guide.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.", + "oneOf": [ + { + "type": "string", + "description": "`auto` is the default value\n", + "enum": [ + "auto" + ], + "x-ms-enum": { + "name": "AssistantsApiResponseFormatOption", + "modelAsString": true, + "values": [ + { + "value": "auto" + } + ] + } + }, + { + "$ref": "#/components/schemas/ResponseFormatText" + }, + { + "$ref": "#/components/schemas/ResponseFormatJsonObject" + }, + { + "$ref": "#/components/schemas/ResponseFormatJsonSchema" + } + ] + }, + "assistantsApiResponseFormat": { + "type": "object", + "description": "An object describing the expected output of the model. If `json_object` only `function` type `tools` are allowed to be passed to the Run. If `text` the model can return text or any value needed.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "text", + "json_object" + ], + "x-ms-enum": { + "name": "AssistantsApiResponseFormat", + "modelAsString": true, + "values": [ + { + "value": "text" + }, + { + "value": "json_object" + } + ] + }, + "example": "json_object", + "default": "text", + "description": "Must be one of `text` or `json_object`." + } + } + }, + "assistantObject": { + "type": "object", + "title": "Assistant", + "description": "Represents an `assistant` that can call the model and use tools.", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `assistant`.", + "type": "string", + "enum": [ + "assistant" + ], + "x-ms-enum": { + "name": "AssistantObjectType", + "modelAsString": true, + "values": [ + { + "value": "assistant", + "description": "The object type, which is always assistant" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the assistant was created.", + "type": "integer" + }, + "name": { + "description": "The name of the assistant. The maximum length is 256 characters.\n", + "type": "string", + "maxLength": 256, + "nullable": true + }, + "description": { + "description": "The description of the assistant. The maximum length is 512 characters.\n", + "type": "string", + "maxLength": 512, + "nullable": true + }, + "model": { + "description": "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n", + "type": "string" + }, + "instructions": { + "description": "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n", + "type": "string", + "maxLength": 256000, + "nullable": true + }, + "tools": { + "description": "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n", + "default": [], + "type": "array", + "maxItems": 128, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n", + "maxItems": 1, + "items": { + "type": "string" + } + } + } + } + } + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "temperature": { + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n", + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + }, + "required": [ + "id", + "object", + "created_at", + "name", + "description", + "model", + "instructions", + "tools", + "metadata" + ] + }, + "createAssistantRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "model": { + "description": "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n", + "anyOf": [ + { + "type": "string" + } + ] + }, + "name": { + "description": "The name of the assistant. The maximum length is 256 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 256 + }, + "description": { + "description": "The description of the assistant. The maximum length is 512 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 512 + }, + "instructions": { + "description": "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 256000 + }, + "tools": { + "description": "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.\n", + "default": [], + "type": "array", + "maxItems": 128, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n", + "maxItems": 1, + "items": { + "type": "string" + } + }, + "vector_stores": { + "type": "array", + "description": "A helper to create a vector store with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n", + "maxItems": 1, + "items": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n", + "maxItems": 10000, + "items": { + "type": "string" + } + }, + "chunking_strategy": { + "type": "object", + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "oneOf": [ + { + "type": "object", + "title": "Auto Chunking Strategy", + "description": "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `auto`.", + "enum": [ + "auto" + ], + "x-ms-enum": { + "name": "AutoChunkingStrategyType", + "modelAsString": true, + "values": [ + { + "value": "auto" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "title": "Static Chunking Strategy", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `static`.", + "enum": [ + "static" + ], + "x-ms-enum": { + "name": "StaticChunkingStrategyType", + "modelAsString": true, + "values": [ + { + "value": "static" + } + ] + } + }, + "static": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_chunk_size_tokens": { + "type": "integer", + "minimum": 100, + "maximum": 4096, + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }, + "chunk_overlap_tokens": { + "type": "integer", + "description": "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + } + }, + "required": [ + "max_chunk_size_tokens", + "chunk_overlap_tokens" + ] + } + }, + "required": [ + "type", + "static" + ] + } + ] + }, + "metadata": { + "type": "object", + "description": "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "x-oaiTypeLabel": "map" + } + } + } + } + }, + "oneOf": [ + { + "required": [ + "vector_store_ids" + ] + }, + { + "required": [ + "vector_stores" + ] + } + ] + } + }, + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "temperature": { + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n", + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + }, + "required": [ + "model" + ] + }, + "modifyAssistantRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "model": { + "description": "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models/overview) for descriptions of them.\n", + "anyOf": [ + { + "type": "string" + } + ] + }, + "name": { + "description": "The name of the assistant. The maximum length is 256 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 256 + }, + "description": { + "description": "The description of the assistant. The maximum length is 512 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 512 + }, + "instructions": { + "description": "The system instructions that the assistant uses. The maximum length is 32768 characters.\n", + "type": "string", + "nullable": true, + "maxLength": 32768 + }, + "tools": { + "description": "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.\n", + "default": [], + "type": "array", + "maxItems": 128, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "Overrides the list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "Overrides the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n", + "maxItems": 1, + "items": { + "type": "string" + } + } + } + } + } + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "temperature": { + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n", + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + } + }, + "deleteAssistantResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "assistant.deleted" + ], + "x-ms-enum": { + "name": "DeleteAssistantResponseState", + "modelAsString": true, + "values": [ + { + "value": "assistant.deleted" + } + ] + } + } + }, + "required": [ + "id", + "object", + "deleted" + ] + }, + "listAssistantsResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/assistantObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "assistantToolsCode": { + "type": "object", + "title": "Code interpreter tool", + "properties": { + "type": { + "type": "string", + "description": "The type of tool being defined: `code_interpreter`", + "enum": [ + "code_interpreter" + ], + "x-ms-enum": { + "name": "assistantToolsCodeType", + "modelAsString": true, + "values": [ + { + "value": "code_interpreter" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + "assistantToolsFileSearch": { + "type": "object", + "title": "FileSearch tool", + "properties": { + "type": { + "type": "string", + "description": "The type of tool being defined: `file_search`", + "enum": [ + "file_search" + ], + "x-ms-enum": { + "name": "assistantToolsFileSearchType", + "modelAsString": true, + "values": [ + { + "value": "file_search" + } + ] + } + }, + "file_search": { + "type": "object", + "description": "Overrides for the file search tool.", + "properties": { + "max_num_results": { + "type": "integer", + "minimum": 1, + "maximum": 50, + "description": "The maximum number of results the file search tool should output. The default is 20 for gpt-4* models and 5 for gpt-3.5-turbo. This number should be between 1 and 50 inclusive.\n\nNote that the file search tool may output fewer than `max_num_results` results. \n" + } + } + } + }, + "required": [ + "type" + ] + }, + "assistantToolsFileSearchTypeOnly": { + "type": "object", + "title": "FileSearch tool", + "properties": { + "type": { + "type": "string", + "description": "The type of tool being defined: `file_search`", + "enum": [ + "file_search" + ], + "x-ms-enum": { + "name": "assistantToolsFileSearchType", + "modelAsString": true, + "values": [ + { + "value": "file_search" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + "assistantToolsFunction": { + "type": "object", + "title": "Function tool", + "properties": { + "type": { + "type": "string", + "description": "The type of tool being defined: `function`", + "enum": [ + "function" + ], + "x-ms-enum": { + "name": "assistantToolsFunction", + "modelAsString": true, + "values": [ + { + "value": "function" + } + ] + } + }, + "function": { + "type": "object", + "description": "The function definition.", + "properties": { + "description": { + "type": "string", + "description": "A description of what the function does, used by the model to choose when and how to call the function." + }, + "name": { + "type": "string", + "description": "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "parameters": { + "$ref": "#/components/schemas/chatCompletionFunctionParameters" + } + }, + "required": [ + "name", + "parameters", + "description" + ] + } + }, + "required": [ + "type", + "function" + ] + }, + "truncationObject": { + "type": "object", + "title": "Thread Truncation Controls", + "description": "Controls for how a thread will be truncated prior to the run. Use this to control the initial context window of the run.", + "properties": { + "type": { + "type": "string", + "description": "The truncation strategy to use for the thread. The default is `auto`. If set to `last_messages`, the thread will be truncated to the n most recent messages in the thread. When set to `auto`, messages in the middle of the thread will be dropped to fit the context length of the model, `max_prompt_tokens`.", + "enum": [ + "auto", + "last_messages" + ], + "x-ms-enum": { + "name": "TruncationType", + "modelAsString": true, + "values": [ + { + "value": "auto" + }, + { + "value": "last_messages" + } + ] + } + }, + "last_messages": { + "type": "integer", + "description": "The number of most recent messages from the thread when constructing the context for the run.", + "minimum": 1, + "nullable": true + } + }, + "required": [ + "type" + ] + }, + "assistantsApiToolChoiceOption": { + "description": "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tools and instead generates a message.\n`auto` is the default value and means the model can pick between generating a message or calling a tool.\nSpecifying a particular tool like `{\"type\": \"file_search\"}` or `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n", + "oneOf": [ + { + "type": "string", + "description": "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n", + "enum": [ + "none", + "auto" + ], + "x-ms-enum": { + "name": "AssistantsApiToolChoiceOption", + "modelAsString": true, + "values": [ + { + "value": "none" + }, + { + "value": "auto" + } + ] + } + }, + { + "$ref": "#/components/schemas/assistantsNamedToolChoice" + } + ] + }, + "assistantsNamedToolChoice": { + "type": "object", + "description": "Specifies a tool the model should use. Use to force the model to call a specific tool.", + "properties": { + "type": { + "type": "string", + "enum": [ + "function", + "code_interpreter", + "file_search" + ], + "description": "The type of the tool. If type is `function`, the function name must be set", + "x-ms-enum": { + "name": "AssistantsNamedToolChoiceType", + "modelAsString": true, + "values": [ + { + "value": "function" + }, + { + "value": "code_interpreter" + }, + { + "value": "file_search" + } + ] + } + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "name" + ] + } + }, + "required": [ + "type" + ] + }, + "runObject": { + "type": "object", + "title": "A run on a thread", + "description": "Represents an execution run on a [thread](/docs/api-reference/threads).", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `thread.run`.", + "type": "string", + "enum": [ + "thread.run" + ], + "x-ms-enum": { + "name": "runObjectType", + "modelAsString": true, + "values": [ + { + "value": "thread.run", + "description": "The run object type which is always thread.run" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the run was created.", + "type": "integer" + }, + "thread_id": { + "description": "The ID of the [thread](/docs/api-reference/threads) that was executed on as a part of this run.", + "type": "string" + }, + "assistant_id": { + "description": "The ID of the assistant used for execution of this run.", + "type": "string" + }, + "status": { + "description": "The status of the run, which can be either `queued`, `in_progress`, `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or `expired`.", + "type": "string", + "enum": [ + "queued", + "in_progress", + "requires_action", + "cancelling", + "cancelled", + "failed", + "completed", + "expired" + ], + "x-ms-enum": { + "name": "RunObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "queued", + "description": "The queued state" + }, + { + "value": "in_progress", + "description": "The in_progress state" + }, + { + "value": "requires_action", + "description": "The required_action state" + }, + { + "value": "cancelling", + "description": "The cancelling state" + }, + { + "value": "cancelled", + "description": "The cancelled state" + }, + { + "value": "failed", + "description": "The failed state" + }, + { + "value": "completed", + "description": "The completed state" + }, + { + "value": "expired", + "description": "The expired state" + } + ] + } + }, + "required_action": { + "type": "object", + "description": "Details on the action required to continue the run. Will be `null` if no action is required.", + "nullable": true, + "properties": { + "type": { + "description": "For now, this is always `submit_tool_outputs`.", + "type": "string", + "enum": [ + "submit_tool_outputs" + ] + }, + "submit_tool_outputs": { + "type": "object", + "description": "Details on the tool outputs needed for this run to continue.", + "properties": { + "tool_calls": { + "type": "array", + "description": "A list of the relevant tool calls.", + "items": { + "$ref": "#/components/schemas/runToolCallObject" + } + } + }, + "required": [ + "tool_calls" + ] + } + }, + "required": [ + "type", + "submit_tool_outputs" + ] + }, + "last_error": { + "type": "object", + "description": "The last error associated with this run. Will be `null` if there are no errors.", + "nullable": true, + "properties": { + "code": { + "type": "string", + "description": "One of `server_error` or `rate_limit_exceeded`.", + "enum": [ + "server_error", + "rate_limit_exceeded" + ], + "x-ms-enum": { + "name": "LastErrorCode", + "modelAsString": true, + "values": [ + { + "value": "server_error", + "description": "The server failed to respond to request due to server error" + }, + { + "value": "rate_limit_exceeded", + "description": "The server failed to respond to request due to rate limit exceeded" + } + ] + } + }, + "message": { + "type": "string", + "description": "A human-readable description of the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "expires_at": { + "description": "The Unix timestamp (in seconds) for when the run will expire.", + "type": "integer" + }, + "started_at": { + "description": "The Unix timestamp (in seconds) for when the run was started.", + "type": "integer", + "nullable": true + }, + "cancelled_at": { + "description": "The Unix timestamp (in seconds) for when the run was cancelled.", + "type": "integer", + "nullable": true + }, + "failed_at": { + "description": "The Unix timestamp (in seconds) for when the run failed.", + "type": "integer", + "nullable": true + }, + "completed_at": { + "description": "The Unix timestamp (in seconds) for when the run was completed.", + "type": "integer", + "nullable": true + }, + "incomplete_details": { + "description": "Details on why the run is incomplete. Will be `null` if the run is not incomplete.", + "type": "object", + "nullable": true, + "properties": { + "reason": { + "description": "The reason why the run is incomplete. This will point to which specific token limit was reached over the course of the run.", + "type": "string", + "enum": [ + "max_completion_tokens", + "max_prompt_tokens" + ], + "x-ms-enum": { + "name": "IncompleteDetailsReason", + "modelAsString": true, + "values": [ + { + "value": "max_completion_tokens" + }, + { + "value": "max_prompt_tokens" + } + ] + } + } + } + }, + "model": { + "description": "The model that the assistant used for this run.", + "type": "string" + }, + "instructions": { + "description": "The instructions that the assistant used for this run.", + "type": "string" + }, + "tools": { + "description": "The list of tools that the assistant used for this run.", + "default": [], + "type": "array", + "maxItems": 20, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "usage": { + "$ref": "#/components/schemas/runCompletionUsage" + }, + "temperature": { + "description": "The sampling temperature used for this run. If not set, defaults to 1.", + "type": "number", + "nullable": true + }, + "top_p": { + "description": "The nucleus sampling value used for this run. If not set, defaults to 1.", + "type": "number", + "nullable": true + }, + "max_prompt_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of prompt tokens specified to have been used over the course of the run.\n", + "minimum": 256 + }, + "max_completion_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of completion tokens specified to have been used over the course of the run.\n", + "minimum": 256 + }, + "truncation_strategy": { + "$ref": "#/components/schemas/truncationObject", + "nullable": true + }, + "tool_choice": { + "$ref": "#/components/schemas/assistantsApiToolChoiceOption", + "nullable": true + }, + "parallel_tool_calls": { + "$ref": "#/components/schemas/ParallelToolCalls", + "nullable": true + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + }, + "required": [ + "id", + "object", + "created_at", + "thread_id", + "assistant_id", + "status", + "required_action", + "last_error", + "expires_at", + "started_at", + "cancelled_at", + "failed_at", + "completed_at", + "model", + "instructions", + "tools", + "metadata", + "usage", + "incomplete_details", + "max_prompt_tokens", + "max_completion_tokens", + "truncation_strategy", + "tool_choice", + "response_format" + ] + }, + "createRunRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "assistant_id": { + "description": "The ID of the assistant to use to execute this run.", + "type": "string" + }, + "model": { + "description": "The ID of the Model to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.", + "type": "string", + "nullable": true + }, + "instructions": { + "description": "Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis.", + "type": "string", + "nullable": true + }, + "additional_instructions": { + "description": "Appends additional instructions at the end of the instructions for the run. This is useful for modifying the behavior on a per-run basis without overriding other instructions.", + "type": "string", + "nullable": true + }, + "additional_messages": { + "description": "Adds additional messages to the thread before creating the run.", + "type": "array", + "items": { + "$ref": "#/components/schemas/createMessageRequest" + }, + "nullable": true + }, + "tools": { + "description": "Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.", + "nullable": true, + "type": "array", + "maxItems": 20, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }, + "stream": { + "type": "boolean", + "nullable": true, + "description": "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + }, + "max_prompt_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n", + "minimum": 256 + }, + "max_completion_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n", + "minimum": 256 + }, + "truncation_strategy": { + "$ref": "#/components/schemas/truncationObject", + "nullable": true + }, + "tool_choice": { + "$ref": "#/components/schemas/assistantsApiToolChoiceOption", + "nullable": true + }, + "parallel_tool_calls": { + "$ref": "#/components/schemas/ParallelToolCalls" + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + }, + "required": [ + "thread_id", + "assistant_id" + ] + }, + "listRunsResponse": { + "type": "object", + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/runObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "modifyRunRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "submitToolOutputsRunRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "tool_outputs": { + "description": "A list of tools for which the outputs are being submitted.", + "type": "array", + "items": { + "type": "object", + "properties": { + "tool_call_id": { + "type": "string", + "description": "The ID of the tool call in the `required_action` object within the run object the output is being submitted for." + }, + "output": { + "type": "string", + "description": "The output of the tool call to be submitted to continue the run." + } + } + } + }, + "stream": { + "type": "boolean", + "nullable": true, + "description": "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + } + }, + "required": [ + "tool_outputs" + ] + }, + "runToolCallObject": { + "type": "object", + "description": "Tool call objects", + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call. This ID must be referenced when you submit the tool outputs in using the [Submit tool outputs to run](/docs/api-reference/runs/submitToolOutputs) endpoint." + }, + "type": { + "type": "string", + "description": "The type of tool call the output is required for. For now, this is always `function`.", + "enum": [ + "function" + ], + "x-ms-enum": { + "name": "RunToolCallObjectType", + "modelAsString": true, + "values": [ + { + "value": "function" + } + ] + } + }, + "function": { + "type": "object", + "description": "The function definition.", + "properties": { + "name": { + "type": "string", + "description": "The name of the function." + }, + "arguments": { + "type": "string", + "description": "The arguments that the model expects you to pass to the function." + } + }, + "required": [ + "name", + "arguments" + ] + } + }, + "required": [ + "id", + "type", + "function" + ] + }, + "createThreadAndRunRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "assistant_id": { + "description": "The ID of the assistant to use to execute this run.", + "type": "string" + }, + "thread": { + "$ref": "#/components/schemas/createThreadRequest", + "description": "If no thread is provided, an empty thread will be created." + }, + "model": { + "description": "The ID of the [Model](/docs/api-reference/models) to be used to execute this run. If a value is provided here, it will override the model associated with the assistant. If not, the model associated with the assistant will be used.", + "type": "string", + "nullable": true + }, + "instructions": { + "description": "Override the default system message of the assistant. This is useful for modifying the behavior on a per-run basis.", + "type": "string", + "nullable": true + }, + "tools": { + "description": "Override the tools the assistant can use for this run. This is useful for modifying the behavior on a per-run basis.", + "nullable": true, + "type": "array", + "maxItems": 20, + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearch" + }, + { + "$ref": "#/components/schemas/assistantToolsFunction" + } + ] + } + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The ID of the vector store attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n", + "maxItems": 1, + "items": { + "type": "string" + } + } + } + } + }, + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + }, + "stream": { + "type": "boolean", + "nullable": true, + "description": "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + }, + "stream_options": { + "$ref": "#/components/schemas/chatCompletionStreamOptions", + "nullable": true + }, + "max_prompt_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n", + "minimum": 256 + }, + "max_completion_tokens": { + "type": "integer", + "nullable": true, + "description": "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n", + "minimum": 256 + }, + "truncation_strategy": { + "$ref": "#/components/schemas/truncationObject", + "nullable": true + }, + "tool_choice": { + "$ref": "#/components/schemas/assistantsApiToolChoiceOption", + "nullable": true + }, + "parallel_tool_calls": { + "$ref": "#/components/schemas/ParallelToolCalls" + }, + "response_format": { + "$ref": "#/components/schemas/assistantsApiResponseFormatOption", + "nullable": true + } + }, + "required": [ + "thread_id", + "assistant_id" + ] + }, + "threadObject": { + "type": "object", + "title": "Thread", + "description": "Represents a thread that contains [messages](/docs/api-reference/messages).", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `thread`.", + "type": "string", + "enum": [ + "thread" + ], + "x-ms-enum": { + "name": "ThreadObjectType", + "modelAsString": true, + "values": [ + { + "value": "thread", + "description": "The type of thread object which is always `thread`" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the thread was created.", + "type": "integer" + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n", + "maxItems": 1, + "items": { + "type": "string" + } + } + } + } + }, + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + }, + "required": [ + "id", + "object", + "created_at", + "tool_resources", + "metadata" + ] + }, + "createThreadRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "messages": { + "description": "A list of [messages](/docs/api-reference/messages) to start the thread with.", + "type": "array", + "items": { + "$ref": "#/components/schemas/createMessageRequest" + } + }, + "tool_resources": { + "type": "object", + "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n", + "maxItems": 1, + "items": { + "type": "string" + } + }, + "vector_stores": { + "type": "array", + "description": "A helper to create a vector store with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n", + "maxItems": 1, + "items": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of file IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n", + "maxItems": 10000, + "items": { + "type": "string" + } + }, + "chunking_strategy": { + "type": "object", + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "oneOf": [ + { + "type": "object", + "title": "Auto Chunking Strategy", + "description": "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `auto`.", + "enum": [ + "auto" + ], + "x-ms-enum": { + "name": "AutoChunkingStrategyType", + "modelAsString": true, + "values": [ + { + "value": "auto" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "title": "Static Chunking Strategy", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `static`.", + "enum": [ + "static" + ], + "x-ms-enum": { + "name": "StaticChunkingStrategyType", + "modelAsString": true, + "values": [ + { + "value": "static" + } + ] + } + }, + "static": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_chunk_size_tokens": { + "type": "integer", + "minimum": 100, + "maximum": 4096, + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }, + "chunk_overlap_tokens": { + "type": "integer", + "description": "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + } + }, + "required": [ + "max_chunk_size_tokens", + "chunk_overlap_tokens" + ] + } + }, + "required": [ + "type", + "static" + ] + } + ] + }, + "metadata": { + "type": "object", + "description": "Set of 16 key-value pairs that can be attached to a vector store. This can be useful for storing additional information about the vector store in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n" + } + } + } + } + }, + "oneOf": [ + { + "required": [ + "vector_store_ids" + ] + }, + { + "required": [ + "vector_stores" + ] + } + ] + } + }, + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "modifyThreadRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "tool_resources": { + "type": "object", + "description": "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n", + "properties": { + "code_interpreter": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "description": "A list of File IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n", + "default": [], + "maxItems": 20, + "items": { + "type": "string" + } + } + } + }, + "file_search": { + "type": "object", + "properties": { + "vector_store_ids": { + "type": "array", + "description": "The vector store attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n", + "maxItems": 1, + "items": { + "type": "string" + } + } + } + } + }, + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "deleteThreadResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "thread.deleted" + ], + "x-ms-enum": { + "name": "DeleteThreadResponseObjectState", + "modelAsString": true, + "values": [ + { + "value": "thread.deleted", + "description": "The delete thread response object state which is `thread.deleted`" + } + ] + } + } + }, + "required": [ + "id", + "object", + "deleted" + ] + }, + "listThreadsResponse": { + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/threadObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "messageObject": { + "type": "object", + "title": "The message object", + "description": "Represents a message within a [thread](/docs/api-reference/threads).", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `thread.message`.", + "type": "string", + "enum": [ + "thread.message" + ], + "x-ms-enum": { + "name": "MessageObjectType", + "modelAsString": true, + "values": [ + { + "value": "thread.message", + "description": "The message object type which is `thread.message`" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the message was created.", + "type": "integer" + }, + "thread_id": { + "description": "The [thread](/docs/api-reference/threads) ID that this message belongs to.", + "type": "string" + }, + "status": { + "description": "The status of the message, which can be either `in_progress`, `incomplete`, or `completed`.", + "type": "string", + "enum": [ + "in_progress", + "incomplete", + "completed" + ], + "x-ms-enum": { + "name": "MessageObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "in_progress" + }, + { + "value": "incomplete" + }, + { + "value": "completed" + } + ] + } + }, + "incomplete_details": { + "description": "On an incomplete message, details about why the message is incomplete.", + "type": "object", + "properties": { + "reason": { + "type": "string", + "description": "The reason the message is incomplete.", + "enum": [ + "content_filter", + "max_tokens", + "run_cancelled", + "run_expired", + "run_failed" + ], + "x-ms-enum": { + "name": "MessageObjectIncompleteReason", + "modelAsString": true, + "values": [ + { + "value": "content_filter" + }, + { + "value": "max_tokens" + }, + { + "value": "run_cancelled" + }, + { + "value": "run_expired" + }, + { + "value": "run_failed" + } + ] + } + } + }, + "nullable": true, + "required": [ + "reason" + ] + }, + "completed_at": { + "description": "The Unix timestamp (in seconds) for when the message was completed.", + "type": "integer", + "nullable": true + }, + "incomplete_at": { + "description": "The Unix timestamp (in seconds) for when the message was marked as incomplete.", + "type": "integer", + "nullable": true + }, + "role": { + "description": "The entity that produced the message. One of `user` or `assistant`.", + "type": "string", + "enum": [ + "user", + "assistant" + ], + "x-ms-enum": { + "name": "MessageObjectRole", + "modelAsString": true, + "values": [ + { + "value": "user" + }, + { + "value": "assistant" + } + ] + } + }, + "content": { + "description": "The content of the message in array of text and/or images.", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/messageContentImageFileObject" + }, + { + "$ref": "#/components/schemas/messageContentTextObject" + } + ] + } + }, + "assistant_id": { + "description": "If applicable, the ID of the assistant that authored this message.", + "type": "string", + "nullable": true + }, + "run_id": { + "description": "If applicable, the ID of the [run](/docs/api-reference/runs) associated with the authoring of this message.", + "type": "string", + "nullable": true + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "The ID of the file to attach to the message." + }, + "tools": { + "description": "The tools to add this file to.", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearchTypeOnly" + } + ] + } + } + } + }, + "description": "A list of files attached to the message, and the tools they were added to.", + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + }, + "required": [ + "id", + "object", + "created_at", + "thread_id", + "status", + "incomplete_details", + "completed_at", + "incomplete_at", + "role", + "content", + "assistant_id", + "run_id", + "attachments", + "metadata" + ] + }, + "messageDeltaObject": { + "type": "object", + "title": "Message delta object", + "description": "Represents a message delta i.e. any changed fields on a message during streaming.\n", + "properties": { + "id": { + "description": "The identifier of the message, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `thread.message.delta`.", + "type": "string", + "enum": [ + "thread.message.delta" + ], + "x-ms-enum": { + "name": "MessageDeltaObjectType", + "modelAsString": true, + "values": [ + { + "value": "thread.message.delta" + } + ] + } + }, + "delta": { + "description": "The delta containing the fields that have changed on the Message.", + "type": "object", + "properties": { + "role": { + "description": "The entity that produced the message. One of `user` or `assistant`.", + "type": "string", + "enum": [ + "user", + "assistant" + ], + "x-ms-enum": { + "name": "MessageDeltaObjectDeltaRole", + "modelAsString": true, + "values": [ + { + "value": "user" + }, + { + "value": "assistant" + } + ] + } + }, + "content": { + "description": "The content of the message in array of text and/or images.", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/messageDeltaContentImageFileObject" + }, + { + "$ref": "#/components/schemas/messageDeltaContentTextObject" + } + ] + } + } + } + } + }, + "required": [ + "id", + "object", + "delta" + ] + }, + "createMessageRequest": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "assistant" + ], + "x-ms-enum": { + "name": "CreateMessageRequestRole", + "modelAsString": true, + "values": [ + { + "value": "user" + }, + { + "value": "assistant" + } + ] + }, + "description": "The role of the entity that is creating the message. Allowed values include:\n- `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.\n- `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.\n" + }, + "content": { + "type": "string", + "minLength": 1, + "maxLength": 256000, + "description": "The content of the message." + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "The ID of the file to attach to the message." + }, + "tools": { + "description": "The tools to add this file to.", + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/assistantToolsCode" + }, + { + "$ref": "#/components/schemas/assistantToolsFileSearchTypeOnly" + } + ] + } + } + } + }, + "description": "A list of files attached to the message, and the tools they should be added to.", + "required": [ + "file_id", + "tools" + ], + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "modifyMessageRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "deleteMessageResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "thread.message.deleted" + ], + "x-ms-enum": { + "name": "DeleteMessageResponseObject", + "modelAsString": true, + "values": [ + { + "value": "thread.message.deleted", + "description": "The delete message response object state" + } + ] + } + } + }, + "required": [ + "id", + "object", + "deleted" + ] + }, + "listMessagesResponse": { + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/messageObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "messageContentImageFileObject": { + "title": "Image file", + "type": "object", + "description": "References an image File in the content of a message.", + "properties": { + "type": { + "description": "Always `image_file`.", + "type": "string", + "enum": [ + "image_file" + ], + "x-ms-enum": { + "name": "MessageContentImageFileObjectType", + "modelAsString": true, + "values": [ + { + "value": "image_file", + "description": "The message content image file type" + } + ] + } + }, + "image_file": { + "type": "object", + "properties": { + "file_id": { + "description": "The File ID of the image in the message content.", + "type": "string" + } + }, + "required": [ + "file_id" + ] + } + }, + "required": [ + "type", + "image_file" + ] + }, + "messageContentTextObject": { + "title": "Text", + "type": "object", + "description": "The text content that is part of a message.", + "properties": { + "type": { + "description": "Always `text`.", + "type": "string", + "enum": [ + "text" + ], + "x-ms-enum": { + "name": "messageContentTextObjectType", + "modelAsString": true, + "values": [ + { + "value": "text", + "description": "The message content text Object type" + } + ] + } + }, + "text": { + "type": "object", + "properties": { + "value": { + "description": "The data that makes up the text.", + "type": "string" + }, + "annotations": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/messageContentTextAnnotationsFileCitationObject" + }, + { + "$ref": "#/components/schemas/messageContentTextAnnotationsFilePathObject" + } + ] + } + } + }, + "required": [ + "value", + "annotations" + ] + } + }, + "required": [ + "type", + "text" + ] + }, + "messageContentTextAnnotationsFileCitationObject": { + "title": "File citation", + "type": "object", + "description": "A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the \"retrieval\" tool to search files.", + "properties": { + "type": { + "description": "Always `file_citation`.", + "type": "string", + "enum": [ + "file_citation" + ], + "x-ms-enum": { + "name": "FileCitationObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_citation", + "description": "The file citation object type" + } + ] + } + }, + "text": { + "description": "The text in the message content that needs to be replaced.", + "type": "string" + }, + "file_citation": { + "type": "object", + "properties": { + "file_id": { + "description": "The ID of the specific File the citation is from.", + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "start_index": { + "type": "integer", + "minimum": 0 + }, + "end_index": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "type", + "text", + "file_citation", + "start_index", + "end_index" + ] + }, + "messageContentTextAnnotationsFilePathObject": { + "title": "File path", + "type": "object", + "description": "A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.", + "properties": { + "type": { + "description": "Always `file_path`.", + "type": "string", + "enum": [ + "file_path" + ], + "x-ms-enum": { + "name": "FilePathObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_path", + "description": "The file path object type" + } + ] + } + }, + "text": { + "description": "The text in the message content that needs to be replaced.", + "type": "string" + }, + "file_path": { + "type": "object", + "properties": { + "file_id": { + "description": "The ID of the file that was generated.", + "type": "string" + } + }, + "required": [ + "file_id" + ] + }, + "start_index": { + "type": "integer", + "minimum": 0 + }, + "end_index": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "type", + "text", + "file_path", + "start_index", + "end_index" + ] + }, + "messageDeltaContentImageFileObject": { + "title": "Image file", + "type": "object", + "description": "References an image File in the content of a message.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the content part in the message." + }, + "type": { + "description": "Always `image_file`.", + "type": "string", + "enum": [ + "image_file" + ], + "x-ms-enum": { + "name": "MessageDeltaContentImageFileObjectType", + "modelAsString": true, + "values": [ + { + "value": "image_file" + } + ] + } + }, + "image_file": { + "type": "object", + "properties": { + "file_id": { + "description": "The File ID of the image in the message content.", + "type": "string" + } + } + } + }, + "required": [ + "index", + "type" + ] + }, + "messageDeltaContentTextObject": { + "title": "Text", + "type": "object", + "description": "The text content that is part of a message.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the content part in the message." + }, + "type": { + "description": "Always `text`.", + "type": "string", + "enum": [ + "text" + ], + "x-ms-enum": { + "name": "MessageDeltaContentTextObjectType", + "modelAsString": true, + "values": [ + { + "value": "text" + } + ] + } + }, + "text": { + "type": "object", + "properties": { + "value": { + "description": "The data that makes up the text.", + "type": "string" + }, + "annotations": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/messageDeltaContentTextAnnotationsFileCitationObject" + }, + { + "$ref": "#/components/schemas/messageDeltaContentTextAnnotationsFilePathObject" + } + ] + } + } + } + } + }, + "required": [ + "index", + "type" + ] + }, + "messageDeltaContentTextAnnotationsFileCitationObject": { + "title": "File citation", + "type": "object", + "description": "A citation within the message that points to a specific quote from a specific File associated with the assistant or the message. Generated when the assistant uses the \"file_search\" tool to search files.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the annotation in the text content part." + }, + "type": { + "description": "Always `file_citation`.", + "type": "string", + "enum": [ + "file_citation" + ], + "x-ms-enum": { + "name": "MessageDeltaContentTextAnnotationsFileCitationObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_citation" + } + ] + } + }, + "text": { + "description": "The text in the message content that needs to be replaced.", + "type": "string" + }, + "file_citation": { + "type": "object", + "properties": { + "file_id": { + "description": "The ID of the specific File the citation is from.", + "type": "string" + }, + "quote": { + "description": "The specific quote in the file.", + "type": "string" + } + } + }, + "start_index": { + "type": "integer", + "minimum": 0 + }, + "end_index": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "index", + "type" + ] + }, + "messageDeltaContentTextAnnotationsFilePathObject": { + "title": "File path", + "type": "object", + "description": "A URL for the file that's generated when the assistant used the `code_interpreter` tool to generate a file.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the annotation in the text content part." + }, + "type": { + "description": "Always `file_path`.", + "type": "string", + "enum": [ + "file_path" + ], + "x-ms-enum": { + "name": "MessageDeltaContentTextAnnotationsFilePathObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_path" + } + ] + } + }, + "text": { + "description": "The text in the message content that needs to be replaced.", + "type": "string" + }, + "file_path": { + "type": "object", + "properties": { + "file_id": { + "description": "The ID of the file that was generated.", + "type": "string" + } + } + }, + "start_index": { + "type": "integer", + "minimum": 0 + }, + "end_index": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "index", + "type" + ] + }, + "runStepObject": { + "type": "object", + "title": "Run steps", + "description": "Represents a step in execution of a run.\n", + "properties": { + "id": { + "description": "The identifier of the run step, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `assistant.run.step``.", + "type": "string", + "enum": [ + "assistant.run.step" + ], + "x-ms-enum": { + "name": "RunStepObjectType", + "modelAsString": true, + "values": [ + { + "value": "assistant.run.step", + "description": "The object type, which is always `assistant.run.step`" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the run step was created.", + "type": "integer" + }, + "assistant_id": { + "description": "The ID of the [assistant](/docs/api-reference/assistants) associated with the run step.", + "type": "string" + }, + "thread_id": { + "description": "The ID of the [thread](/docs/api-reference/threads) that was run.", + "type": "string" + }, + "run_id": { + "description": "The ID of the [run](/docs/api-reference/runs) that this run step is a part of.", + "type": "string" + }, + "type": { + "description": "The type of run step, which can be either `message_creation` or `tool_calls`.", + "type": "string", + "enum": [ + "message_creation", + "tool_calls" + ], + "x-ms-enum": { + "name": "RunStepObjectType", + "modelAsString": true, + "values": [ + { + "value": "message_creation", + "description": "The message_creation run step" + }, + { + "value": "tool_calls", + "description": "The tool_calls run step" + } + ] + } + }, + "status": { + "description": "The status of the run, which can be either `in_progress`, `cancelled`, `failed`, `completed`, or `expired`.", + "type": "string", + "enum": [ + "in_progress", + "cancelled", + "failed", + "completed", + "expired" + ], + "x-ms-enum": { + "name": "RunStepObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "in_progress", + "description": "The in_progress run status" + }, + { + "value": "cancelled", + "description": "The cancelled run status" + }, + { + "value": "failed", + "description": "The cancelled run status" + }, + { + "value": "completed", + "description": "The cancelled run status" + }, + { + "value": "expired", + "description": "The cancelled run status" + } + ] + } + }, + "step_details": { + "type": "object", + "description": "The details of the run step.", + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDetailsMessageCreationObject" + }, + { + "$ref": "#/components/schemas/runStepDetailsToolCallsObject" + } + ] + }, + "last_error": { + "type": "object", + "description": "The last error associated with this run step. Will be `null` if there are no errors.", + "nullable": true, + "properties": { + "code": { + "type": "string", + "description": "One of `server_error` or `rate_limit_exceeded`.", + "enum": [ + "server_error", + "rate_limit_exceeded" + ], + "x-ms-enum": { + "name": "LastErrorCode", + "modelAsString": true, + "values": [ + { + "value": "server_error", + "description": "The server_error" + }, + { + "value": "rate_limit_exceeded", + "description": "The rate_limit_exceeded status" + } + ] + } + }, + "message": { + "type": "string", + "description": "A human-readable description of the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "expired_at": { + "description": "The Unix timestamp (in seconds) for when the run step expired. A step is considered expired if the parent run is expired.", + "type": "integer", + "nullable": true + }, + "cancelled_at": { + "description": "The Unix timestamp (in seconds) for when the run step was cancelled.", + "type": "integer", + "nullable": true + }, + "failed_at": { + "description": "The Unix timestamp (in seconds) for when the run step failed.", + "type": "integer", + "nullable": true + }, + "completed_at": { + "description": "The Unix timestamp (in seconds) for when the run step completed.", + "type": "integer", + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + }, + "required": [ + "id", + "object", + "created_at", + "assistant_id", + "thread_id", + "run_id", + "type", + "status", + "step_details", + "last_error", + "expired_at", + "cancelled_at", + "failed_at", + "completed_at", + "metadata" + ] + }, + "runStepDeltaObject": { + "type": "object", + "title": "Run step delta object", + "description": "Represents a run step delta i.e. any changed fields on a run step during streaming.\n", + "properties": { + "id": { + "description": "The identifier of the run step, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `thread.run.step.delta`.", + "type": "string", + "enum": [ + "thread.run.step.delta" + ], + "x-ms-enum": { + "name": "RunStepDeltaObjectType", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.delta" + } + ] + } + }, + "delta": { + "description": "The delta containing the fields that have changed on the run step.", + "type": "object", + "properties": { + "step_details": { + "type": "object", + "description": "The details of the run step.", + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsMessageCreationObject" + }, + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsObject" + } + ] + } + } + } + }, + "required": [ + "id", + "object", + "delta" + ] + }, + "listRunStepsResponse": { + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "runStepDetailsMessageCreationObject": { + "title": "Message creation", + "type": "object", + "description": "Details of the message creation by the run step.", + "properties": { + "type": { + "description": "Always `message_creation``.", + "type": "string", + "enum": [ + "message_creation" + ], + "x-ms-enum": { + "name": "RunStepDetailsMessageCreationObjectType", + "modelAsString": true, + "values": [ + { + "value": "message_creation" + } + ] + } + }, + "message_creation": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "The ID of the message that was created by this run step." + } + }, + "required": [ + "message_id" + ] + } + }, + "required": [ + "type", + "message_creation" + ] + }, + "runStepDeltaStepDetailsMessageCreationObject": { + "title": "Message creation", + "type": "object", + "description": "Details of the message creation by the run step.", + "properties": { + "type": { + "description": "Always `message_creation`.", + "type": "string", + "enum": [ + "message_creation" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsMessageCreationObjectType", + "modelAsString": true, + "values": [ + { + "value": "message_creation" + } + ] + } + }, + "message_creation": { + "type": "object", + "properties": { + "message_id": { + "type": "string", + "description": "The ID of the message that was created by this run step." + } + } + } + }, + "required": [ + "type" + ] + }, + "runStepDetailsToolCallsObject": { + "title": "Tool calls", + "type": "object", + "description": "Details of the tool call.", + "properties": { + "type": { + "description": "Always `tool_calls`.", + "type": "string", + "enum": [ + "tool_calls" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsObjectType", + "modelAsString": true, + "values": [ + { + "value": "tool_calls" + } + ] + } + }, + "tool_calls": { + "type": "array", + "description": "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `retrieval` or `function`.\n", + "items": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDetailsToolCallsCodeObject" + }, + { + "$ref": "#/components/schemas/runStepDetailsToolCallsFileSearchObject" + }, + { + "$ref": "#/components/schemas/runStepDetailsToolCallsFunctionObject" + } + ] + } + } + }, + "required": [ + "type", + "tool_calls" + ] + }, + "runStepDeltaStepDetailsToolCallsObject": { + "title": "Tool calls", + "type": "object", + "description": "Details of the tool call.", + "properties": { + "type": { + "description": "Always `tool_calls`.", + "type": "string", + "enum": [ + "tool_calls" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsToolCallsObjectType", + "modelAsString": true, + "values": [ + { + "value": "tool_calls" + } + ] + } + }, + "tool_calls": { + "type": "array", + "description": "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search` or `function`.\n", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsCodeObject" + }, + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsFileSearchObject" + }, + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsFunctionObject" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + "runStepDetailsToolCallsCodeObject": { + "title": "Code interpreter tool call", + "type": "object", + "description": "Details of the Code Interpreter tool call the run step was involved in.", + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `code_interpreter` for this type of tool call.", + "enum": [ + "code_interpreter" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsCodeObjectType", + "modelAsString": true, + "values": [ + { + "value": "code_interpreter" + } + ] + } + }, + "code_interpreter": { + "type": "object", + "description": "The Code Interpreter tool call definition.", + "required": [ + "input", + "outputs" + ], + "properties": { + "input": { + "type": "string", + "description": "The input to the Code Interpreter tool call." + }, + "outputs": { + "type": "array", + "description": "The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.", + "items": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDetailsToolCallsCodeOutputLogsObject" + }, + { + "$ref": "#/components/schemas/runStepDetailsToolCallsCodeOutputImageObject" + } + ] + } + } + } + } + }, + "required": [ + "id", + "type", + "code_interpreter" + ] + }, + "runStepDeltaStepDetailsToolCallsCodeObject": { + "title": "Code interpreter tool call", + "type": "object", + "description": "Details of the Code Interpreter tool call the run step was involved in.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the tool call in the tool calls array." + }, + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `code_interpreter` for this type of tool call.", + "enum": [ + "code_interpreter" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsToolCallsCodeObjectType", + "modelAsString": true, + "values": [ + { + "value": "code_interpreter" + } + ] + } + }, + "code_interpreter": { + "type": "object", + "description": "The Code Interpreter tool call definition.", + "properties": { + "input": { + "type": "string", + "description": "The input to the Code Interpreter tool call." + }, + "outputs": { + "type": "array", + "description": "The outputs from the Code Interpreter tool call. Code Interpreter can output one or more items, including text (`logs`) or images (`image`). Each of these are represented by a different object type.", + "items": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputLogsObject" + }, + { + "$ref": "#/components/schemas/runStepDeltaStepDetailsToolCallsCodeOutputImageObject" + } + ] + } + } + } + } + }, + "required": [ + "index", + "type" + ] + }, + "runStepDetailsToolCallsCodeOutputLogsObject": { + "title": "Code interpreter log output", + "type": "object", + "description": "Text output from the Code Interpreter tool call as part of a run step.", + "properties": { + "type": { + "description": "Always `logs`.", + "type": "string", + "enum": [ + "logs" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsCodeOutputLogsObjectType", + "modelAsString": true, + "values": [ + { + "value": "logs" + } + ] + } + }, + "logs": { + "type": "string", + "description": "The text output from the Code Interpreter tool call." + } + }, + "required": [ + "type", + "logs" + ] + }, + "runStepDeltaStepDetailsToolCallsCodeOutputLogsObject": { + "title": "Code interpreter log output", + "type": "object", + "description": "Text output from the Code Interpreter tool call as part of a run step.", + "properties": { + "index": { + "type": "integer", + "description": "The index of the output in the outputs array." + }, + "type": { + "description": "Always `logs`.", + "type": "string", + "enum": [ + "logs" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsToolCallsCodeOutputLogsObjectType", + "modelAsString": true, + "values": [ + { + "value": "logs" + } + ] + } + }, + "logs": { + "type": "string", + "description": "The text output from the Code Interpreter tool call." + } + }, + "required": [ + "index", + "type" + ] + }, + "runStepDetailsToolCallsCodeOutputImageObject": { + "title": "Code interpreter image output", + "type": "object", + "properties": { + "type": { + "description": "Always `image`.", + "type": "string", + "enum": [ + "image" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsCodeOutputImageObjectType", + "modelAsString": true, + "values": [ + { + "value": "image" + } + ] + } + }, + "image": { + "type": "object", + "properties": { + "file_id": { + "description": "The File ID of the image.", + "type": "string" + } + }, + "required": [ + "file_id" + ] + } + }, + "required": [ + "type", + "image" + ] + }, + "runStepDeltaStepDetailsToolCallsCodeOutputImageObject": { + "title": "Code interpreter image output", + "type": "object", + "properties": { + "index": { + "type": "integer", + "description": "The index of the output in the outputs array." + }, + "type": { + "description": "Always `image`.", + "type": "string", + "enum": [ + "image" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsToolCallsCodeOutputImageObject", + "modelAsString": true, + "values": [ + { + "value": "image" + } + ] + } + }, + "image": { + "type": "object", + "properties": { + "file_id": { + "description": "The file ID of the image.", + "type": "string" + } + } + } + }, + "required": [ + "index", + "type" + ] + }, + "runStepDetailsToolCallsFileSearchObject": { + "title": "File search tool call", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call object." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `file_search` for this type of tool call.", + "enum": [ + "file_search" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsFileSearchObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_search" + } + ] + } + }, + "file_search": { + "type": "object", + "description": "For now, this is always going to be an empty object.", + "properties": { + "results": { + "type": "array", + "description": "The results of the file search.", + "items": { + "$ref": "#/components/schemas/runStepDetailsToolCallsFileSearchResultObject" + } + } + } + } + }, + "required": [ + "id", + "type", + "file_search" + ] + }, + "runStepDetailsToolCallsFileSearchResultObject": { + "title": "File search tool call result", + "type": "object", + "description": "A result instance of the file search.", + "x-oaiTypeLabel": "map", + "properties": { + "file_id": { + "type": "string", + "description": "The ID of the file that result was found in." + }, + "file_name": { + "type": "string", + "description": "The name of the file that result was found in." + }, + "score": { + "type": "number", + "description": "The score of the result. All values must be a floating point number between 0 and 1.", + "minimum": 0, + "maximum": 1 + }, + "content": { + "type": "array", + "description": "The content of the result that was found. The content is only included if requested via the include query parameter.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of the content.", + "enum": [ + "text" + ] + }, + "text": { + "type": "string", + "description": "The text content of the file." + } + } + } + } + }, + "required": [ + "file_id", + "file_name", + "score" + ] + }, + "runStepDeltaStepDetailsToolCallsFileSearchObject": { + "title": "File search tool call", + "type": "object", + "properties": { + "index": { + "type": "integer", + "description": "The index of the tool call in the tool calls array." + }, + "id": { + "type": "string", + "description": "The ID of the tool call object." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `retrieval` for this type of tool call.", + "enum": [ + "file_search" + ], + "x-ms-enum": { + "name": "RunStepDeltaStepDetailsToolCallsFileSearchObjectType", + "modelAsString": true, + "values": [ + { + "value": "file_search" + } + ] + } + }, + "file_search": { + "type": "object", + "description": "For now, this is always going to be an empty object." + } + }, + "required": [ + "index", + "type", + "file_search" + ] + }, + "runStepDetailsToolCallsFunctionObject": { + "type": "object", + "title": "Function tool call", + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call object." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `function` for this type of tool call.", + "enum": [ + "function" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsFunctionObjectType", + "modelAsString": true, + "values": [ + { + "value": "function" + } + ] + } + }, + "function": { + "type": "object", + "description": "The definition of the function that was called.", + "properties": { + "name": { + "type": "string", + "description": "The name of the function." + }, + "arguments": { + "type": "string", + "description": "The arguments passed to the function." + }, + "output": { + "type": "string", + "description": "The output of the function. This will be `null` if the outputs have not been [submitted](/docs/api-reference/runs/submitToolOutputs) yet.", + "nullable": true + } + }, + "required": [ + "name", + "arguments", + "output" + ] + } + }, + "required": [ + "id", + "type", + "function" + ] + }, + "runStepDeltaStepDetailsToolCallsFunctionObject": { + "type": "object", + "title": "Function tool call", + "properties": { + "index": { + "type": "integer", + "description": "The index of the tool call in the tool calls array." + }, + "id": { + "type": "string", + "description": "The ID of the tool call object." + }, + "type": { + "type": "string", + "description": "The type of tool call. This is always going to be `function` for this type of tool call.", + "enum": [ + "function" + ], + "x-ms-enum": { + "name": "RunStepDetailsToolCallsFunctionObjectType", + "modelAsString": true, + "values": [ + { + "value": "function" + } + ] + } + }, + "function": { + "type": "object", + "description": "The definition of the function that was called.", + "properties": { + "name": { + "type": "string", + "description": "The name of the function." + }, + "arguments": { + "type": "string", + "description": "The arguments passed to the function." + }, + "output": { + "type": "string", + "description": "The output of the function. This will be `null` if the outputs have not been submitted yet.", + "nullable": true + } + } + } + }, + "required": [ + "index", + "type" + ] + }, + "vectorStoreExpirationAfter": { + "type": "object", + "title": "Vector store expiration policy", + "description": "The expiration policy for a vector store.", + "properties": { + "anchor": { + "description": "Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`.", + "type": "string", + "enum": [ + "last_active_at" + ], + "x-ms-enum": { + "name": "VectorStoreExpirationAfterAnchor", + "modelAsString": true, + "values": [ + { + "value": "last_active_at", + "description": "The anchor timestamp after which the expiration policy applies." + } + ] + } + }, + "days": { + "description": "The number of days after the anchor time that the vector store will expire.", + "type": "integer", + "minimum": 1, + "maximum": 365 + } + }, + "required": [ + "anchor", + "days" + ] + }, + "vectorStoreObject": { + "type": "object", + "title": "Vector store", + "description": "A vector store is a collection of processed files can be used by the `file_search` tool.", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `vector_store`.", + "type": "string", + "enum": [ + "vector_store" + ] + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the vector store was created.", + "type": "integer" + }, + "name": { + "description": "The name of the vector store.", + "type": "string" + }, + "usage_bytes": { + "description": "The total number of bytes used by the files in the vector store.", + "type": "integer" + }, + "file_counts": { + "type": "object", + "properties": { + "in_progress": { + "description": "The number of files that are currently being processed.", + "type": "integer" + }, + "completed": { + "description": "The number of files that have been successfully processed.", + "type": "integer" + }, + "failed": { + "description": "The number of files that have failed to process.", + "type": "integer" + }, + "cancelled": { + "description": "The number of files that were cancelled.", + "type": "integer" + }, + "total": { + "description": "The total number of files.", + "type": "integer" + } + }, + "required": [ + "in_progress", + "completed", + "failed", + "cancelled", + "total" + ] + }, + "status": { + "description": "The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use.", + "type": "string", + "enum": [ + "expired", + "in_progress", + "completed" + ], + "x-ms-enum": { + "name": "VectorStoreObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "expired" + }, + { + "value": "in_progress" + }, + { + "value": "completed" + } + ] + } + }, + "expires_after": { + "$ref": "#/components/schemas/vectorStoreExpirationAfter" + }, + "expires_at": { + "description": "The Unix timestamp (in seconds) for when the vector store will expire.", + "type": "integer", + "nullable": true + }, + "last_active_at": { + "description": "The Unix timestamp (in seconds) for when the vector store was last active.", + "type": "integer", + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + }, + "required": [ + "id", + "object", + "usage_bytes", + "created_at", + "status", + "last_active_at", + "name", + "bytes", + "file_counts", + "metadata" + ] + }, + "createVectorStoreRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "file_ids": { + "description": "A list of file IDs that the vector store should use. Useful for tools like `file_search` that can access files.", + "type": "array", + "maxItems": 500, + "items": { + "type": "string" + } + }, + "name": { + "description": "The name of the vector store.", + "type": "string" + }, + "expires_after": { + "$ref": "#/components/schemas/vectorStoreExpirationAfter" + }, + "chunking_strategy": { + "type": "object", + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.", + "oneOf": [ + { + "$ref": "#/components/schemas/autoChunkingStrategyRequestParam" + }, + { + "$ref": "#/components/schemas/staticChunkingStrategyRequestParam" + } + ] + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "updateVectorStoreRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "description": "The name of the vector store.", + "type": "string", + "nullable": true + }, + "expires_after": { + "$ref": "#/components/schemas/vectorStoreExpirationAfter", + "nullable": true + }, + "metadata": { + "description": "Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.\n", + "type": "object", + "nullable": true + } + } + }, + "listVectorStoresResponse": { + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/vectorStoreObject" + } + }, + "first_id": { + "type": "string", + "example": "vs_abc123" + }, + "last_id": { + "type": "string", + "example": "vs_abc456" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "deleteVectorStoreResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "vector_store.deleted" + ], + "x-ms-enum": { + "name": "DeleteVectorStoreResponseObject", + "modelAsString": true, + "values": [ + { + "value": "vector_store.deleted", + "description": "The delete vector store response object state" + } + ] + } + } + }, + "required": [ + "id", + "object", + "deleted" + ] + }, + "vectorStoreFileObject": { + "type": "object", + "title": "Vector store files", + "description": "A list of files attached to a vector store.", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `vector_store.file`.", + "type": "string", + "enum": [ + "vector_store.file" + ], + "x-ms-enum": { + "name": "VectorStoreFileObjectType", + "modelAsString": true, + "values": [ + { + "value": "vector_store.file" + } + ] + } + }, + "usage_bytes": { + "description": "The total vector store usage in bytes. Note that this may be different from the original file size.", + "type": "integer" + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the vector store file was created.", + "type": "integer" + }, + "vector_store_id": { + "description": "The ID of the vector store that the file is attached to.", + "type": "string" + }, + "status": { + "description": "The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use.", + "type": "string", + "enum": [ + "in_progress", + "completed", + "cancelled", + "failed" + ], + "x-ms-enum": { + "name": "VectorStoreFileObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "in_progress" + }, + { + "value": "completed" + }, + { + "value": "cancelled" + }, + { + "value": "failed" + } + ] + } + }, + "last_error": { + "type": "object", + "description": "The last error associated with this vector store file. Will be `null` if there are no errors.", + "nullable": true, + "properties": { + "code": { + "type": "string", + "description": "One of `server_error` or `invalid_file` or `unsupported_file`.", + "enum": [ + "server_error", + "unsupported_file", + "invalid_file" + ], + "x-ms-enum": { + "name": "VectorStoreFileObjectLastErrorCode", + "modelAsString": true, + "values": [ + { + "value": "server_error" + }, + { + "value": "unsupported_file" + }, + { + "value": "invalid_file" + } + ] + } + }, + "message": { + "type": "string", + "description": "A human-readable description of the error." + } + }, + "required": [ + "code", + "message" + ] + }, + "chunking_strategy": { + "type": "object", + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty.", + "oneOf": [ + { + "$ref": "#/components/schemas/autoChunkingStrategyRequestParam" + }, + { + "$ref": "#/components/schemas/staticChunkingStrategyRequestParam" + } + ] + } + }, + "required": [ + "id", + "object", + "usage_bytes", + "created_at", + "vector_store_id", + "status", + "last_error" + ] + }, + "otherChunkingStrategyResponseParam": { + "type": "object", + "title": "Other Chunking Strategy", + "description": "This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API.", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `other`.", + "enum": [ + "other" + ], + "x-ms-enum": { + "name": "OtherChunkingStrategyResponseParamType", + "modelAsString": true, + "values": [ + { + "value": "other" + } + ] + } + } + }, + "required": [ + "type" + ] + }, + "staticChunkingStrategyResponseParam": { + "type": "object", + "title": "Static Chunking Strategy", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `static`.", + "enum": [ + "static" + ], + "x-ms-enum": { + "name": "StaticChunkingStrategyResponseParamType", + "modelAsString": true, + "values": [ + { + "value": "static" + } + ] + } + }, + "static": { + "$ref": "#/components/schemas/staticChunkingStrategy" + } + }, + "required": [ + "type", + "static" + ] + }, + "staticChunkingStrategy": { + "type": "object", + "additionalProperties": false, + "properties": { + "max_chunk_size_tokens": { + "type": "integer", + "minimum": 100, + "maximum": 4096, + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }, + "chunk_overlap_tokens": { + "type": "integer", + "description": "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + } + }, + "required": [ + "max_chunk_size_tokens", + "chunk_overlap_tokens" + ] + }, + "autoChunkingStrategyRequestParam": { + "type": "object", + "title": "Auto Chunking Strategy", + "description": "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`.", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `auto`.", + "enum": [ + "auto" + ] + } + }, + "required": [ + "type" + ] + }, + "staticChunkingStrategyRequestParam": { + "type": "object", + "title": "Static Chunking Strategy", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "description": "Always `static`.", + "enum": [ + "static" + ] + }, + "static": { + "$ref": "#/components/schemas/staticChunkingStrategy" + } + }, + "required": [ + "type", + "static" + ] + }, + "chunkingStrategyRequestParam": { + "type": "object", + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy.", + "oneOf": [ + { + "$ref": "#/components/schemas/autoChunkingStrategyRequestParam" + }, + { + "$ref": "#/components/schemas/staticChunkingStrategyRequestParam" + } + ] + }, + "createVectorStoreFileRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "file_id": { + "description": "A File ID that the vector store should use. Useful for tools like `file_search` that can access files.", + "type": "string" + }, + "chunking_strategy": { + "$ref": "#/components/schemas/chunkingStrategyRequestParam" + } + }, + "required": [ + "file_id" + ] + }, + "listVectorStoreFilesResponse": { + "properties": { + "object": { + "type": "string", + "example": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/vectorStoreFileObject" + } + }, + "first_id": { + "type": "string", + "example": "file-abc123" + }, + "last_id": { + "type": "string", + "example": "file-abc456" + }, + "has_more": { + "type": "boolean", + "example": false + } + }, + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ] + }, + "deleteVectorStoreFileResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "vector_store.file.deleted" + ], + "x-ms-enum": { + "name": "DeleteVectorStoreFileResponseObject", + "modelAsString": true, + "values": [ + { + "value": "vector_store.file.deleted" + } + ] + } + } + }, + "required": [ + "id", + "object", + "deleted" + ] + }, + "vectorStoreFileBatchObject": { + "type": "object", + "title": "Vector store file batch", + "description": "A batch of files attached to a vector store.", + "properties": { + "id": { + "description": "The identifier, which can be referenced in API endpoints.", + "type": "string" + }, + "object": { + "description": "The object type, which is always `vector_store.file_batch`.", + "type": "string", + "enum": [ + "vector_store.files_batch" + ], + "x-ms-enum": { + "name": "VectorStoreFileBatchObjectType", + "modelAsString": true, + "values": [ + { + "value": "vector_store.files_batch" + } + ] + } + }, + "created_at": { + "description": "The Unix timestamp (in seconds) for when the vector store files batch was created.", + "type": "integer" + }, + "vector_store_id": { + "description": "The ID of the vector store that the File is attached to.", + "type": "string" + }, + "status": { + "description": "The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`.", + "type": "string", + "enum": [ + "in_progress", + "completed", + "cancelled", + "failed" + ], + "x-ms-enum": { + "name": "VectorStoreFileBatchObjectStatus", + "modelAsString": true, + "values": [ + { + "value": "in_progress" + }, + { + "value": "completed" + }, + { + "value": "cancelled" + }, + { + "value": "failed" + } + ] + } + }, + "file_counts": { + "type": "object", + "properties": { + "in_progress": { + "description": "The number of files that are currently being processed.", + "type": "integer" + }, + "completed": { + "description": "The number of files that have been processed.", + "type": "integer" + }, + "failed": { + "description": "The number of files that have failed to process.", + "type": "integer" + }, + "cancelled": { + "description": "The number of files that where cancelled.", + "type": "integer" + }, + "total": { + "description": "The total number of files.", + "type": "integer" + } + }, + "required": [ + "in_progress", + "completed", + "cancelled", + "failed", + "total" + ] + } + }, + "required": [ + "id", + "object", + "created_at", + "vector_store_id", + "status", + "file_counts" + ] + }, + "createVectorStoreFileBatchRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "file_ids": { + "description": "A list of File IDs that the vector store should use. Useful for tools like `file_search` that can access files.", + "type": "array", + "minItems": 1, + "maxItems": 500, + "items": { + "type": "string" + } + }, + "chunking_strategy": { + "$ref": "#/components/schemas/chunkingStrategyRequestParam" + } + }, + "required": [ + "file_ids" + ] + }, + "assistantStreamEvent": { + "description": "Represents an event emitted when streaming a Run.\n\nEach event in a server-sent events stream has an `event` and `data` property:\n\n```\nevent: thread.created\ndata: {\"id\": \"thread_123\", \"object\": \"thread\", ...}\n```\n\nWe emit events whenever a new object is created, transitions to a new state, or is being\nstreamed in parts (deltas). For example, we emit `thread.run.created` when a new run\nis created, `thread.run.completed` when a run completes, and so on. When an Assistant chooses\nto create a message during a run, we emit a `thread.message.created event`, a\n`thread.message.in_progress` event, many `thread.message.delta` events, and finally a\n`thread.message.completed` event.\n\nWe may add additional events over time, so we recommend handling unknown events gracefully\nin your code.", + "oneOf": [ + { + "$ref": "#/components/schemas/threadStreamEvent" + }, + { + "$ref": "#/components/schemas/runStreamEvent" + }, + { + "$ref": "#/components/schemas/runStepStreamEvent" + }, + { + "$ref": "#/components/schemas/messageStreamEvent" + }, + { + "$ref": "#/components/schemas/errorEvent" + }, + { + "$ref": "#/components/schemas/doneEvent" + } + ] + }, + "threadStreamEvent": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.created" + ], + "x-ms-enum": { + "name": "ThreadStreamEventEnum", + "modelAsString": true, + "values": [ + { + "value": "thread.created", + "description": "The thread created event" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/threadObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a new thread is created." + } + ] + }, + "runStreamEvent": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.created" + ], + "x-ms-enum": { + "name": "RunStreamEventCreated", + "modelAsString": true, + "values": [ + { + "value": "thread.run.created" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a new run is created." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.queued" + ], + "x-ms-enum": { + "name": "RunStreamEventQueued", + "modelAsString": true, + "values": [ + { + "value": "thread.run.queued" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run moves to a `queued` status." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.in_progress" + ], + "x-ms-enum": { + "name": "RunStreamEventInProgress", + "modelAsString": true, + "values": [ + { + "value": "thread.run.in_progress" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run moves to an `in_progress` status." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.requires_action" + ], + "x-ms-enum": { + "name": "RunStreamEventRequiresAction", + "modelAsString": true, + "values": [ + { + "value": "thread.run.requires_action" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run moves to a `requires_action` status." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.completed" + ], + "x-ms-enum": { + "name": "RunStreamEventCompleted", + "modelAsString": true, + "values": [ + { + "value": "thread.run.completed" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run is completed." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.failed" + ], + "x-ms-enum": { + "name": "RunStreamEventFailed", + "modelAsString": true, + "values": [ + { + "value": "thread.run.failed" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run fails." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.cancelling" + ], + "x-ms-enum": { + "name": "RunStreamEventCancelling", + "modelAsString": true, + "values": [ + { + "value": "thread.run.cancelling" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run moves to a `cancelling` status." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.cancelled" + ], + "x-ms-enum": { + "name": "RunStreamEventCancelled", + "modelAsString": true, + "values": [ + { + "value": "thread.run.cancelled" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run is cancelled." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.expired" + ], + "x-ms-enum": { + "name": "RunStreamEventExpired", + "modelAsString": true, + "values": [ + { + "value": "thread.run.expired" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run expires." + } + ] + }, + "runStepStreamEvent": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.created" + ], + "x-ms-enum": { + "name": "RunStepStreamEventCreated", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.created" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step is created." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.in_progress" + ], + "x-ms-enum": { + "name": "RunStepStreamEventInProgress", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.in_progress" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step moves to an `in_progress` state." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.delta" + ], + "x-ms-enum": { + "name": "RunStepStreamEventDelta", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.delta" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepDeltaObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when parts of a run step are being streamed." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.completed" + ], + "x-ms-enum": { + "name": "RunStepStreamEventCompleted", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.completed" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step is completed." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.failed" + ], + "x-ms-enum": { + "name": "RunStepStreamEventFailed", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.failed" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step fails." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.cancelled" + ], + "x-ms-enum": { + "name": "RunStepStreamEventCancelled", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.cancelled" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step is cancelled." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.run.step.expired" + ], + "x-ms-enum": { + "name": "RunStepStreamEventExpired", + "modelAsString": true, + "values": [ + { + "value": "thread.run.step.expired" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/runStepObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a run step expires." + } + ] + }, + "messageStreamEvent": { + "oneOf": [ + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.message.created" + ], + "x-ms-enum": { + "name": "MessageStreamEventCreated", + "modelAsString": true, + "values": [ + { + "value": "thread.message.created" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/messageObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a message is created." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.message.in_progress" + ], + "x-ms-enum": { + "name": "MessageStreamEventInProgress", + "modelAsString": true, + "values": [ + { + "value": "thread.message.in_progress" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/messageObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a message moves to an `in_progress` state." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.message.delta" + ], + "x-ms-enum": { + "name": "MessageStreamEventDelta", + "modelAsString": true, + "values": [ + { + "value": "thread.message.delta" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/messageDeltaObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when parts of a message are being streamed." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.message.completed" + ], + "x-ms-enum": { + "name": "MessageStreamEventCompleted", + "modelAsString": true, + "values": [ + { + "value": "thread.message.completed" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/messageObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a message is completed." + }, + { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "thread.message.incomplete" + ], + "x-ms-enum": { + "name": "MessageStreamEventIncomplete", + "modelAsString": true, + "values": [ + { + "value": "thread.message.incomplete" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/messageObject" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a message ends before it is completed." + } + ] + }, + "Annotation": { + "oneOf": [ + { + "$ref": "#/components/schemas/FileCitation" + }, + { + "$ref": "#/components/schemas/UrlCitation" + }, + { + "$ref": "#/components/schemas/FilePath" + } + ] + }, + "Click": { + "type": "object", + "title": "Click", + "description": "A click action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "click" + ], + "default": "click", + "description": "Specifies the event type. For a click action, this property is \nalways set to `click`.\n", + "x-stainless-const": true + }, + "button": { + "type": "string", + "enum": [ + "left", + "right", + "wheel", + "back", + "forward" + ], + "description": "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`.\n" + }, + "x": { + "type": "integer", + "description": "The x-coordinate where the click occurred.\n" + }, + "y": { + "type": "integer", + "description": "The y-coordinate where the click occurred.\n" + } + }, + "required": [ + "type", + "button", + "x", + "y" + ] + }, + "CodeInterpreterFileOutput": { + "type": "object", + "title": "Code interpreter file output", + "description": "The output of a code interpreter tool call that is a file.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "files" + ], + "description": "The type of the code interpreter file output. Always `files`.\n", + "x-stainless-const": true + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "mime_type": { + "type": "string", + "description": "The MIME type of the file.\n" + }, + "file_id": { + "type": "string", + "description": "The ID of the file.\n" + } + }, + "required": [ + "mime_type", + "file_id" + ] + } + } + }, + "required": [ + "type", + "files" + ] + }, + "CodeInterpreterTextOutput": { + "type": "object", + "title": "Code interpreter text output", + "description": "The output of a code interpreter tool call that is text.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "logs" + ], + "description": "The type of the code interpreter text output. Always `logs`.\n", + "x-stainless-const": true + }, + "logs": { + "type": "string", + "description": "The logs of the code interpreter tool call.\n" + } + }, + "required": [ + "type", + "logs" + ] + }, + "CodeInterpreterTool": { + "type": "object", + "title": "Code interpreter", + "description": "A tool that runs code.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "code_interpreter" + ], + "description": "The type of the code interpreter tool. Always `code_interpreter`.\n", + "x-stainless-const": true + }, + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The IDs of the files to run the code on.\n" + } + }, + "required": [ + "type", + "file_ids" + ] + }, + "CodeInterpreterToolCall": { + "type": "object", + "title": "Code interpreter tool call", + "description": "A tool call to run code.\n", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the code interpreter tool call.\n" + }, + "type": { + "type": "string", + "enum": [ + "code_interpreter_call" + ], + "description": "The type of the code interpreter tool call. Always `code_interpreter_call`.\n", + "x-stainless-const": true + }, + "code": { + "type": "string", + "description": "The code to run.\n" + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "interpreting", + "completed" + ], + "description": "The status of the code interpreter tool call.\n" + }, + "results": { + "type": "array", + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/CodeInterpreterToolOutput" + }, + "description": "The results of the code interpreter tool call.\n" + } + }, + "required": [ + "id", + "type", + "code", + "status", + "results" + ] + }, + "CodeInterpreterToolOutput": { + "oneOf": [ + { + "$ref": "#/components/schemas/CodeInterpreterTextOutput" + }, + { + "$ref": "#/components/schemas/CodeInterpreterFileOutput" + } + ] + }, + "ComparisonFilter": { + "type": "object", + "additionalProperties": false, + "title": "Comparison Filter", + "description": "A filter used to compare a specified attribute key to a given value using a defined comparison operation.\n", + "properties": { + "type": { + "type": "string", + "default": "eq", + "enum": [ + "eq", + "ne", + "gt", + "gte", + "lt", + "lte" + ], + "description": "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal\n" + }, + "key": { + "type": "string", + "description": "The key to compare against the value." + }, + "value": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "description": "The value to compare against the attribute key; supports string, number, or boolean types." + } + }, + "required": [ + "type", + "key", + "value" + ], + "x-oaiMeta": { + "name": "ComparisonFilter" + } + }, + "CompoundFilter": { + "type": "object", + "additionalProperties": false, + "title": "Compound Filter", + "description": "Combine multiple filters using `and` or `or`.", + "properties": { + "type": { + "type": "string", + "description": "Type of operation: `and` or `or`.", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "description": "Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`.", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ComparisonFilter" + } + ] + } + } + }, + "required": [ + "type", + "filters" + ], + "x-oaiMeta": { + "name": "CompoundFilter" + } + }, + "ComputerAction": { + "oneOf": [ + { + "$ref": "#/components/schemas/Click" + }, + { + "$ref": "#/components/schemas/DoubleClick" + }, + { + "$ref": "#/components/schemas/Drag" + }, + { + "$ref": "#/components/schemas/KeyPress" + }, + { + "$ref": "#/components/schemas/Move" + }, + { + "$ref": "#/components/schemas/Screenshot" + }, + { + "$ref": "#/components/schemas/Scroll" + }, + { + "$ref": "#/components/schemas/Type" + }, + { + "$ref": "#/components/schemas/Wait" + } + ] + }, + "ComputerScreenshotImage": { + "type": "object", + "description": "A computer screenshot image used with the computer use tool.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_screenshot" + ], + "default": "computer_screenshot", + "description": "Specifies the event type. For a computer screenshot, this property is \nalways set to `computer_screenshot`.\n", + "x-stainless-const": true + }, + "image_url": { + "type": "string", + "description": "The URL of the screenshot image." + }, + "file_id": { + "type": "string", + "description": "The identifier of an uploaded file that contains the screenshot." + } + }, + "required": [ + "type" + ] + }, + "ComputerTool": { + "type": "object", + "title": "Computer use", + "description": "A tool that controls a virtual computer. Learn more about the \n[computer tool](/docs/guides/tools-computer-use).\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "computer-preview" + ], + "description": "The type of the computer use tool. Always `computer_use_preview`.\n", + "x-stainless-const": true + }, + "display_width": { + "type": "number", + "description": "The width of the computer display.\n" + }, + "display_height": { + "type": "number", + "description": "The height of the computer display.\n" + }, + "environment": { + "type": "string", + "description": "The type of computer environment to control.\n", + "enum": [ + "mac", + "windows", + "ubuntu", + "browser" + ] + } + }, + "required": [ + "type", + "display_width", + "display_height", + "environment" + ] + }, + "ComputerToolCall": { + "type": "object", + "title": "Computer tool call", + "description": "A tool call to a computer use tool. See the \n[computer use guide](/docs/guides/tools-computer-use) for more information.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the computer call. Always `computer_call`.", + "enum": [ + "computer_call" + ], + "default": "computer_call" + }, + "id": { + "type": "string", + "description": "The unique ID of the computer call." + }, + "call_id": { + "type": "string", + "description": "An identifier used when responding to the tool call with output.\n" + }, + "action": { + "$ref": "#/components/schemas/ComputerAction", + "x-oaiExpandable": true + }, + "pending_safety_checks": { + "type": "array", + "x-oaiExpandable": true, + "items": { + "$ref": "#/components/schemas/ComputerToolCallSafetyCheck" + }, + "description": "The pending safety checks for the computer call.\n" + }, + "status": { + "type": "string", + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "type", + "id", + "action", + "call_id", + "pending_safety_checks", + "status" + ] + }, + "ComputerToolCallOutput": { + "type": "object", + "title": "Computer tool call output", + "description": "The output of a computer tool call.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the computer tool call output. Always `computer_call_output`.\n", + "enum": [ + "computer_call_output" + ], + "default": "computer_call_output", + "x-stainless-const": true + }, + "id": { + "type": "string", + "description": "The ID of the computer tool call output.\n" + }, + "call_id": { + "type": "string", + "description": "The ID of the computer tool call that produced the output.\n" + }, + "acknowledged_safety_checks": { + "type": "array", + "x-oaiExpandable": true, + "description": "The safety checks reported by the API that have been acknowledged by the \ndeveloper.\n", + "items": { + "$ref": "#/components/schemas/ComputerToolCallSafetyCheck" + } + }, + "output": { + "$ref": "#/components/schemas/ComputerScreenshotImage" + }, + "status": { + "type": "string", + "description": "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "type", + "call_id", + "output" + ] + }, + "ComputerToolCallOutputResource": { + "allOf": [ + { + "$ref": "#/components/schemas/ComputerToolCallOutput" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the computer call tool output.\n" + } + }, + "required": [ + "id" + ] + } + ] + }, + "ComputerToolCallSafetyCheck": { + "type": "object", + "description": "A pending safety check for the computer call.\n", + "properties": { + "id": { + "type": "string", + "description": "The ID of the pending safety check." + }, + "code": { + "type": "string", + "description": "The type of the pending safety check." + }, + "message": { + "type": "string", + "description": "Details about the pending safety check." + } + }, + "required": [ + "id", + "code", + "message" + ] + }, + "Content": { + "description": "Multi-modal input and output contents.\n", + "oneOf": [ + { + "title": "Input content types", + "x-oaiExpandable": true, + "$ref": "#/components/schemas/InputContent" + }, + { + "title": "Output content types", + "x-oaiExpandable": true, + "$ref": "#/components/schemas/OutputContent" + } + ] + }, + "Coordinate": { + "type": "object", + "title": "Coordinate", + "description": "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.\n", + "properties": { + "x": { + "type": "integer", + "description": "The x-coordinate.\n" + }, + "y": { + "type": "integer", + "description": "The y-coordinate.\n" + } + }, + "required": [ + "x", + "y" + ] + }, + "CreateModelResponseProperties": { + "allOf": [ + { + "$ref": "#/components/schemas/ModelResponseProperties" + } + ] + }, + "createResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/CreateModelResponseProperties" + }, + { + "$ref": "#/components/schemas/ResponseProperties" + }, + { + "type": "object", + "properties": { + "input": { + "description": "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Image inputs](/docs/guides/images)\n- [File inputs](/docs/guides/pdf-files)\n- [Conversation state](/docs/guides/conversation-state)\n- [Function calling](/docs/guides/function-calling)\n", + "x-oaiExpandable": true, + "oneOf": [ + { + "type": "string", + "title": "Text input", + "description": "A text input to the model, equivalent to a text input with the \n`user` role.\n" + }, + { + "type": "array", + "title": "Input item list", + "description": "A list of one or many input items to the model, containing \ndifferent content types.\n", + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/InputItem" + } + } + ] + }, + "include": { + "type": "array", + "description": "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n", + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/includable" + }, + "nullable": true + }, + "parallel_tool_calls": { + "type": "boolean", + "description": "Whether to allow the model to run tool calls in parallel.\n", + "default": true, + "nullable": true + }, + "store": { + "type": "boolean", + "description": "Whether to store the generated model response for later retrieval via\nAPI.\n", + "default": true, + "nullable": true + }, + "stream": { + "description": "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](/docs/api-reference/responses-streaming)\nfor more information.\n", + "type": "boolean", + "nullable": true, + "default": false + } + }, + "required": [ + "model", + "input" + ] + } + ] + }, + "DoubleClick": { + "type": "object", + "title": "DoubleClick", + "description": "A double click action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "double_click" + ], + "default": "double_click", + "description": "Specifies the event type. For a double click action, this property is \nalways set to `double_click`.\n", + "x-stainless-const": true + }, + "x": { + "type": "integer", + "description": "The x-coordinate where the double click occurred.\n" + }, + "y": { + "type": "integer", + "description": "The y-coordinate where the double click occurred.\n" + } + }, + "required": [ + "type", + "x", + "y" + ] + }, + "Drag": { + "type": "object", + "title": "Drag", + "description": "A drag action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "drag" + ], + "default": "drag", + "description": "Specifies the event type. For a drag action, this property is \nalways set to `drag`.\n", + "x-stainless-const": true + }, + "path": { + "type": "array", + "description": "An array of coordinates representing the path of the drag action. Coordinates will appear as an array\nof objects, eg\n```\n[\n { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```\n", + "x-oaiExpandable": true, + "items": { + "title": "Drag path coordinates", + "x-oaiExpandable": true, + "description": "A series of x/y coordinate pairs in the drag path.\n", + "$ref": "#/components/schemas/Coordinate" + } + } + }, + "required": [ + "type", + "path" + ] + }, + "EasyInputMessage": { + "type": "object", + "title": "Input message", + "description": "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n", + "properties": { + "role": { + "type": "string", + "description": "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n", + "enum": [ + "user", + "assistant", + "system", + "developer" + ] + }, + "content": { + "description": "Text, image, or audio input to the model, used to generate a response.\nCan also contain previous assistant responses.\n", + "x-oaiExpandable": true, + "oneOf": [ + { + "type": "string", + "title": "Text input", + "description": "A text input to the model.\n" + }, + { + "$ref": "#/components/schemas/InputMessageContentList" + } + ] + }, + "type": { + "type": "string", + "description": "The type of the message input. Always `message`.\n", + "enum": [ + "message" + ], + "x-stainless-const": true + } + }, + "required": [ + "role", + "content" + ] + }, + "FileCitation": { + "type": "object", + "title": "File citation", + "description": "A citation to a file.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the file citation. Always `file_citation`.\n", + "enum": [ + "file_citation" + ], + "x-stainless-const": true + }, + "index": { + "type": "integer", + "description": "The index of the file in the list of files.\n" + }, + "file_id": { + "type": "string", + "description": "The ID of the file.\n" + } + }, + "required": [ + "type", + "index", + "file_id" + ] + }, + "FilePath": { + "type": "object", + "title": "File path", + "description": "A path to a file.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the file path. Always `file_path`.\n", + "enum": [ + "file_path" + ], + "x-stainless-const": true + }, + "file_id": { + "type": "string", + "description": "The ID of the file.\n" + }, + "index": { + "type": "integer", + "description": "The index of the file in the list of files.\n" + } + }, + "required": [ + "type", + "file_id", + "index" + ] + }, + "FileSearchRanker": { + "type": "string", + "description": "The ranker to use for the file search. If not specified will use the `auto` ranker.", + "enum": [ + "auto", + "default_2024_08_21" + ] + }, + "FileSearchTool": { + "type": "object", + "title": "File search", + "description": "A tool that searches for relevant content from uploaded files.\nLearn more about the [file search tool](/docs/guides/tools-file-search).\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "file_search" + ], + "description": "The type of the file search tool. Always `file_search`.\n", + "x-stainless-const": true + }, + "vector_store_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The IDs of the vector stores to search.\n" + }, + "max_num_results": { + "type": "integer", + "description": "The maximum number of results to return. This number should be between 1 \nand 50 inclusive.\n" + }, + "filters": { + "description": "A filter to apply based on file attributes.", + "oneOf": [ + { + "$ref": "#/components/schemas/ComparisonFilter" + }, + { + "$ref": "#/components/schemas/CompoundFilter" + } + ], + "x-oaiExpandable": true + }, + "ranking_options": { + "description": "Ranking options for search.", + "type": "object", + "additionalProperties": false, + "properties": { + "ranker": { + "type": "string", + "description": "The ranker to use for the file search.", + "enum": [ + "auto", + "default-2024-11-15" + ], + "default": "auto" + }, + "score_threshold": { + "type": "number", + "description": "The score threshold for the file search, a number between 0 and 1.\nNumbers closer to 1 will attempt to return only the most relevant\nresults, but may return fewer results.\n", + "minimum": 0, + "maximum": 1, + "default": 0 + } + } + } + }, + "required": [ + "type", + "vector_store_ids" + ] + }, + "FileSearchToolCall": { + "type": "object", + "title": "File search tool call", + "description": "The results of a file search tool call. See the \n[file search guide](/docs/guides/tools-file-search) for more information.\n", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the file search tool call.\n" + }, + "type": { + "type": "string", + "enum": [ + "file_search_call" + ], + "description": "The type of the file search tool call. Always `file_search_call`.\n", + "x-stainless-const": true + }, + "status": { + "type": "string", + "description": "The status of the file search tool call. One of `in_progress`, \n`searching`, `incomplete` or `failed`,\n", + "enum": [ + "in_progress", + "searching", + "completed", + "incomplete", + "failed" + ] + }, + "queries": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The queries used to search for files.\n" + }, + "results": { + "type": "array", + "description": "The results of the file search tool call.\n", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "The unique ID of the file.\n" + }, + "text": { + "type": "string", + "description": "The text that was retrieved from the file.\n" + }, + "filename": { + "type": "string", + "description": "The name of the file.\n" + }, + "attributes": { + "$ref": "#/components/schemas/VectorStoreFileAttributes" + }, + "score": { + "type": "number", + "format": "float", + "description": "The relevance score of the file - a value between 0 and 1.\n" + } + } + }, + "nullable": true + } + }, + "required": [ + "id", + "type", + "status", + "queries" + ] + }, + "FunctionTool": { + "type": "object", + "title": "Function", + "description": "Defines a function in your own code the model can choose to call. Learn more\nabout [function calling](/docs/guides/function-calling).\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the function tool. Always `function`.\n", + "x-stainless-const": true + }, + "name": { + "type": "string", + "description": "The name of the function to call.\n" + }, + "description": { + "type": "string", + "nullable": true, + "description": "A description of the function. Used by the model to determine whether\nor not to call the function.\n" + }, + "parameters": { + "type": "object", + "description": "A JSON schema object describing the parameters of the function.\n", + "additionalProperties": true + }, + "strict": { + "type": "boolean", + "description": "Whether to enforce strict parameter validation. Default `true`.\n" + } + }, + "required": [ + "type", + "name", + "parameters", + "strict" + ] + }, + "FunctionToolCall": { + "type": "object", + "title": "Function tool call", + "description": "A tool call to run a function. See the \n[function calling guide](/docs/guides/function-calling) for more information.\n", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the function tool call.\n" + }, + "type": { + "type": "string", + "enum": [ + "function_call" + ], + "description": "The type of the function tool call. Always `function_call`.\n", + "x-stainless-const": true + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model.\n" + }, + "name": { + "type": "string", + "description": "The name of the function to run.\n" + }, + "arguments": { + "type": "string", + "description": "A JSON string of the arguments to pass to the function.\n" + }, + "status": { + "type": "string", + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "id", + "type", + "call_id", + "name", + "arguments" + ] + }, + "FunctionToolCallOutput": { + "type": "object", + "title": "Function tool call output", + "description": "The output of a function tool call.\n", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the function tool call output. Populated when this item\nis returned via API.\n" + }, + "type": { + "type": "string", + "enum": [ + "function_call_output" + ], + "description": "The type of the function tool call output. Always `function_call_output`.\n", + "x-stainless-const": true + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model.\n" + }, + "output": { + "type": "string", + "description": "A JSON string of the output of the function tool call.\n" + }, + "status": { + "type": "string", + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "type", + "call_id", + "output" + ] + }, + "FunctionToolCallOutputResource": { + "allOf": [ + { + "$ref": "#/components/schemas/FunctionToolCallOutput" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the function call tool output.\n" + } + }, + "required": [ + "id" + ] + } + ] + }, + "includable": { + "type": "string", + "description": "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n", + "enum": [ + "file_search_call.results", + "message.input_image.image_url", + "computer_call_output.output.image_url" + ] + }, + "InputAudio": { + "type": "object", + "title": "Audio input", + "description": "An audio input to the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the input item. Always `input_audio`.\n", + "enum": [ + "input_audio" + ], + "x-stainless-const": true + }, + "data": { + "type": "string", + "description": "Base64-encoded audio data.\n" + }, + "format": { + "type": "string", + "description": "The format of the audio data. Currently supported formats are `mp3` and\n`wav`.\n", + "enum": [ + "mp3", + "wav" + ] + } + }, + "required": [ + "type", + "data", + "format" + ] + }, + "InputContent": { + "oneOf": [ + { + "$ref": "#/components/schemas/InputText" + }, + { + "$ref": "#/components/schemas/InputImage" + }, + { + "$ref": "#/components/schemas/InputFile" + } + ], + "x-oaiExpandable": true + }, + "InputFile": { + "type": "object", + "title": "File input", + "description": "A file input to the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the input item. Always `input_file`.\n", + "enum": [ + "input_file" + ], + "x-stainless-const": true + }, + "file_id": { + "type": "string", + "description": "The ID of the file to be sent to the model.\n" + }, + "filename": { + "type": "string", + "description": "The name of the file to be sent to the model.\n" + }, + "file_data": { + "type": "string", + "description": "The content of the file to be sent to the model.\n" + } + }, + "required": [ + "type" + ] + }, + "InputImage": { + "type": "object", + "title": "Image input", + "description": "An image input to the model. Learn about [image inputs](/docs/guides/vision).\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the input item. Always `input_image`.\n", + "enum": [ + "input_image" + ], + "x-stainless-const": true + }, + "image_url": { + "type": "string", + "description": "The URL of the image to be sent to the model. A fully qualified URL or\nbase64 encoded image in a data URL.\n", + "nullable": true + }, + "file_id": { + "type": "string", + "description": "The ID of the file to be sent to the model.\n", + "nullable": true + }, + "detail": { + "type": "string", + "description": "The detail level of the image to be sent to the model. One of `high`,\n`low`, or `auto`. Defaults to `auto`.\n", + "enum": [ + "high", + "low", + "auto" + ], + "default": "auto" + } + }, + "required": [ + "type", + "detail" + ] + }, + "InputItem": { + "oneOf": [ + { + "$ref": "#/components/schemas/EasyInputMessage" + }, + { + "type": "object", + "title": "Item", + "description": "An item representing part of the context for the response to be \ngenerated by the model. Can contain text, images, and audio inputs,\nas well as previous assistant responses and tool call outputs.\n", + "$ref": "#/components/schemas/Item" + }, + { + "$ref": "#/components/schemas/ItemReference" + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "InputMessage": { + "type": "object", + "title": "Input message", + "description": "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the message input. Always set to `message`.\n", + "enum": [ + "message" + ], + "x-stainless-const": true + }, + "role": { + "type": "string", + "description": "The role of the message input. One of `user`, `system`, or `developer`.\n", + "enum": [ + "user", + "system", + "developer" + ] + }, + "status": { + "type": "string", + "description": "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + }, + "content": { + "$ref": "#/components/schemas/InputMessageContentList" + } + }, + "required": [ + "role", + "content" + ] + }, + "InputMessageContentList": { + "type": "array", + "title": "Input item content list", + "description": "A list of one or many input items to the model, containing different content \ntypes.\n", + "x-oaiExpandable": true, + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/InputContent" + } + }, + "InputMessageResource": { + "allOf": [ + { + "$ref": "#/components/schemas/InputMessage" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the message input.\n" + } + }, + "required": [ + "id" + ] + } + ] + }, + "InputText": { + "type": "object", + "title": "Text input", + "description": "A text input to the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the input item. Always `input_text`.\n", + "enum": [ + "input_text" + ], + "x-stainless-const": true + }, + "text": { + "type": "string", + "description": "The text input to the model.\n" + } + }, + "required": [ + "type", + "text" + ] + }, + "Item": { + "type": "object", + "description": "Content item used to generate a response.\n", + "oneOf": [ + { + "$ref": "#/components/schemas/InputMessage" + }, + { + "$ref": "#/components/schemas/OutputMessage" + }, + { + "$ref": "#/components/schemas/FileSearchToolCall" + }, + { + "$ref": "#/components/schemas/ComputerToolCall" + }, + { + "$ref": "#/components/schemas/ComputerToolCallOutput" + }, + { + "$ref": "#/components/schemas/FunctionToolCall" + }, + { + "$ref": "#/components/schemas/FunctionToolCallOutput" + }, + { + "$ref": "#/components/schemas/ReasoningItem" + } + ], + "x-oaiExpandable": true, + "discriminator": { + "propertyName": "type" + } + }, + "ItemReference": { + "type": "object", + "title": "Item reference", + "description": "An internal identifier for an item to reference.\n", + "properties": { + "id": { + "type": "string", + "description": "The ID of the item to reference.\n" + }, + "type": { + "type": "string", + "description": "The type of item to reference. Always `item_reference`.\n", + "enum": [ + "item_reference" + ], + "x-stainless-const": true + } + }, + "required": [ + "id", + "type" + ] + }, + "ItemResource": { + "description": "Content item used to generate a response.\n", + "oneOf": [ + { + "$ref": "#/components/schemas/InputMessageResource" + }, + { + "$ref": "#/components/schemas/OutputMessage" + }, + { + "$ref": "#/components/schemas/FileSearchToolCall" + }, + { + "$ref": "#/components/schemas/ComputerToolCall" + }, + { + "$ref": "#/components/schemas/ComputerToolCallOutputResource" + }, + { + "$ref": "#/components/schemas/FunctionToolCall" + }, + { + "$ref": "#/components/schemas/FunctionToolCallOutputResource" + } + ], + "x-oaiExpandable": true, + "discriminator": { + "propertyName": "type" + } + }, + "KeyPress": { + "type": "object", + "title": "KeyPress", + "description": "A collection of keypresses the model would like to perform.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "keypress" + ], + "default": "keypress", + "description": "Specifies the event type. For a keypress action, this property is \nalways set to `keypress`.\n", + "x-stainless-const": true + }, + "keys": { + "type": "array", + "items": { + "type": "string", + "description": "One of the keys the model is requesting to be pressed.\n" + }, + "description": "The combination of keys the model is requesting to be pressed. This is an\narray of strings, each representing a key.\n" + } + }, + "required": [ + "type", + "keys" + ] + }, + "Metadata": { + "type": "object", + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. \n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.\n", + "additionalProperties": { + "type": "string" + }, + "x-oaiTypeLabel": "map", + "nullable": true + }, + "ModelResponseProperties": { + "type": "object", + "properties": { + "model": { + "type": "string", + "description": "Model used to generate the responses." + }, + "metadata": { + "$ref": "#/components/schemas/Metadata" + }, + "temperature": { + "type": "number", + "minimum": 0, + "maximum": 2, + "default": 1, + "example": 1, + "nullable": true, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + }, + "top_p": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 1, + "example": 1, + "nullable": true, + "description": "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + }, + "user": { + "type": "string", + "example": "user-1234", + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + } + } + }, + "Move": { + "type": "object", + "title": "Move", + "description": "A mouse move action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "move" + ], + "default": "move", + "description": "Specifies the event type. For a move action, this property is \nalways set to `move`.\n", + "x-stainless-const": true + }, + "x": { + "type": "integer", + "description": "The x-coordinate to move to.\n" + }, + "y": { + "type": "integer", + "description": "The y-coordinate to move to.\n" + } + }, + "required": [ + "type", + "x", + "y" + ] + }, + "OutputAudio": { + "type": "object", + "title": "Output audio", + "description": "An audio output from the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the output audio. Always `output_audio`.\n", + "enum": [ + "output_audio" + ], + "x-stainless-const": true + }, + "data": { + "type": "string", + "description": "Base64-encoded audio data from the model.\n" + }, + "transcript": { + "type": "string", + "description": "The transcript of the audio data from the model.\n" + } + }, + "required": [ + "type", + "data", + "transcript" + ] + }, + "OutputContent": { + "oneOf": [ + { + "$ref": "#/components/schemas/OutputText" + }, + { + "$ref": "#/components/schemas/Refusal" + } + ] + }, + "OutputItem": { + "anyOf": [ + { + "$ref": "#/components/schemas/OutputMessage" + }, + { + "$ref": "#/components/schemas/FileSearchToolCall" + }, + { + "$ref": "#/components/schemas/FunctionToolCall" + }, + { + "$ref": "#/components/schemas/ComputerToolCall" + }, + { + "$ref": "#/components/schemas/ReasoningItem" + } + ], + "x-oaiExpandable": true, + "discriminator": { + "propertyName": "type" + } + }, + "OutputMessage": { + "type": "object", + "title": "Output message", + "description": "An output message from the model.\n", + "properties": { + "id": { + "type": "string", + "description": "The unique ID of the output message.\n" + }, + "type": { + "type": "string", + "description": "The type of the output message. Always `message`.\n", + "enum": [ + "message" + ], + "x-stainless-const": true + }, + "role": { + "type": "string", + "description": "The role of the output message. Always `assistant`.\n", + "enum": [ + "assistant" + ], + "x-stainless-const": true + }, + "content": { + "type": "array", + "description": "The content of the output message.\n", + "x-oaiExpandable": true, + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/OutputContent" + } + }, + "status": { + "type": "string", + "description": "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "id", + "type", + "role", + "content", + "status" + ] + }, + "OutputText": { + "type": "object", + "title": "Output text", + "description": "A text output from the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the output text. Always `output_text`.\n", + "enum": [ + "output_text" + ], + "x-stainless-const": true + }, + "text": { + "type": "string", + "description": "The text output from the model.\n" + }, + "annotations": { + "type": "array", + "description": "The annotations of the text output.\n", + "items": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/Annotation" + } + } + }, + "required": [ + "type", + "text", + "annotations" + ] + }, + "Reasoning": { + "type": "object", + "description": "**o-series models only**\n\nConfiguration options for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n", + "title": "Reasoning", + "x-oaiExpandable": true, + "properties": { + "effort": { + "$ref": "#/components/schemas/ReasoningEffort" + }, + "generate_summary": { + "type": "string", + "description": "**o-series models only** \n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `concise` or `detailed`.\n", + "enum": [ + "concise", + "detailed" + ], + "nullable": true + } + }, + "required": [ + "effort" + ] + }, + "ReasoningItem": { + "type": "object", + "description": "A description of the chain of thought used by a reasoning model while generating\na response.\n", + "title": "Reasoning", + "x-oaiExpandable": true, + "properties": { + "type": { + "type": "string", + "description": "The type of the object. Always `reasoning`.\n", + "enum": [ + "reasoning" + ], + "x-stainless-const": true + }, + "id": { + "type": "string", + "description": "The unique identifier of the reasoning content.\n" + }, + "content": { + "type": "array", + "description": "Reasoning text contents.\n", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The type of the object. Always `text`.\n", + "enum": [ + "reasoning_summary" + ], + "x-stainless-const": true + }, + "text": { + "type": "string", + "description": "A short summary of the reasoning used by the model when generating\nthe response.\n" + } + }, + "required": [ + "type", + "text" + ] + } + }, + "status": { + "type": "string", + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + } + }, + "required": [ + "id", + "content", + "type" + ] + }, + "Refusal": { + "type": "object", + "title": "Refusal", + "description": "A refusal from the model.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the refusal. Always `refusal`.\n", + "enum": [ + "refusal" + ], + "x-stainless-const": true + }, + "refusal": { + "type": "string", + "description": "The refusal explanationfrom the model.\n" + } + }, + "required": [ + "type", + "refusal" + ] + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/ModelResponseProperties" + }, + { + "$ref": "#/components/schemas/ResponseProperties" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this Response.\n" + }, + "object": { + "type": "string", + "description": "The object type of this resource - always set to `response`.\n", + "enum": [ + "response" + ], + "x-stainless-const": true + }, + "status": { + "type": "string", + "description": "The status of the response generation. One of `completed`, `failed`, \n`in_progress`, or `incomplete`.\n", + "enum": [ + "completed", + "failed", + "in_progress", + "incomplete" + ] + }, + "created_at": { + "type": "number", + "description": "Unix timestamp (in seconds) of when this Response was created.\n" + }, + "error": { + "$ref": "#/components/schemas/ResponseError" + }, + "incomplete_details": { + "type": "object", + "nullable": true, + "description": "Details about why the response is incomplete.\n", + "properties": { + "reason": { + "type": "string", + "description": "The reason why the response is incomplete.", + "enum": [ + "max_output_tokens", + "content_filter" + ] + } + } + }, + "output": { + "type": "array", + "x-oaiExpandable": true, + "description": "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and \n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n", + "items": { + "$ref": "#/components/schemas/OutputItem", + "x-oaiExpandable": true + } + }, + "output_text": { + "type": "string", + "nullable": true, + "description": "SDK-only convenience property that contains the aggregated text output \nfrom all `output_text` items in the `output` array, if any are present. \nSupported in the Python and JavaScript SDKs.\n", + "x-oaiSupportedSDKs": [ + "python", + "javascript" + ] + }, + "usage": { + "$ref": "#/components/schemas/ResponseUsage" + }, + "parallel_tool_calls": { + "type": "boolean", + "description": "Whether to allow the model to run tool calls in parallel.\n", + "default": true + } + }, + "required": [ + "id", + "object", + "created_at", + "error", + "incomplete_details", + "instructions", + "model", + "tools", + "output", + "parallel_tool_calls", + "metadata", + "tool_choice", + "temperature", + "top_p" + ], + "x-oaiMeta": { + "name": "The response object", + "group": "responses", + "example": "{\n \"id\": \"resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41\",\n \"object\": \"response\",\n \"created_at\": 1741476777,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [\n {\n \"type\": \"message\",\n \"id\": \"msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41\",\n \"status\": \"completed\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"The image depicts a scenic landscape with a wooden boardwalk or pathway leading through lush, green grass under a blue sky with some clouds. The setting suggests a peaceful natural area, possibly a park or nature reserve. There are trees and shrubs in the background.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1.0,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1.0,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 328,\n \"input_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"output_tokens\": 52,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 380\n },\n \"user\": null,\n \"metadata\": {}\n}\n" + } + } + ] + }, + "ResponseAudioDeltaEvent": { + "type": "object", + "description": "Emitted when there is a partial audio response.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.audio.delta`.\n", + "enum": [ + "response.audio.delta" + ], + "x-stainless-const": true + }, + "delta": { + "type": "string", + "description": "A chunk of Base64 encoded response audio bytes.\n" + } + }, + "required": [ + "type", + "delta" + ], + "x-oaiMeta": { + "name": "response.audio.delta", + "group": "responses", + "example": "{\n \"type\": \"response.audio.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \"base64encoded...\"\n}\n" + } + }, + "ResponseAudioDoneEvent": { + "type": "object", + "description": "Emitted when the audio response is complete.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.audio.done`.\n", + "enum": [ + "response.audio.done" + ], + "x-stainless-const": true + } + }, + "required": [ + "type", + "response_id" + ], + "x-oaiMeta": { + "name": "response.audio.done", + "group": "responses", + "example": "{\n \"type\": \"response.audio.done\",\n \"response_id\": \"resp-123\"\n}\n" + } + }, + "ResponseAudioTranscriptDeltaEvent": { + "type": "object", + "description": "Emitted when there is a partial transcript of audio.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.audio.transcript.delta`.\n", + "enum": [ + "response.audio.transcript.delta" + ], + "x-stainless-const": true + }, + "delta": { + "type": "string", + "description": "The partial transcript of the audio response.\n" + } + }, + "required": [ + "type", + "response_id", + "delta" + ], + "x-oaiMeta": { + "name": "response.audio.transcript.delta", + "group": "responses", + "example": "{\n \"type\": \"response.audio.transcript.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \" ... partial transcript ... \"\n}\n" + } + }, + "ResponseAudioTranscriptDoneEvent": { + "type": "object", + "description": "Emitted when the full audio transcript is completed.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.audio.transcript.done`.\n", + "enum": [ + "response.audio.transcript.done" + ], + "x-stainless-const": true + } + }, + "required": [ + "type", + "response_id" + ], + "x-oaiMeta": { + "name": "response.audio.transcript.done", + "group": "responses", + "example": "{\n \"type\": \"response.audio.transcript.done\",\n \"response_id\": \"resp_123\"\n}\n" + } + }, + "ResponseCodeInterpreterCallCodeDeltaEvent": { + "type": "object", + "description": "Emitted when a partial code snippet is added by the code interpreter.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.code_interpreter_call.code.delta`.\n", + "enum": [ + "response.code_interpreter_call.code.delta" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the code interpreter call is in progress.\n" + }, + "delta": { + "type": "string", + "description": "The partial code snippet added by the code interpreter.\n" + } + }, + "required": [ + "type", + "response_id", + "output_index", + "delta" + ], + "x-oaiMeta": { + "name": "response.code_interpreter_call.code.delta", + "group": "responses", + "example": "{\n \"type\": \"response.code_interpreter_call.code.delta\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"delta\": \"partial code\"\n}\n" + } + }, + "ResponseCodeInterpreterCallCodeDoneEvent": { + "type": "object", + "description": "Emitted when code snippet output is finalized by the code interpreter.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.code_interpreter_call.code.done`.\n", + "enum": [ + "response.code_interpreter_call.code.done" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the code interpreter call is in progress.\n" + }, + "code": { + "type": "string", + "description": "The final code snippet output by the code interpreter.\n" + } + }, + "required": [ + "type", + "response_id", + "output_index", + "code" + ], + "x-oaiMeta": { + "name": "response.code_interpreter_call.code.done", + "group": "responses", + "example": "{\n \"type\": \"response.code_interpreter_call.code.done\",\n \"response_id\": \"resp-123\",\n \"output_index\": 3,\n \"code\": \"console.log('done');\"\n}\n" + } + }, + "ResponseCodeInterpreterCallCompletedEvent": { + "type": "object", + "description": "Emitted when the code interpreter call is completed.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.code_interpreter_call.completed`.\n", + "enum": [ + "response.code_interpreter_call.completed" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the code interpreter call is in progress.\n" + }, + "code_interpreter_call": { + "$ref": "#/components/schemas/CodeInterpreterToolCall" + } + }, + "required": [ + "type", + "response_id", + "output_index", + "code_interpreter_call" + ], + "x-oaiMeta": { + "name": "response.code_interpreter_call.completed", + "group": "responses", + "example": "{\n \"type\": \"response.code_interpreter_call.completed\",\n \"response_id\": \"resp-123\",\n \"output_index\": 5,\n \"code_interpreter_call\": {}\n}\n" + } + }, + "ResponseCodeInterpreterCallInProgressEvent": { + "type": "object", + "description": "Emitted when a code interpreter call is in progress.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.code_interpreter_call.in_progress`.\n", + "enum": [ + "response.code_interpreter_call.in_progress" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the code interpreter call is in progress.\n" + }, + "code_interpreter_call": { + "$ref": "#/components/schemas/CodeInterpreterToolCall" + } + }, + "required": [ + "type", + "response_id", + "output_index", + "code_interpreter_call" + ], + "x-oaiMeta": { + "name": "response.code_interpreter_call.in_progress", + "group": "responses", + "example": "{\n \"type\": \"response.code_interpreter_call.in.progress\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"code_interpreter_call\": {}\n}\n" + } + }, + "ResponseCodeInterpreterCallInterpretingEvent": { + "type": "object", + "description": "Emitted when the code interpreter is actively interpreting the code snippet.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.code_interpreter_call.interpreting`.\n", + "enum": [ + "response.code_interpreter_call.interpreting" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the code interpreter call is in progress.\n" + }, + "code_interpreter_call": { + "$ref": "#/components/schemas/CodeInterpreterToolCall" + } + }, + "required": [ + "type", + "response_id", + "output_index", + "code_interpreter_call" + ], + "x-oaiMeta": { + "name": "response.code_interpreter_call.interpreting", + "group": "responses", + "example": "{\n \"type\": \"response.code_interpreter_call.interpreting\",\n \"response_id\": \"resp-123\",\n \"output_index\": 4,\n \"code_interpreter_call\": {}\n}\n" + } + }, + "ResponseCompletedEvent": { + "type": "object", + "description": "Emitted when the model response is complete.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.completed`.\n", + "enum": [ + "response.completed" + ], + "x-stainless-const": true + }, + "response": { + "$ref": "#/components/schemas/response", + "description": "Properties of the completed response.\n" + } + }, + "required": [ + "type", + "response" + ], + "x-oaiMeta": { + "name": "response.completed", + "group": "responses", + "example": "{\n \"type\": \"response.completed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"input\": [],\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [\n {\n \"id\": \"msg_123\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 0,\n \"output_tokens\": 0,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 0\n },\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + } + }, + "ResponseContentPartAddedEvent": { + "type": "object", + "description": "Emitted when a new content part is added.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.content_part.added`.\n", + "enum": [ + "response.content_part.added" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the content part was added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the content part was added to.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that was added.\n" + }, + "part": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/OutputContent", + "description": "The content part that was added.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "part" + ], + "x-oaiMeta": { + "name": "response.content_part.added", + "group": "responses", + "example": "{\n \"type\": \"response.content_part.added\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"\",\n \"annotations\": []\n }\n}\n" + } + }, + "ResponseContentPartDoneEvent": { + "type": "object", + "description": "Emitted when a content part is done.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.content_part.done`.\n", + "enum": [ + "response.content_part.done" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the content part was added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the content part was added to.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that is done.\n" + }, + "part": { + "x-oaiExpandable": true, + "$ref": "#/components/schemas/OutputContent", + "description": "The content part that is done.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "part" + ], + "x-oaiMeta": { + "name": "response.content_part.done", + "group": "responses", + "example": "{\n \"type\": \"response.content_part.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n}\n" + } + }, + "ResponseCreatedEvent": { + "type": "object", + "description": "An event that is emitted when a response is created.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.created`.\n", + "enum": [ + "response.created" + ], + "x-stainless-const": true + }, + "response": { + "$ref": "#/components/schemas/response", + "description": "The response that was created.\n" + } + }, + "required": [ + "type", + "response" + ], + "x-oaiMeta": { + "name": "response.created", + "group": "responses", + "example": "{\n \"type\": \"response.created\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + } + }, + "ResponseError": { + "type": "object", + "description": "An error object returned when the model fails to generate a Response.\n", + "nullable": true, + "properties": { + "code": { + "$ref": "#/components/schemas/ResponseErrorCode" + }, + "message": { + "type": "string", + "description": "A human-readable description of the error.\n" + } + }, + "required": [ + "code", + "message" + ] + }, + "ResponseErrorCode": { + "type": "string", + "description": "The error code for the response.\n", + "enum": [ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found" + ] + }, + "ResponseErrorEvent": { + "type": "object", + "description": "Emitted when an error occurs.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `error`.\n", + "enum": [ + "error" + ], + "x-stainless-const": true + }, + "code": { + "type": "string", + "description": "The error code.\n", + "nullable": true + }, + "message": { + "type": "string", + "description": "The error message.\n" + }, + "param": { + "type": "string", + "description": "The error parameter.\n", + "nullable": true + } + }, + "required": [ + "type", + "code", + "message", + "param" + ], + "x-oaiMeta": { + "name": "error", + "group": "responses", + "example": "{\n \"type\": \"error\",\n \"code\": \"ERR_SOMETHING\",\n \"message\": \"Something went wrong\",\n \"param\": null\n}\n" + } + }, + "ResponseFailedEvent": { + "type": "object", + "description": "An event that is emitted when a response fails.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.failed`.\n", + "enum": [ + "response.failed" + ], + "x-stainless-const": true + }, + "response": { + "$ref": "#/components/schemas/response", + "description": "The response that failed.\n" + } + }, + "required": [ + "type", + "response" + ], + "x-oaiMeta": { + "name": "response.failed", + "group": "responses", + "example": "{\n \"type\": \"response.failed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"failed\",\n \"error\": {\n \"code\": \"server_error\",\n \"message\": \"The model failed to generate a response.\"\n },\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + } + }, + "ResponseFileSearchCallCompletedEvent": { + "type": "object", + "description": "Emitted when a file search call is completed (results found).", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.file_search_call.completed`.\n", + "enum": [ + "response.file_search_call.completed" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the file search call is initiated.\n" + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated.\n" + } + }, + "required": [ + "type", + "output_index", + "item_id" + ], + "x-oaiMeta": { + "name": "response.file_search_call.completed", + "group": "responses", + "example": "{\n \"type\": \"response.file_search_call.completed\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + } + }, + "ResponseFileSearchCallInProgressEvent": { + "type": "object", + "description": "Emitted when a file search call is initiated.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.file_search_call.in_progress`.\n", + "enum": [ + "response.file_search_call.in_progress" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the file search call is initiated.\n" + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated.\n" + } + }, + "required": [ + "type", + "output_index", + "item_id" + ], + "x-oaiMeta": { + "name": "response.file_search_call.in_progress", + "group": "responses", + "example": "{\n \"type\": \"response.file_search_call.in_progress\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + } + }, + "ResponseFileSearchCallSearchingEvent": { + "type": "object", + "description": "Emitted when a file search is currently searching.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.file_search_call.searching`.\n", + "enum": [ + "response.file_search_call.searching" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the file search call is searching.\n" + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated.\n" + } + }, + "required": [ + "type", + "output_index", + "item_id" + ], + "x-oaiMeta": { + "name": "response.file_search_call.searching", + "group": "responses", + "example": "{\n \"type\": \"response.file_search_call.searching\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + } + }, + "ResponseFunctionCallArgumentsDeltaEvent": { + "type": "object", + "description": "Emitted when there is a partial function-call arguments delta.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.function_call_arguments.delta`.\n", + "enum": [ + "response.function_call_arguments.delta" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the function-call arguments delta is added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the function-call arguments delta is added to.\n" + }, + "delta": { + "type": "string", + "description": "The function-call arguments delta that is added.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "delta" + ], + "x-oaiMeta": { + "name": "response.function_call_arguments.delta", + "group": "responses", + "example": "{\n \"type\": \"response.function_call_arguments.delta\",\n \"item_id\": \"item-abc\",\n \"output_index\": 0,\n \"delta\": \"{ \\\"arg\\\":\"\n}\n" + } + }, + "ResponseFunctionCallArgumentsDoneEvent": { + "type": "object", + "description": "Emitted when function-call arguments are finalized.", + "properties": { + "type": { + "type": "string", + "enum": [ + "response.function_call_arguments.done" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the item." + }, + "output_index": { + "type": "integer", + "description": "The index of the output item." + }, + "arguments": { + "type": "string", + "description": "The function-call arguments." + } + }, + "required": [ + "type", + "item_id", + "output_index", + "arguments" + ], + "x-oaiMeta": { + "name": "response.function_call_arguments.done", + "group": "responses", + "example": "{\n \"type\": \"response.function_call_arguments.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"arguments\": \"{ \\\"arg\\\": 123 }\"\n}\n" + } + }, + "ResponseInProgressEvent": { + "type": "object", + "description": "Emitted when the response is in progress.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.in_progress`.\n", + "enum": [ + "response.in_progress" + ], + "x-stainless-const": true + }, + "response": { + "$ref": "#/components/schemas/response", + "description": "The response that is in progress.\n" + } + }, + "required": [ + "type", + "response" + ], + "x-oaiMeta": { + "name": "response.in_progress", + "group": "responses", + "example": "{\n \"type\": \"response.in_progress\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + } + }, + "ResponseIncompleteEvent": { + "type": "object", + "description": "An event that is emitted when a response finishes as incomplete.\n", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.incomplete`.\n", + "enum": [ + "response.incomplete" + ], + "x-stainless-const": true + }, + "response": { + "$ref": "#/components/schemas/response", + "description": "The response that was incomplete.\n" + } + }, + "required": [ + "type", + "response" + ], + "x-oaiMeta": { + "name": "response.incomplete", + "group": "responses", + "example": "{\n \"type\": \"response.incomplete\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"incomplete\",\n \"error\": null, \n \"incomplete_details\": {\n \"reason\": \"max_tokens\"\n },\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + } + }, + "responseItemList": { + "type": "object", + "description": "A list of Response items.", + "properties": { + "object": { + "type": "string", + "description": "The type of object returned, must be `list`.", + "enum": [ + "list" + ], + "x-stainless-const": true + }, + "data": { + "type": "array", + "description": "A list of items used to generate this response.", + "items": { + "$ref": "#/components/schemas/ItemResource" + } + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more items available." + }, + "first_id": { + "type": "string", + "description": "The ID of the first item in the list." + }, + "last_id": { + "type": "string", + "description": "The ID of the last item in the list." + } + }, + "required": [ + "object", + "data", + "has_more", + "first_id", + "last_id" + ], + "x-oaiExpandable": true, + "x-oaiMeta": { + "name": "The input item list", + "group": "responses", + "example": "{\n \"object\": \"list\",\n \"data\": [\n {\n \"id\": \"msg_abc123\",\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_text\",\n \"text\": \"Tell me a three sentence bedtime story about a unicorn.\"\n }\n ]\n }\n ],\n \"first_id\": \"msg_abc123\",\n \"last_id\": \"msg_abc123\",\n \"has_more\": false\n}\n" + } + }, + "ResponseModalities": { + "type": "array", + "nullable": true, + "description": "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to \n[generate audio](/docs/guides/audio). To request that this model generate \nboth text and audio responses, you can use:\n\n`[\"text\", \"audio\"]`\n", + "items": { + "type": "string", + "enum": [ + "text", + "audio" + ] + } + }, + "ResponseModalitiesTextOnly": { + "type": "array", + "nullable": true, + "description": "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThis API will soon support other output modalities, including audio and images.\n", + "items": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "ResponseOutputItemAddedEvent": { + "type": "object", + "description": "Emitted when a new output item is added.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.output_item.added`.\n", + "enum": [ + "response.output_item.added" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that was added.\n" + }, + "item": { + "$ref": "#/components/schemas/OutputItem", + "x-oaiExpandable": true, + "description": "The output item that was added.\n" + } + }, + "required": [ + "type", + "output_index", + "item" + ], + "x-oaiMeta": { + "name": "response.output_item.added", + "group": "responses", + "example": "{\n \"type\": \"response.output_item.added\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"in_progress\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": []\n }\n}\n" + } + }, + "ResponseOutputItemDoneEvent": { + "type": "object", + "description": "Emitted when an output item is marked done.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.output_item.done`.\n", + "enum": [ + "response.output_item.done" + ], + "x-stainless-const": true + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that was marked done.\n" + }, + "item": { + "$ref": "#/components/schemas/OutputItem", + "x-oaiExpandable": true, + "description": "The output item that was marked done.\n" + } + }, + "required": [ + "type", + "output_index", + "item" + ], + "x-oaiMeta": { + "name": "response.output_item.done", + "group": "responses", + "example": "{\n \"type\": \"response.output_item.done\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"completed\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n}\n" + } + }, + "ResponseProperties": { + "type": "object", + "properties": { + "previous_response_id": { + "type": "string", + "description": "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about \n[conversation state](/docs/guides/conversation-state).\n", + "nullable": true + }, + "reasoning": { + "$ref": "#/components/schemas/Reasoning", + "nullable": true + }, + "max_output_tokens": { + "description": "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n", + "type": "integer", + "nullable": true + }, + "instructions": { + "type": "string", + "description": "Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will be not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n", + "nullable": true + }, + "text": { + "type": "object", + "description": "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)\n", + "properties": { + "format": { + "$ref": "#/components/schemas/TextResponseFormatConfiguration" + } + } + }, + "tools": { + "type": "array", + "description": "An array of tools the model may call while generating a response. You \ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](/docs/guides/tools-web-search)\n or [file search](/docs/guides/tools-file-search). Learn more about\n [built-in tools](/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code. Learn more about\n [function calling](/docs/guides/function-calling).\n", + "items": { + "$ref": "#/components/schemas/Tool" + } + }, + "tool_choice": { + "description": "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n", + "x-oaiExpandable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ToolChoiceOptions" + }, + { + "$ref": "#/components/schemas/ToolChoiceTypes" + }, + { + "$ref": "#/components/schemas/ToolChoiceFunction" + } + ] + }, + "truncation": { + "type": "string", + "description": "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the \n response to fit the context window by dropping input items in the\n middle of the conversation. \n- `disabled` (default): If a model response will exceed the context window \n size for a model, the request will fail with a 400 error.\n", + "enum": [ + "auto", + "disabled" + ], + "nullable": true, + "default": "disabled" + } + } + }, + "ResponseRefusalDeltaEvent": { + "type": "object", + "description": "Emitted when there is a partial refusal text.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.refusal.delta`.\n", + "enum": [ + "response.refusal.delta" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the refusal text is added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the refusal text is added to.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that the refusal text is added to.\n" + }, + "delta": { + "type": "string", + "description": "The refusal text that is added.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "delta" + ], + "x-oaiMeta": { + "name": "response.refusal.delta", + "group": "responses", + "example": "{\n \"type\": \"response.refusal.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"refusal text so far\"\n}\n" + } + }, + "ResponseRefusalDoneEvent": { + "type": "object", + "description": "Emitted when refusal text is finalized.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.refusal.done`.\n", + "enum": [ + "response.refusal.done" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the refusal text is finalized.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the refusal text is finalized.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that the refusal text is finalized.\n" + }, + "refusal": { + "type": "string", + "description": "The refusal text that is finalized.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "refusal" + ], + "x-oaiMeta": { + "name": "response.refusal.done", + "group": "responses", + "example": "{\n \"type\": \"response.refusal.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"content_index\": 2,\n \"refusal\": \"final refusal text\"\n}\n" + } + }, + "responseStreamEvent": { + "anyOf": [ + { + "$ref": "#/components/schemas/ResponseAudioDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseAudioDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseAudioTranscriptDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseAudioTranscriptDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseCodeInterpreterCallCompletedEvent" + }, + { + "$ref": "#/components/schemas/ResponseCodeInterpreterCallInProgressEvent" + }, + { + "$ref": "#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent" + }, + { + "$ref": "#/components/schemas/ResponseCompletedEvent" + }, + { + "$ref": "#/components/schemas/ResponseContentPartAddedEvent" + }, + { + "$ref": "#/components/schemas/ResponseContentPartDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseCreatedEvent" + }, + { + "$ref": "#/components/schemas/ResponseErrorEvent" + }, + { + "$ref": "#/components/schemas/ResponseFileSearchCallCompletedEvent" + }, + { + "$ref": "#/components/schemas/ResponseFileSearchCallInProgressEvent" + }, + { + "$ref": "#/components/schemas/ResponseFileSearchCallSearchingEvent" + }, + { + "$ref": "#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseFunctionCallArgumentsDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseInProgressEvent" + }, + { + "$ref": "#/components/schemas/ResponseFailedEvent" + }, + { + "$ref": "#/components/schemas/ResponseIncompleteEvent" + }, + { + "$ref": "#/components/schemas/ResponseOutputItemAddedEvent" + }, + { + "$ref": "#/components/schemas/ResponseOutputItemDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseRefusalDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseRefusalDoneEvent" + }, + { + "$ref": "#/components/schemas/ResponseTextAnnotationDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseTextDeltaEvent" + }, + { + "$ref": "#/components/schemas/ResponseTextDoneEvent" + } + ], + "discriminator": { + "propertyName": "type" + } + }, + "ResponseTextAnnotationDeltaEvent": { + "type": "object", + "description": "Emitted when a text annotation is added.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.output_text.annotation.added`.\n", + "enum": [ + "response.output_text.annotation.added" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the text annotation was added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the text annotation was added to.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that the text annotation was added to.\n" + }, + "annotation_index": { + "type": "integer", + "description": "The index of the annotation that was added.\n" + }, + "annotation": { + "$ref": "#/components/schemas/Annotation" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "annotation_index", + "annotation" + ], + "x-oaiMeta": { + "name": "response.output_text.annotation.added", + "group": "responses", + "example": "{\n \"type\": \"response.output_text.annotation.added\",\n \"item_id\": \"msg_abc123\",\n \"output_index\": 1,\n \"content_index\": 0,\n \"annotation_index\": 0,\n \"annotation\": {\n \"type\": \"file_citation\",\n \"index\": 390,\n \"file_id\": \"file-4wDz5b167pAf72nx1h9eiN\",\n \"filename\": \"dragons.pdf\"\n }\n}\n" + } + }, + "ResponseTextDeltaEvent": { + "type": "object", + "description": "Emitted when there is an additional text delta.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.output_text.delta`.\n", + "enum": [ + "response.output_text.delta" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the text delta was added to.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the text delta was added to.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that the text delta was added to.\n" + }, + "delta": { + "type": "string", + "description": "The text delta that was added.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "delta" + ], + "x-oaiMeta": { + "name": "response.output_text.delta", + "group": "responses", + "example": "{\n \"type\": \"response.output_text.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"In\"\n}\n" + } + }, + "ResponseTextDoneEvent": { + "type": "object", + "description": "Emitted when text content is finalized.", + "properties": { + "type": { + "type": "string", + "description": "The type of the event. Always `response.output_text.done`.\n", + "enum": [ + "response.output_text.done" + ], + "x-stainless-const": true + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the text content is finalized.\n" + }, + "output_index": { + "type": "integer", + "description": "The index of the output item that the text content is finalized.\n" + }, + "content_index": { + "type": "integer", + "description": "The index of the content part that the text content is finalized.\n" + }, + "text": { + "type": "string", + "description": "The text content that is finalized.\n" + } + }, + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "text" + ], + "x-oaiMeta": { + "name": "response.output_text.done", + "group": "responses", + "example": "{\n \"type\": \"response.output_text.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\"\n}\n" + } + }, + "ResponseUsage": { + "type": "object", + "description": "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n", + "properties": { + "input_tokens": { + "type": "integer", + "description": "The number of input tokens." + }, + "output_tokens": { + "type": "integer", + "description": "The number of output tokens." + }, + "output_tokens_details": { + "type": "object", + "description": "A detailed breakdown of the output tokens.", + "properties": { + "reasoning_tokens": { + "type": "integer", + "description": "The number of reasoning tokens." + } + }, + "required": [ + "reasoning_tokens" + ] + }, + "total_tokens": { + "type": "integer", + "description": "The total number of tokens used." + } + }, + "required": [ + "input_tokens", + "output_tokens", + "output_tokens_details", + "total_tokens" + ] + }, + "Screenshot": { + "type": "object", + "title": "Screenshot", + "description": "A screenshot action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "screenshot" + ], + "default": "screenshot", + "description": "Specifies the event type. For a screenshot action, this property is \nalways set to `screenshot`.\n", + "x-stainless-const": true + } + }, + "required": [ + "type" + ] + }, + "Scroll": { + "type": "object", + "title": "Scroll", + "description": "A scroll action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "scroll" + ], + "default": "scroll", + "description": "Specifies the event type. For a scroll action, this property is \nalways set to `scroll`.\n", + "x-stainless-const": true + }, + "x": { + "type": "integer", + "description": "The x-coordinate where the scroll occurred.\n" + }, + "y": { + "type": "integer", + "description": "The y-coordinate where the scroll occurred.\n" + }, + "scroll_x": { + "type": "integer", + "description": "The horizontal scroll distance.\n" + }, + "scroll_y": { + "type": "integer", + "description": "The vertical scroll distance.\n" + } + }, + "required": [ + "type", + "x", + "y", + "scroll_x", + "scroll_y" + ] + }, + "StopConfiguration": { + "description": "Up to 4 sequences where the API will stop generating further tokens. The\nreturned text will not contain the stop sequence.\n", + "default": null, + "nullable": true, + "oneOf": [ + { + "type": "string", + "default": "<|endoftext|>", + "example": "\n", + "nullable": true + }, + { + "type": "array", + "minItems": 1, + "maxItems": 4, + "items": { + "type": "string", + "example": "[\"\\n\"]" + } + } + ] + }, + "TextResponseFormatConfiguration": { + "description": "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs, \nwhich ensures the model will match your supplied JSON schema. Learn more in the \n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n", + "oneOf": [ + { + "$ref": "#/components/schemas/ResponseFormatText" + }, + { + "$ref": "#/components/schemas/TextResponseFormatJsonSchema" + }, + { + "$ref": "#/components/schemas/ResponseFormatJsonObject" + } + ], + "x-oaiExpandable": true + }, + "TextResponseFormatJsonSchema": { + "type": "object", + "title": "JSON schema", + "description": "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n", + "properties": { + "type": { + "type": "string", + "description": "The type of response format being defined. Always `json_schema`.", + "enum": [ + "json_schema" + ], + "x-stainless-const": true + }, + "description": { + "type": "string", + "description": "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + }, + "name": { + "type": "string", + "description": "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + }, + "schema": { + "$ref": "#/components/schemas/ResponseFormatJsonSchemaSchema" + }, + "strict": { + "type": "boolean", + "nullable": true, + "default": false, + "description": "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n" + } + }, + "required": [ + "type", + "schema" + ] + }, + "Tool": { + "oneOf": [ + { + "$ref": "#/components/schemas/FileSearchTool" + }, + { + "$ref": "#/components/schemas/FunctionTool" + }, + { + "$ref": "#/components/schemas/ComputerTool" + } + ], + "x-oaiExpandable": true + }, + "ToolChoiceFunction": { + "type": "object", + "title": "Function tool", + "description": "Use this option to force the model to call a specific function.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "For function calling, the type is always `function`.", + "x-stainless-const": true + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "type", + "name" + ] + }, + "ToolChoiceOptions": { + "type": "string", + "title": "Tool choice mode", + "description": "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n", + "enum": [ + "none", + "auto", + "required" + ] + }, + "ToolChoiceTypes": { + "type": "object", + "title": "Hosted tool", + "description": "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n", + "properties": { + "type": { + "type": "string", + "description": "The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `computer_use_preview`\n", + "enum": [ + "file_search", + "computer_use_preview" + ] + } + }, + "required": [ + "type" + ] + }, + "Type": { + "type": "object", + "title": "Type", + "description": "An action to type in text.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "type" + ], + "default": "type", + "description": "Specifies the event type. For a type action, this property is \nalways set to `type`.\n", + "x-stainless-const": true + }, + "text": { + "type": "string", + "description": "The text to type.\n" + } + }, + "required": [ + "type", + "text" + ] + }, + "UpdateVectorStoreFileAttributesRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "attributes": { + "$ref": "#/components/schemas/VectorStoreFileAttributes" + } + }, + "required": [ + "attributes" + ], + "x-oaiMeta": { + "name": "Update vector store file attributes request" + } + }, + "UrlCitation": { + "type": "object", + "title": "URL citation", + "description": "A citation for a web resource used to generate a model response.\n", + "properties": { + "url": { + "type": "string", + "description": "The URL of the web resource.\n" + }, + "title": { + "type": "string", + "description": "The title of the web resource.\n" + }, + "type": { + "type": "string", + "description": "The type of the URL citation. Always `url_citation`.\n", + "enum": [ + "url_citation" + ], + "x-stainless-const": true + }, + "start_index": { + "type": "integer", + "description": "The index of the first character of the URL citation in the message.\n" + }, + "end_index": { + "type": "integer", + "description": "The index of the last character of the URL citation in the message.\n" + } + }, + "required": [ + "url", + "title", + "type", + "start_index", + "end_index" + ] + }, + "VectorStoreFileAttributes": { + "type": "object", + "description": "Set of 16 key-value pairs that can be attached to an object. This can be \nuseful for storing additional information about the object in a structured \nformat, and querying for objects via API or the dashboard. Keys are strings \nwith a maximum length of 64 characters. Values are strings with a maximum \nlength of 512 characters, booleans, or numbers.\n", + "maxProperties": 16, + "additionalProperties": { + "oneOf": [ + { + "type": "string", + "maxLength": 512 + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + "x-oaiTypeLabel": "map", + "nullable": true + }, + "VectorStoreFileContentResponse": { + "type": "object", + "description": "Represents the parsed content of a vector store file.", + "properties": { + "object": { + "type": "string", + "enum": [ + "vector_store.file_content.page" + ], + "description": "The object type, which is always `vector_store.file_content.page`", + "x-stainless-const": true + }, + "data": { + "type": "array", + "description": "Parsed content of the file.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "The content type (currently only `\"text\"`)" + }, + "text": { + "type": "string", + "description": "The text content" + } + } + } + }, + "has_more": { + "type": "boolean", + "description": "Indicates if there are more content pages to fetch." + }, + "next_page": { + "type": "string", + "description": "The token for the next page, if any.", + "nullable": true + } + }, + "required": [ + "object", + "data", + "has_more", + "next_page" + ] + }, + "VectorStoreSearchRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "query": { + "description": "A query string for a search", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string", + "description": "A list of queries to search for.", + "minItems": 1 + } + } + ] + }, + "rewrite_query": { + "description": "Whether to rewrite the natural language query for vector search.", + "type": "boolean", + "default": false + }, + "max_num_results": { + "description": "The maximum number of results to return. This number should be between 1 and 50 inclusive.", + "type": "integer", + "default": 10, + "minimum": 1, + "maximum": 50 + }, + "filters": { + "description": "A filter to apply based on file attributes.", + "oneOf": [ + { + "$ref": "#/components/schemas/ComparisonFilter" + }, + { + "$ref": "#/components/schemas/CompoundFilter" + } + ], + "x-oaiExpandable": true + }, + "ranking_options": { + "description": "Ranking options for search.", + "type": "object", + "additionalProperties": false, + "properties": { + "ranker": { + "type": "string", + "enum": [ + "auto", + "default-2024-11-15" + ], + "default": "auto" + }, + "score_threshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0 + } + } + } + }, + "required": [ + "query" + ], + "x-oaiMeta": { + "name": "Vector store search request" + } + }, + "VectorStoreSearchResultContentObject": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "description": "The type of content.", + "type": "string", + "enum": [ + "text" + ] + }, + "text": { + "description": "The text content returned from search.", + "type": "string" + } + }, + "required": [ + "type", + "text" + ], + "x-oaiMeta": { + "name": "Vector store search result content object" + } + }, + "VectorStoreSearchResultItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "file_id": { + "type": "string", + "description": "The ID of the vector store file." + }, + "filename": { + "type": "string", + "description": "The name of the vector store file." + }, + "score": { + "type": "number", + "description": "The similarity score for the result.", + "minimum": 0, + "maximum": 1 + }, + "attributes": { + "$ref": "#/components/schemas/VectorStoreFileAttributes" + }, + "content": { + "type": "array", + "description": "Content chunks from the file.", + "items": { + "$ref": "#/components/schemas/VectorStoreSearchResultContentObject" + } + } + }, + "required": [ + "file_id", + "filename", + "score", + "attributes", + "content" + ], + "x-oaiMeta": { + "name": "Vector store search result item" + } + }, + "VectorStoreSearchResultsPage": { + "type": "object", + "additionalProperties": false, + "properties": { + "object": { + "type": "string", + "enum": [ + "vector_store.search_results.page" + ], + "description": "The object type, which is always `vector_store.search_results.page`", + "x-stainless-const": true + }, + "search_query": { + "type": "array", + "items": { + "type": "string", + "description": "The query used for this search.", + "minItems": 1 + } + }, + "data": { + "type": "array", + "description": "The list of search result items.", + "items": { + "$ref": "#/components/schemas/VectorStoreSearchResultItem" + } + }, + "has_more": { + "type": "boolean", + "description": "Indicates if there are more results to fetch." + }, + "next_page": { + "type": "string", + "description": "The token for the next page, if any.", + "nullable": true + } + }, + "required": [ + "object", + "search_query", + "data", + "has_more", + "next_page" + ], + "x-oaiMeta": { + "name": "Vector store search results page" + } + }, + "Wait": { + "type": "object", + "title": "Wait", + "description": "A wait action.\n", + "properties": { + "type": { + "type": "string", + "enum": [ + "wait" + ], + "default": "wait", + "description": "Specifies the event type. For a wait action, this property is \nalways set to `wait`.\n", + "x-stainless-const": true + } + }, + "required": [ + "type" + ] + }, + "ReasoningEffort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "default": "medium", + "nullable": true, + "description": "**o-series models only** \n\nConstrains effort on reasoning for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n" + }, + "errorEvent": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "error" + ], + "x-ms-enum": { + "name": "ErrorEventEnum", + "modelAsString": true, + "values": [ + { + "value": "error" + } + ] + } + }, + "data": { + "$ref": "#/components/schemas/error" + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when an error occurs. This can happen due to an internal server error or a timeout." + }, + "doneEvent": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "done" + ], + "x-ms-enum": { + "name": "DoneEventEnum", + "modelAsString": true, + "values": [ + { + "value": "done" + } + ] + } + }, + "data": { + "type": "string", + "enum": [ + "[DONE]" + ], + "x-ms-enum": { + "name": "DoneEventDataEnum", + "modelAsString": true, + "values": [ + { + "value": "[DONE]" + } + ] + } + } + }, + "required": [ + "event", + "data" + ], + "description": "Occurs when a stream ends." + } + }, + "securitySchemes": { + "bearer": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + "scopes": {} + } + }, + "x-tokenInfoFunc": "api.middleware.auth.bearer_auth", + "x-scopeValidateFunc": "api.middleware.auth.validate_scopes" + }, + "apiKey": { + "type": "apiKey", + "name": "api-key", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAIV1.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAIV1.json new file mode 100644 index 000000000..85029e166 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/AIFoundryOpenAIV1.json @@ -0,0 +1,20744 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Microsoft Foundry Models Service", + "license": { + "name": "MIT", + "url": "https://github.com/openai/openai-openapi/blob/master/LICENSE" + }, + "version": "v1" + }, + "paths": { + "/deployments": { + "get": { + "summary": "List deployments", + "operationId": "deployments", + "description": "Get deployments for Azure OpenAI resource for Microsoft Foundry Integration", + "parameters": [ + ], + "responses": { + "200": { + "description": "AI Foundry integration operations for retrieving available deployments", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "x-ms-error-code": { + "required": false, + "description": "String error code indicating what went wrong.", + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/deployments/{deployment-id}": { + "get": { + "summary": "Get deployment by name", + "operationId": "deployment-by-name", + "description": "Get deployment details by deployment name for LLM resource for Microsoft Foundry Integration", + "parameters": [{ + "name": "deployment-id", + "in": "path", + "required": true, + "schema": { + "enum": [""], + "type": "" + } + }], + "responses": { + "200": { + "description": "" + } + } + } + }, + "/chat/completions": { + "post": { + "operationId": "createChatCompletion", + "description": "Creates a chat completion.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureCreateChatCompletionResponse" + } + }, + "text/event-stream": { + "schema": { + "$ref": "#/components/schemas/AzureCreateChatCompletionStreamResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Chat" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureCreateChatCompletionRequest" + }, + "example": { + "model": "gpt-4o", + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant" + }, + { + "role": "user", + "content": "How are you?" + } + ], + "max_tokens": 50 + } + } + } + }, + "x-ms-examples": { + "Create a chat completion": { + "$ref": "./examples/chat_completions.yaml" + } + }, + "summary": "Creates a chat completion." + } + }, + "/containers": { + "get": { + "operationId": "ListContainers", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerListResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + }, + "post": { + "operationId": "CreateContainer", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateContainerBody" + } + } + } + } + } + }, + "/containers/{container_id}": { + "get": { + "operationId": "RetrieveContainer", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + }, + "delete": { + "operationId": "DeleteContainer", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "id", + "object", + "deleted" + ], + "properties": { + "id": { + "type": "string" + }, + "object": { + "type": "string", + "enum": [ + "container.deleted" + ] + }, + "deleted": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + } + }, + "/containers/{container_id}/files": { + "post": { + "operationId": "CreateContainerFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerFileResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateContainerFileBodyMultiPart" + } + } + } + } + }, + "get": { + "operationId": "ListContainerFiles", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerFileListResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + } + }, + "/containers/{container_id}/files/{file_id}": { + "get": { + "operationId": "RetrieveContainerFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ContainerFileResource" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + }, + "delete": { + "operationId": "DeleteContainerFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "id", + "object", + "deleted" + ], + "properties": { + "id": { + "type": "string" + }, + "object": { + "type": "string", + "enum": [ + "container.file.deleted" + ] + }, + "deleted": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + } + }, + "/containers/{container_id}/files/{file_id}/content": { + "get": { + "operationId": "RetrieveContainerFileContent", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Containers" + ] + } + }, + "/embeddings": { + "post": { + "operationId": "createEmbedding", + "summary": "Creates an embedding vector representing the input text.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateEmbeddingResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Embeddings" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureCreateEmbeddingRequest" + }, + "example": { + "model": "text-embedding-ada-002", + "input": "How are you?" + } + } + } + }, + "x-ms-examples": { + "Create an embedding request": { + "$ref": "./examples/embeddings.yaml" + } + } + } + }, + "/evals": { + "get": { + "operationId": "listEvals", + "summary": "List evaluations for a project.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "Identifier for the last eval from the previous pagination request.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of evals to be returned in a single pagination response.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order for evals by timestamp. Use `asc` for ascending order or\n`desc` for descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + }, + "explode": false + }, + { + "name": "order_by", + "in": "query", + "required": false, + "description": "Evals can be ordered by creation time or last updated time. Use\n`created_at` for creation time or `updated_at` for last updated\ntime.", + "schema": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ], + "default": "created_at" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalList" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ] + }, + "post": { + "operationId": "createEval", + "description": "Create the structure of an evaluation that can be used to test a model's\nperformance.\n\nAn evaluation is a set of testing criteria and a datasource. After\ncreating an evaluation, you can run it on different models and model\nparameters. We support several types of graders and datasources.\n\nFor more information, see the [Evals guide](/docs/guides/evals).\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.Eval" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "statusCode": { + "type": "number", + "enum": [ + 201 + ] + }, + "name": { + "type": "string", + "description": "The name of the evaluation." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "data_source_config": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigParams" + } + ], + "description": "The configuration for the data source used for the evaluation runs. Dictates the schema of the data used in the evaluation." + }, + "testing_criteria": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + }, + "description": "A list of graders for all eval runs in this group. Graders can reference variables in the data source using double curly braces notation, like `{{item.variable_name}}`. To reference the model's output, use the `sample` namespace (ie, `{{sample.output_text}}`)." + } + }, + "required": [ + "statusCode", + "data_source_config", + "testing_criteria" + ] + } + } + } + }, + "summary": "Create the structure of an evaluation that can be used to test a model's\nperformance." + } + }, + "/evals/{eval_id}": { + "get": { + "operationId": "getEval", + "summary": "Retrieves an evaluation by its ID.", + "description": "Retrieves an evaluation by its ID.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.Eval" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ] + }, + "post": { + "operationId": "updateEval", + "description": "Update select, mutable properties of a specified evaluation.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.Eval" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/OpenAI.MetadataPropertyForRequest" + } + } + } + } + } + }, + "summary": "Update select, mutable properties of a specified evaluation." + }, + "delete": { + "operationId": "deleteEval", + "description": "Delete a specified evaluation.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "object", + "deleted", + "eval_id" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "eval.deleted" + ] + }, + "deleted": { + "type": "boolean" + }, + "eval_id": { + "type": "string" + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Delete a specified evaluation." + } + }, + "/evals/{eval_id}/runs": { + "get": { + "operationId": "getEvalRuns", + "summary": "Retrieve a list of runs for a specified evaluation.", + "description": "Retrieve a list of runs for a specified evaluation.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "after", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + }, + "explode": false + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "queued", + "in_progress", + "completed", + "canceled", + "failed" + ] + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRunList" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ] + }, + "post": { + "operationId": "createEvalRun", + "description": "Create a new evaluation run, beginning the grading process.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "The request has succeeded and a new resource has been created as a result.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRun" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateEvalRunRequest" + } + } + } + }, + "summary": "Create a new evaluation run, beginning the grading process." + } + }, + "/evals/{eval_id}/runs/{run_id}": { + "get": { + "operationId": "getEvalRun", + "description": "Retrieve a specific evaluation run by its ID.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRun" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Retrieve a specific evaluation run by its ID." + }, + "post": { + "operationId": "cancelEvalRun", + "description": "Cancel a specific evaluation run by its ID.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRun" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Cancel a specific evaluation run by its ID." + }, + "delete": { + "operationId": "deleteEvalRun", + "description": "Delete a specific evaluation run by its ID.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "object", + "deleted", + "eval_run_id" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "eval_run.deleted" + ] + }, + "deleted": { + "type": "boolean" + }, + "eval_run_id": { + "type": "string" + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Delete a specific evaluation run by its ID." + } + }, + "/evals/{eval_id}/runs/{run_id}/output_items": { + "get": { + "operationId": "getEvalRunOutputItems", + "description": "Get a list of output items for a specified evaluation run.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "after", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "status", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "fail", + "pass" + ] + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRunOutputItemList" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Get a list of output items for a specified evaluation run." + } + }, + "/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}": { + "get": { + "operationId": "getEvalRunOutputItem", + "description": "Retrieve a specific output item from an evaluation run by its ID.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-evals", + "in": "header", + "required": true, + "description": "Enables access to AOAI Evals, a preview feature.\nThis feature requires the 'aoai-evals' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "eval_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "output_item_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.EvalRunOutputItem" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Evals" + ], + "summary": "Retrieve a specific output item from an evaluation run by its ID." + } + }, + "/files": { + "post": { + "operationId": "createFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureOpenAIFile" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Files" + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AzureCreateFileRequestMultiPart" + } + } + } + }, + "x-ms-examples": { + "Create a file request": { + "$ref": "./examples/files.yaml" + } + } + }, + "get": { + "operationId": "listFiles", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "purpose", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureListFilesResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Files" + ] + } + }, + "/files/{file_id}": { + "get": { + "operationId": "retrieveFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "description": "The ID of the file to use for this request.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureOpenAIFile" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Files" + ] + }, + "delete": { + "operationId": "deleteFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "description": "The ID of the file to use for this request.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.DeleteFileResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Files" + ] + } + }, + "/files/{file_id}/content": { + "get": { + "operationId": "downloadFile", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "description": "The ID of the file to use for this request.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Files" + ] + } + }, + "/fine_tuning/alpha/graders/run": { + "post": { + "operationId": "runGrader", + "summary": "Run a grader.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.RunGraderResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.RunGraderRequest" + } + } + } + } + } + }, + "/fine_tuning/alpha/graders/validate": { + "post": { + "operationId": "validateGrader", + "summary": "Validate a grader.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ValidateGraderResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ValidateGraderRequest" + } + } + } + } + } + }, + "/fine_tuning/jobs": { + "post": { + "operationId": "createFineTuningJob", + "summary": "Creates a fine-tuning job which begins the process of creating a new model from a given dataset.\n\nResponse includes details of the enqueued job including job status and the name of the fine-tuned models once complete.\n\n[Learn more about fine-tuning](/docs/guides/fine-tuning)", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateFineTuningJobRequest" + } + } + } + } + }, + "get": { + "operationId": "listPaginatedFineTuningJobs", + "summary": "List your organization's fine-tuning jobs", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "Identifier for the last job from the previous pagination request.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Number of fine-tuning jobs to retrieve.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListPaginatedFineTuningJobsResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}": { + "get": { + "operationId": "retrieveFineTuningJob", + "summary": "Get info about a fine-tuning job.\n\n[Learn more about fine-tuning](/docs/guides/fine-tuning)", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/cancel": { + "post": { + "operationId": "cancelFineTuningJob", + "summary": "Immediately cancel a fine-tune job.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job to cancel.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints": { + "get": { + "operationId": "listFineTuningJobCheckpoints", + "summary": "List the checkpoints for a fine-tuning job.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job to get checkpoints for.", + "schema": { + "type": "string" + } + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "Identifier for the last checkpoint ID from the previous pagination request.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Number of checkpoints to retrieve.", + "schema": { + "type": "integer", + "format": "int32", + "default": 10 + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListFineTuningJobCheckpointsResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints/{fine_tuning_checkpoint_id}/copy": { + "post": { + "operationId": "FineTuning_CopyCheckpoint", + "description": "Creates a copy of a fine-tuning checkpoint at the given destination account and region.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-copy-ft-checkpoints", + "in": "header", + "required": true, + "description": "Enables access to checkpoint copy operations for models, an AOAI preview feature.\nThis feature requires the 'aoai-copy-ft-checkpoints' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "accept", + "in": "header", + "required": true, + "schema": { + "type": "string", + "enum": [ + "application/json" + ] + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "fine_tuning_checkpoint_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CopyModelResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CopyModelRequest" + } + } + } + }, + "summary": "Creates a copy of a fine-tuning checkpoint at the given destination account and region." + }, + "get": { + "operationId": "FineTuning_GetCheckpoint", + "description": "Gets the status of a fine-tuning checkpoint copy.\n\nNOTE: This Azure OpenAI API is in preview and subject to change.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "aoai-copy-ft-checkpoints", + "in": "header", + "required": true, + "description": "Enables access to checkpoint copy operations for models, an AOAI preview feature.\nThis feature requires the 'aoai-copy-ft-checkpoints' header to be set to 'preview'.", + "schema": { + "type": "string", + "enum": [ + "preview" + ] + } + }, + { + "name": "accept", + "in": "header", + "required": true, + "schema": { + "type": "string", + "enum": [ + "application/json" + ] + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "fine_tuning_checkpoint_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CopyModelResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ], + "summary": "Gets the status of a fine-tuning checkpoint copy." + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/events": { + "get": { + "operationId": "listFineTuningEvents", + "summary": "Get status updates for a fine-tuning job.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job to get events for.", + "schema": { + "type": "string" + } + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "Identifier for the last event from the previous pagination request.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Number of events to retrieve.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListFineTuningJobEventsResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/pause": { + "post": { + "operationId": "pauseFineTuningJob", + "summary": "Pause a fine-tune job.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job to pause.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/fine_tuning/jobs/{fine_tuning_job_id}/resume": { + "post": { + "operationId": "resumeFineTuningJob", + "summary": "Resume a paused fine-tune job.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "fine_tuning_job_id", + "in": "path", + "required": true, + "description": "The ID of the fine-tuning job to resume.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Fine-tuning" + ] + } + }, + "/models": { + "get": { + "operationId": "listModels", + "summary": "Lists the currently available models, and provides basic information about each one such as the\nowner and availability.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListModelsResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/models/{model}": { + "get": { + "operationId": "retrieveModel", + "summary": "Retrieves a model instance, providing basic information about the model such as the owner and\npermissioning.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "model", + "in": "path", + "required": true, + "description": "The ID of the model to use for this request.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.Model" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Models" + ] + } + }, + "/responses": { + "post": { + "operationId": "createResponse", + "description": "Creates a model response.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureResponse" + } + }, + "text/event-stream": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Responses" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureCreateResponse" + }, + "example": { + "model": "gpt-4o", + "input": "How are you?", + "stream": false + } + } + } + }, + "x-ms-examples": { + "Create a response request": { + "$ref": "./examples/responses.yaml" + } + }, + "summary": "Creates a model response." + } + }, + "/responses/{response_id}": { + "get": { + "operationId": "getResponse", + "description": "Retrieves a model response with the given ID.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "response_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "include_obfuscation", + "in": "query", + "required": false, + "description": "When true, stream obfuscation will be enabled. Stream obfuscation adds random characters to an `obfuscation` field on streaming delta events to normalize payload sizes as a mitigation to certain side-channel attacks. These obfuscation fields are included by default, but add a small amount of overhead to the data stream. You can set `include_obfuscation` to false to optimize for bandwidth if you trust the network links between your application and the OpenAI API.", + "schema": { + "type": "boolean", + "default": true + }, + "explode": false + }, + { + "name": "include[]", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Includable" + }, + "default": [] + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Responses" + ], + "summary": "Retrieves a model response with the given ID." + }, + "delete": { + "operationId": "deleteResponse", + "description": "Deletes a response by ID.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "response_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "object", + "id", + "deleted" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "response.deleted" + ] + }, + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Responses" + ], + "summary": "Deletes a response by ID." + } + }, + "/responses/{response_id}/input_items": { + "get": { + "operationId": "listInputItems", + "description": "Returns a list of input items for a given response.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "response_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "before", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include before=obj_foo in order to fetch the previous page of the list.", + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ResponseItemList" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Responses" + ], + "summary": "Returns a list of input items for a given response." + } + }, + "/vector_stores": { + "get": { + "operationId": "listVectorStores", + "summary": "Returns a list of vector stores.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "before", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include before=obj_foo in order to fetch the previous page of the list.", + "schema": { + "type": "string" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListVectorStoresResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + }, + "post": { + "operationId": "createVectorStore", + "summary": "Creates a vector store.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateVectorStoreRequest" + } + } + } + }, + "x-ms-examples": { + "Create a vector store request": { + "$ref": "./examples/vector_stores.yaml" + } + } + } + }, + "/vector_stores/{vector_store_id}": { + "get": { + "operationId": "getVectorStore", + "summary": "Retrieves a vector store.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store to retrieve.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + }, + "post": { + "operationId": "modifyVectorStore", + "summary": "Modifies a vector store.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store to modify.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.UpdateVectorStoreRequest" + } + } + } + } + }, + "delete": { + "operationId": "deleteVectorStore", + "summary": "Delete a vector store.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store to delete.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.DeleteVectorStoreResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + } + }, + "/vector_stores/{vector_store_id}/file_batches": { + "post": { + "operationId": "createVectorStoreFileBatch", + "summary": "Create a vector store file batch.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store for which to create a file batch.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileBatchObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateVectorStoreFileBatchRequest" + } + } + } + } + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}": { + "get": { + "operationId": "getVectorStoreFileBatch", + "summary": "Retrieves a vector store file batch.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the file batch belongs to.", + "schema": { + "type": "string" + } + }, + { + "name": "batch_id", + "in": "path", + "required": true, + "description": "The ID of the file batch being retrieved.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileBatchObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel": { + "post": { + "operationId": "cancelVectorStoreFileBatch", + "summary": "Cancel a vector store file batch. This attempts to cancel the processing of files in this batch as soon as possible.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the file batch belongs to.", + "schema": { + "type": "string" + } + }, + { + "name": "batch_id", + "in": "path", + "required": true, + "description": "The ID of the file batch to cancel.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileBatchObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + } + }, + "/vector_stores/{vector_store_id}/file_batches/{batch_id}/files": { + "get": { + "operationId": "listFilesInVectorStoreBatch", + "summary": "Returns a list of vector store files in a batch.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the file batch belongs to.", + "schema": { + "type": "string" + } + }, + { + "name": "batch_id", + "in": "path", + "required": true, + "description": "The ID of the file batch that the files belong to.", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "before", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include before=obj_foo in order to fetch the previous page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "filter", + "in": "query", + "required": false, + "description": "Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.", + "schema": { + "$ref": "#/components/schemas/OpenAI.ListVectorStoreFilesFilter" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListVectorStoreFilesResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + } + }, + "/vector_stores/{vector_store_id}/files": { + "get": { + "operationId": "listVectorStoreFiles", + "summary": "Returns a list of vector store files.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the files belong to.", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the\ndefault is 20.", + "schema": { + "type": "integer", + "format": "int32", + "default": 20 + }, + "explode": false + }, + { + "name": "order", + "in": "query", + "required": false, + "description": "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and`desc`\nfor descending order.", + "schema": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "explode": false + }, + { + "name": "after", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `after` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include after=obj_foo in order to fetch the next page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "before", + "in": "query", + "required": false, + "description": "A cursor for use in pagination. `before` is an object ID that defines your place in the list.\nFor instance, if you make a list request and receive 100 objects, ending with obj_foo, your\nsubsequent call can include before=obj_foo in order to fetch the previous page of the list.", + "schema": { + "type": "string" + }, + "explode": false + }, + { + "name": "filter", + "in": "query", + "required": false, + "description": "Filter by file status. One of `in_progress`, `completed`, `failed`, `cancelled`.", + "schema": { + "$ref": "#/components/schemas/OpenAI.ListVectorStoreFilesFilter" + }, + "explode": false + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.ListVectorStoreFilesResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + }, + "post": { + "operationId": "createVectorStoreFile", + "summary": "Create a vector store file by attaching a [File](/docs/api-reference/files) to a [vector store](/docs/api-reference/vector-stores/object).", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store for which to create a File.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.CreateVectorStoreFileRequest" + } + } + } + } + } + }, + "/vector_stores/{vector_store_id}/files/{file_id}": { + "get": { + "operationId": "getVectorStoreFile", + "summary": "Retrieves a vector store file.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the file belongs to.", + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "description": "The ID of the file being retrieved.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + }, + "post": { + "operationId": "updateVectorStoreFileAttributes", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileObject" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.UpdateVectorStoreFileAttributesRequest" + } + } + } + } + }, + "delete": { + "operationId": "deleteVectorStoreFile", + "summary": "Delete a vector store file. This will remove the file from the vector store but the file itself will not be deleted. To delete the file, use the [delete file](/docs/api-reference/files/delete) endpoint.", + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": false, + "description": "The explicit Microsoft Foundry Models API version to use for this request.\n`v1` if not otherwise specified.", + "schema": { + "$ref": "#/components/schemas/AzureAIFoundryModelsApiVersion", + "default": "v1" + } + }, + { + "name": "vector_store_id", + "in": "path", + "required": true, + "description": "The ID of the vector store that the file belongs to.", + "schema": { + "type": "string" + } + }, + { + "name": "file_id", + "in": "path", + "required": true, + "description": "The ID of the file to delete.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The request has succeeded.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OpenAI.DeleteVectorStoreFileResponse" + } + } + } + }, + "default": { + "description": "An unexpected error response.", + "headers": { + "apim-request-id": { + "required": false, + "description": "A request ID used for troubleshooting purposes.", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AzureErrorResponse" + } + } + } + } + }, + "tags": [ + "Vector Stores" + ] + } + } + }, + "security": [ + { + "ApiKeyAuth": [] + }, + { + "ApiKeyAuth_": [] + }, + { + "OAuth2Auth": [ + "https://cognitiveservices.azure.com/.default" + ] + } + ], + "components": { + "schemas": { + "AzureAIFoundryModelsApiVersion": { + "type": "string", + "enum": [ + "v1", + "preview" + ] + }, + "AzureChatCompletionResponseMessage": { + "type": "object", + "required": [ + "content", + "refusal", + "role" + ], + "properties": { + "content": { + "type": "string", + "nullable": true, + "description": "The contents of the message." + }, + "refusal": { + "type": "string", + "nullable": true, + "description": "The refusal message generated by the model." + }, + "tool_calls": { + "$ref": "#/components/schemas/ChatCompletionMessageToolCallsItem", + "readOnly": true + }, + "annotations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "url_citation" + ], + "description": "The type of the URL citation. Always `url_citation`." + }, + "url_citation": { + "type": "object", + "properties": { + "end_index": { + "type": "integer", + "format": "int32", + "description": "The index of the last character of the URL citation in the message." + }, + "start_index": { + "type": "integer", + "format": "int32", + "description": "The index of the first character of the URL citation in the message." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The URL of the web resource." + }, + "title": { + "type": "string", + "description": "The title of the web resource." + } + }, + "required": [ + "end_index", + "start_index", + "url", + "title" + ], + "description": "A URL citation when using web search." + } + }, + "required": [ + "type", + "url_citation" + ] + }, + "description": "Annotations for the message, when applicable, as when using the\n[web search tool](/docs/guides/tools-web-search?api-mode=chat)." + }, + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the author of this message." + }, + "function_call": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "arguments": { + "type": "string" + } + }, + "required": [ + "name", + "arguments" + ], + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "deprecated": true + }, + "audio": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for this audio response." + }, + "expires_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when this audio response will\nno longer be accessible on the server for use in multi-turn\nconversations." + }, + "data": { + "type": "string", + "format": "base64", + "description": "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request." + }, + "transcript": { + "type": "string", + "description": "Transcript of the audio generated by the model." + } + }, + "required": [ + "id", + "expires_at", + "data", + "transcript" + ], + "nullable": true, + "description": "If the audio output modality is requested, this object contains data\nabout the audio response from the model. [Learn more](/docs/guides/audio)." + }, + "context": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatMessageContext" + } + ], + "description": "The Azure-specific context information associated with the chat completion response message." + }, + "reasoning_content": { + "type": "string", + "description": "An Azure-specific extension property containing generated reasoning content from supported models." + } + }, + "description": "The extended response model component for chat completion response messages on the Azure OpenAI service.\nThis model adds support for chat message context, used by the On Your Data feature for intent, citations, and other\ninformation related to retrieval-augmented generation performed." + }, + "AzureChatCompletionStreamResponseDelta": { + "type": "object", + "properties": { + "audio": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionMessageAudioChunk" + } + ], + "description": "Response audio associated with the streaming chat delta payload." + }, + "content": { + "type": "string", + "nullable": true, + "description": "The contents of the chunk message." + }, + "function_call": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "arguments": { + "type": "string" + } + }, + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "deprecated": true + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionMessageToolCallChunk" + }, + "readOnly": true + }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRole" + } + ], + "description": "The role of the author of this message." + }, + "refusal": { + "type": "string", + "nullable": true, + "description": "The refusal message generated by the model." + }, + "context": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatMessageContext" + } + ], + "description": "The Azure-specific context information associated with the chat completion response message." + }, + "reasoning_content": { + "type": "string", + "description": "An Azure-specific extension property containing generated reasoning content from supported models." + } + }, + "description": "The extended response model for a streaming chat response message on the Azure OpenAI service.\nThis model adds support for chat message context, used by the On Your Data feature for intent, citations, and other\ninformation related to retrieval-augmented generation performed." + }, + "AzureChatDataSource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceType" + } + ], + "description": "The differentiating type identifier for the data source." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "azure_search": "#/components/schemas/AzureSearchChatDataSource", + "azure_cosmos_db": "#/components/schemas/AzureCosmosDBChatDataSource", + "elasticsearch": "#/components/schemas/ElasticsearchChatDataSource", + "pinecone": "#/components/schemas/PineconeChatDataSource", + "mongo_db": "#/components/schemas/MongoDBChatDataSource" + } + }, + "description": "A representation of configuration data for a single Azure OpenAI chat data source.\nThis will be used by a chat completions request that should use Azure OpenAI chat extensions to augment the\nresponse behavior.\nThe use of this configuration is compatible only with Azure OpenAI." + }, + "AzureChatDataSourceAccessTokenAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "access_token" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "access_token" + ] + }, + "access_token": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceApiKeyAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "key" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "api_key" + ] + }, + "key": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceAuthenticationOptions": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptionsType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "system_assigned_managed_identity": "#/components/schemas/AzureChatDataSourceSystemAssignedManagedIdentityAuthenticationOptions", + "user_assigned_managed_identity": "#/components/schemas/AzureChatDataSourceUserAssignedManagedIdentityAuthenticationOptions", + "access_token": "#/components/schemas/AzureChatDataSourceAccessTokenAuthenticationOptions", + "connection_string": "#/components/schemas/AzureChatDataSourceConnectionStringAuthenticationOptions", + "key_and_key_id": "#/components/schemas/AzureChatDataSourceKeyAndKeyIdAuthenticationOptions", + "encoded_api_key": "#/components/schemas/AzureChatDataSourceEncodedApiKeyAuthenticationOptions", + "username_and_password": "#/components/schemas/AzureChatDataSourceUsernameAndPasswordAuthenticationOptions" + } + } + }, + "AzureChatDataSourceAuthenticationOptionsType": { + "type": "string", + "enum": [ + "api_key", + "username_and_password", + "connection_string", + "key_and_key_id", + "encoded_api_key", + "access_token", + "system_assigned_managed_identity", + "user_assigned_managed_identity" + ] + }, + "AzureChatDataSourceConnectionStringAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "connection_string" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "connection_string" + ] + }, + "connection_string": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceDeploymentNameVectorizationSource": { + "type": "object", + "required": [ + "type", + "deployment_name" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "deployment_name" + ], + "description": "The type identifier, always 'deployment_name' for this vectorization source type." + }, + "deployment_name": { + "type": "string", + "description": "The embedding model deployment to use for vectorization. This deployment must exist within the same Azure OpenAI\nresource as the model deployment being used for chat completions." + }, + "dimensions": { + "type": "integer", + "format": "int32", + "description": "The number of dimensions to request on embeddings.\nOnly supported in 'text-embedding-3' and later models." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + ], + "description": "Represents a vectorization source that makes internal service calls against an Azure OpenAI embedding model\ndeployment. In contrast with the endpoint-based vectorization source, a deployment-name-based vectorization source\nmust be part of the same Azure OpenAI resource but can be used even in private networks." + }, + "AzureChatDataSourceEncodedApiKeyAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "encoded_api_key" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "encoded_api_key" + ] + }, + "encoded_api_key": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceEndpointVectorizationSource": { + "type": "object", + "required": [ + "type", + "endpoint", + "authentication" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "endpoint" + ], + "description": "The type identifier, always 'endpoint' for this vectorization source type." + }, + "endpoint": { + "type": "string", + "format": "uri", + "description": "Specifies the resource endpoint URL from which embeddings should be retrieved.\nIt should be in the format of:\nhttps://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/embeddings.\nThe api-version query parameter is not allowed." + }, + "authentication": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceApiKeyAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceAccessTokenAuthenticationOptions" + } + ], + "description": "The authentication mechanism to use with the endpoint-based vectorization source.\nEndpoint authentication supports API key and access token mechanisms." + }, + "dimensions": { + "type": "integer", + "format": "int32", + "description": "The number of dimensions to request on embeddings.\nOnly supported in 'text-embedding-3' and later models." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + ], + "description": "Represents a vectorization source that makes public service calls against an Azure OpenAI embedding model deployment." + }, + "AzureChatDataSourceIntegratedVectorizationSource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "integrated" + ], + "description": "The type identifier, always 'integrated' for this vectorization source type." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + ], + "description": "Represents an integrated vectorization source as defined within the supporting search resource." + }, + "AzureChatDataSourceKeyAndKeyIdAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "key", + "key_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "key_and_key_id" + ] + }, + "key": { + "type": "string" + }, + "key_id": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceModelIdVectorizationSource": { + "type": "object", + "required": [ + "type", + "model_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "model_id" + ], + "description": "The type identifier, always 'model_id' for this vectorization source type." + }, + "model_id": { + "type": "string", + "description": "The embedding model build ID to use for vectorization." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + ], + "description": "Represents a vectorization source that makes service calls based on a search service model ID.\nThis source type is currently only supported by Elasticsearch." + }, + "AzureChatDataSourceSystemAssignedManagedIdentityAuthenticationOptions": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "system_assigned_managed_identity" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceType": { + "type": "string", + "enum": [ + "azure_search", + "azure_cosmos_db", + "elasticsearch", + "pinecone", + "mongo_db" + ] + }, + "AzureChatDataSourceUserAssignedManagedIdentityAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "managed_identity_resource_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "user_assigned_managed_identity" + ] + }, + "managed_identity_resource_id": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceUsernameAndPasswordAuthenticationOptions": { + "type": "object", + "required": [ + "type", + "username", + "password" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "username_and_password" + ] + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceAuthenticationOptions" + } + ] + }, + "AzureChatDataSourceVectorizationSource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSourceType" + } + ], + "description": "The differentiating identifier for the concrete vectorization source." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "deployment_name": "#/components/schemas/AzureChatDataSourceDeploymentNameVectorizationSource", + "integrated": "#/components/schemas/AzureChatDataSourceIntegratedVectorizationSource", + "model_id": "#/components/schemas/AzureChatDataSourceModelIdVectorizationSource" + } + }, + "description": "A representation of a data vectorization source usable as an embedding resource with a data source." + }, + "AzureChatDataSourceVectorizationSourceType": { + "type": "string", + "enum": [ + "endpoint", + "deployment_name", + "model_id", + "integrated" + ] + }, + "AzureChatMessageContext": { + "type": "object", + "properties": { + "intent": { + "type": "string", + "description": "The detected intent from the chat history, which is used to carry conversation context between interactions" + }, + "citations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "The content of the citation." + }, + "title": { + "type": "string", + "description": "The title for the citation." + }, + "url": { + "type": "string", + "description": "The URL of the citation." + }, + "filepath": { + "type": "string", + "description": "The file path for the citation." + }, + "chunk_id": { + "type": "string", + "description": "The chunk ID for the citation." + }, + "rerank_score": { + "type": "number", + "format": "double", + "description": "The rerank score for the retrieval." + } + }, + "required": [ + "content" + ] + }, + "description": "The citations produced by the data retrieval." + }, + "all_retrieved_documents": { + "type": "object", + "properties": { + "content": { + "type": "string", + "description": "The content of the citation." + }, + "title": { + "type": "string", + "description": "The title for the citation." + }, + "url": { + "type": "string", + "description": "The URL of the citation." + }, + "filepath": { + "type": "string", + "description": "The file path for the citation." + }, + "chunk_id": { + "type": "string", + "description": "The chunk ID for the citation." + }, + "rerank_score": { + "type": "number", + "format": "double", + "description": "The rerank score for the retrieval." + }, + "search_queries": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The search queries executed to retrieve documents." + }, + "data_source_index": { + "type": "integer", + "format": "int32", + "description": "The index of the data source used for retrieval." + }, + "original_search_score": { + "type": "number", + "format": "double", + "description": "The original search score for the retrieval." + }, + "filter_reason": { + "type": "string", + "enum": [ + "score", + "rerank" + ], + "description": "If applicable, an indication of why the document was filtered." + } + }, + "required": [ + "content", + "search_queries", + "data_source_index" + ], + "description": "Summary information about documents retrieved by the data retrieval operation." + } + }, + "description": "An additional property, added to chat completion response messages, produced by the Azure OpenAI service when using\nextension behavior. This includes intent and citation information from the On Your Data feature." + }, + "AzureContentFilterBlocklistResult": { + "type": "object", + "required": [ + "filtered" + ], + "properties": { + "filtered": { + "type": "boolean", + "description": "A value indicating whether any of the detailed blocklists resulted in a filtering action." + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "filtered": { + "type": "boolean", + "description": "A value indicating whether the blocklist produced a filtering action." + }, + "id": { + "type": "string", + "description": "The ID of the custom blocklist evaluated." + } + }, + "required": [ + "filtered", + "id" + ] + }, + "description": "The pairs of individual blocklist IDs and whether they resulted in a filtering action." + } + }, + "description": "A collection of true/false filtering results for configured custom blocklists." + }, + "AzureContentFilterCompletionTextSpan": { + "type": "object", + "required": [ + "completion_start_offset", + "completion_end_offset" + ], + "properties": { + "completion_start_offset": { + "type": "integer", + "format": "int32", + "description": "Offset of the UTF32 code point which begins the span." + }, + "completion_end_offset": { + "type": "integer", + "format": "int32", + "description": "Offset of the first UTF32 code point which is excluded from the span. This field is always equal to completion_start_offset for empty spans. This field is always larger than completion_start_offset for non-empty spans." + } + }, + "description": "A representation of a span of completion text as used by Azure OpenAI content filter results." + }, + "AzureContentFilterCompletionTextSpanDetectionResult": { + "type": "object", + "required": [ + "filtered", + "detected", + "details" + ], + "properties": { + "filtered": { + "type": "boolean", + "description": "Whether the content detection resulted in a content filtering action." + }, + "detected": { + "type": "boolean", + "description": "Whether the labeled content category was detected in the content." + }, + "details": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AzureContentFilterCompletionTextSpan" + }, + "description": "Detailed information about the detected completion text spans." + } + } + }, + "AzureContentFilterCustomTopicResult": { + "type": "object", + "required": [ + "filtered" + ], + "properties": { + "filtered": { + "type": "boolean", + "description": "A value indicating whether any of the detailed topics resulted in a filtering action." + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "detected": { + "type": "boolean", + "description": "A value indicating whether the topic is detected." + }, + "id": { + "type": "string", + "description": "The ID of the custom topic evaluated." + } + }, + "required": [ + "detected", + "id" + ] + }, + "description": "The pairs of individual topic IDs and whether they are detected." + } + }, + "description": "A collection of true/false filtering results for configured custom topics." + }, + "AzureContentFilterDetectionResult": { + "type": "object", + "required": [ + "filtered", + "detected" + ], + "properties": { + "filtered": { + "type": "boolean", + "description": "Whether the content detection resulted in a content filtering action." + }, + "detected": { + "type": "boolean", + "description": "Whether the labeled content category was detected in the content." + } + }, + "description": "A labeled content filter result item that indicates whether the content was detected and whether the content was\nfiltered." + }, + "AzureContentFilterPersonallyIdentifiableInformationResult": { + "type": "object", + "properties": { + "redacted_text": { + "type": "string", + "description": "The redacted text with PII information removed or masked." + }, + "sub_categories": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AzurePiiSubCategoryResult" + }, + "description": "Detailed results for individual PIIHarmSubCategory(s)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A content filter detection result for Personally Identifiable Information that includes harm extensions." + }, + "AzureContentFilterResultForChoice": { + "type": "object", + "properties": { + "sexual": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category for language related to anatomical organs and genitals, romantic relationships, acts\nportrayed in erotic or affectionate terms, pregnancy, physical sexual acts, including those portrayed as an\nassault or a forced sexual violent act against one's will, prostitution, pornography, and abuse." + }, + "hate": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category that can refer to any content that attacks or uses pejorative or discriminatory\nlanguage with reference to a person or identity group based on certain differentiating attributes of these groups\nincluding but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation,\nreligion, immigration status, ability status, personal appearance, and body size." + }, + "violence": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category for language related to physical actions intended to hurt, injure, damage, or kill\nsomeone or something; describes weapons, guns and related entities, such as manufactures, associations,\nlegislation, and so on." + }, + "self_harm": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category that describes language related to physical actions intended to purposely hurt, injure,\ndamage one's body or kill oneself." + }, + "profanity": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A detection result that identifies whether crude, vulgar, or otherwise objection language is present in the\ncontent." + }, + "custom_blocklists": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterBlocklistResult" + } + ], + "description": "A collection of binary filtering outcomes for configured custom blocklists." + }, + "custom_topics": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterCustomTopicResult" + } + ], + "description": "A collection of binary filtering outcomes for configured custom topics." + }, + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "description": "A distinct, machine-readable code associated with the error." + }, + "message": { + "type": "string", + "description": "A human-readable message associated with the error." + } + }, + "required": [ + "code", + "message" + ], + "description": "If present, details about an error that prevented content filtering from completing its evaluation." + }, + "protected_material_text": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A detection result that describes a match against text protected under copyright or other status." + }, + "protected_material_code": { + "type": "object", + "properties": { + "filtered": { + "type": "boolean", + "description": "Whether the content detection resulted in a content filtering action." + }, + "detected": { + "type": "boolean", + "description": "Whether the labeled content category was detected in the content." + }, + "citation": { + "type": "object", + "properties": { + "license": { + "type": "string", + "description": "The name or identifier of the license associated with the detection." + }, + "URL": { + "type": "string", + "format": "uri", + "description": "The URL associated with the license." + } + }, + "description": "If available, the citation details describing the associated license and its location." + } + }, + "required": [ + "filtered", + "detected" + ], + "description": "A detection result that describes a match against licensed code or other protected source material." + }, + "ungrounded_material": { + "$ref": "#/components/schemas/AzureContentFilterCompletionTextSpanDetectionResult" + }, + "personally_identifiable_information": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterPersonallyIdentifiableInformationResult" + } + ], + "description": "A detection result that describes matches against Personal Identifiable Information with configurable subcategories." + } + }, + "description": "A content filter result for a single response item produced by a generative AI system." + }, + "AzureContentFilterResultForPrompt": { + "type": "object", + "properties": { + "prompt_index": { + "type": "integer", + "format": "int32", + "description": "The index of the input prompt associated with the accompanying content filter result categories." + }, + "content_filter_results": { + "type": "object", + "properties": { + "sexual": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category for language related to anatomical organs and genitals, romantic relationships, acts\nportrayed in erotic or affectionate terms, pregnancy, physical sexual acts, including those portrayed as an\nassault or a forced sexual violent act against one's will, prostitution, pornography, and abuse." + }, + "hate": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category that can refer to any content that attacks or uses pejorative or discriminatory\nlanguage with reference to a person or identity group based on certain differentiating attributes of these groups\nincluding but not limited to race, ethnicity, nationality, gender identity and expression, sexual orientation,\nreligion, immigration status, ability status, personal appearance, and body size." + }, + "violence": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category for language related to physical actions intended to hurt, injure, damage, or kill\nsomeone or something; describes weapons, guns and related entities, such as manufactures, associations,\nlegislation, and so on." + }, + "self_harm": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterSeverityResult" + } + ], + "description": "A content filter category that describes language related to physical actions intended to purposely hurt, injure,\ndamage one's body or kill oneself." + }, + "profanity": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A detection result that identifies whether crude, vulgar, or otherwise objection language is present in the\ncontent." + }, + "custom_blocklists": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterBlocklistResult" + } + ], + "description": "A collection of binary filtering outcomes for configured custom blocklists." + }, + "custom_topics": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterCustomTopicResult" + } + ], + "description": "A collection of binary filtering outcomes for configured custom topics." + }, + "error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32", + "description": "A distinct, machine-readable code associated with the error." + }, + "message": { + "type": "string", + "description": "A human-readable message associated with the error." + } + }, + "required": [ + "code", + "message" + ], + "description": "If present, details about an error that prevented content filtering from completing its evaluation." + }, + "jailbreak": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A detection result that describes user prompt injection attacks, where malicious users deliberately exploit\nsystem vulnerabilities to elicit unauthorized behavior from the LLM. This could lead to inappropriate content\ngeneration or violations of system-imposed restrictions." + }, + "indirect_attack": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterDetectionResult" + } + ], + "description": "A detection result that describes attacks on systems powered by Generative AI models that can happen every time\nan application processes information that wasn’t directly authored by either the developer of the application or\nthe user." + } + }, + "required": [ + "jailbreak", + "indirect_attack" + ], + "description": "The content filter category details for the result." + } + }, + "description": "A content filter result associated with a single input prompt item into a generative AI system." + }, + "AzureContentFilterSeverityResult": { + "type": "object", + "required": [ + "filtered", + "severity" + ], + "properties": { + "filtered": { + "type": "boolean", + "description": "Whether the content severity resulted in a content filtering action." + }, + "severity": { + "type": "string", + "enum": [ + "safe", + "low", + "medium", + "high" + ], + "description": "The labeled severity of the content." + } + }, + "description": "A labeled content filter result item that indicates whether the content was filtered and what the qualitative\nseverity level of the content was, as evaluated against content filter configuration for the category." + }, + "AzureCosmosDBChatDataSource": { + "type": "object", + "required": [ + "type", + "parameters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "azure_cosmos_db" + ], + "description": "The discriminated type identifier, which is always 'azure_cosmos_db'." + }, + "parameters": { + "type": "object", + "properties": { + "top_n_documents": { + "type": "integer", + "format": "int32", + "description": "The configured number of documents to feature in the query." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of the indexed data." + }, + "strictness": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 5, + "description": "The configured strictness of the search relevance filtering.\nHigher strictness will increase precision but lower recall of the answer." + }, + "max_search_queries": { + "type": "integer", + "format": "int32", + "description": "The maximum number of rewritten queries that should be sent to the search provider for a single user message.\nBy default, the system will make an automatic determination." + }, + "allow_partial_result": { + "type": "boolean", + "description": "If set to true, the system will allow partial search results to be used and the request will fail if all\npartial queries fail. If not specified or specified as false, the request will fail if any search query fails.", + "default": false + }, + "include_contexts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ] + }, + "maxItems": 3, + "description": "The output context properties to include on the response.\nBy default, citations and intent will be requested.", + "default": [ + "citations", + "intent" + ] + }, + "container_name": { + "type": "string" + }, + "database_name": { + "type": "string" + }, + "embedding_dependency": { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + }, + "index_name": { + "type": "string" + }, + "authentication": { + "$ref": "#/components/schemas/AzureChatDataSourceConnectionStringAuthenticationOptions" + }, + "fields_mapping": { + "type": "object", + "properties": { + "content_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "vector_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "title_field": { + "type": "string" + }, + "url_field": { + "type": "string" + }, + "filepath_field": { + "type": "string" + }, + "content_fields_separator": { + "type": "string" + } + }, + "required": [ + "content_fields", + "vector_fields" + ] + } + }, + "required": [ + "container_name", + "database_name", + "embedding_dependency", + "index_name", + "authentication", + "fields_mapping" + ], + "description": "The parameter information to control the use of the Azure CosmosDB data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSource" + } + ], + "description": "Represents a data source configuration that will use an Azure CosmosDB resource." + }, + "AzureCreateChatCompletionRequest": { + "type": "object", + "required": [ + "messages", + "model" + ], + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "temperature": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 2, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.", + "default": 1 + }, + "top_p": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 1, + "description": "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.", + "default": 1 + }, + "user": { + "type": "string", + "description": "A unique identifier representing your end-user, which can help to\nmonitor and detect abuse." + }, + "top_logprobs": { + "type": "integer", + "format": "int32", + "minimum": 0, + "maximum": 20, + "description": "An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability." + }, + "messages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + }, + "minItems": 1, + "description": "A list of messages comprising the conversation so far. Depending on the\nmodel you use, different message types (modalities) are supported,\nlike text, images, and audio." + }, + "modalities": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/ResponseModalities" + } + ], + "nullable": true + }, + "reasoning_effort": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningEffort" + } + ], + "nullable": true, + "default": "medium" + }, + "max_completion_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An upper bound for the number of tokens that can be generated for a\ncompletion, including visible output tokens and reasoning tokens." + }, + "frequency_penalty": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": -2, + "maximum": 2, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on\ntheir existing frequency in the text so far, decreasing the model's\nlikelihood to repeat the same line verbatim.", + "default": 0 + }, + "presence_penalty": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": -2, + "maximum": 2, + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on\nwhether they appear in the text so far, increasing the model's likelihood\nto talk about new topics.", + "default": 0 + }, + "response_format": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseFormat" + } + ], + "description": "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables\nStructured Outputs which ensures the model will match your supplied JSON\nschema.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures\nthe message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model\nto produce JSON yourself via a system or user message. Without this, the\nmodel may generate an unending stream of whitespace until the generation\nreaches the token limit, resulting in a long-running and seemingly \"stuck\"\nrequest. Also note that the message content may be partially cut off if\n`finish_reason=\"length\"`, which indicates the generation exceeded\n`max_tokens` or the conversation exceeded the max context length." + }, + "audio": { + "type": "object", + "properties": { + "voice": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VoiceIdsShared" + } + ], + "description": "The voice the model uses to respond. Supported voices are\n`alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, and `shimmer`." + }, + "format": { + "type": "string", + "enum": [ + "wav", + "aac", + "mp3", + "flac", + "opus", + "pcm16" + ], + "description": "Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,\n`opus`, or `pcm16`." + } + }, + "required": [ + "voice", + "format" + ], + "nullable": true, + "description": "Parameters for audio output. Required when audio output is requested with\n`modalities: [\"audio\"]`." + }, + "store": { + "type": "boolean", + "nullable": true, + "description": "Whether or not to store the output of this chat completion request for\nuse in model distillation or evals products.", + "default": false + }, + "stream": { + "type": "boolean", + "nullable": true, + "description": "If set to true, the model response data will be streamed to the client\nas it is generated using server-sent events.", + "default": false + }, + "stop": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.StopConfiguration" + } + ], + "nullable": true, + "default": null + }, + "logit_bias": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + }, + "nullable": true, + "description": "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the\ntokenizer) to an associated bias value from -100 to 100. Mathematically,\nthe bias is added to the logits generated by the model prior to sampling.\nThe exact effect will vary per model, but values between -1 and 1 should\ndecrease or increase likelihood of selection; values like -100 or 100\nshould result in a ban or exclusive selection of the relevant token.", + "x-oaiTypeLabel": "map", + "default": null + }, + "logprobs": { + "type": "boolean", + "nullable": true, + "description": "Whether to return log probabilities of the output tokens or not. If true,\nreturns the log probabilities of each output token returned in the\n`content` of `message`.", + "default": false + }, + "max_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "The maximum number of tokens that can be generated in the chat completion.\nThis value can be used to control costs for text generated via API.\n\nThis value is now deprecated in favor of `max_completion_tokens`, and is\nnot compatible with o1 series models.", + "deprecated": true + }, + "n": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 1, + "maximum": 128, + "description": "How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs.", + "default": 1 + }, + "prediction": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatOutputPrediction" + } + ], + "nullable": true, + "description": "Configuration for a predicted output, which can greatly improve\nresponse times when large parts of the model response are known\nahead of time. This is most common when you are regenerating a\nfile with only minor changes to most of the content." + }, + "seed": { + "type": "integer", + "format": "int64", + "nullable": true, + "minimum": -9223372036854775808, + "maximum": 9223372036854775807, + "description": "This feature is in Beta.\nIf specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend." + }, + "stream_options": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionStreamOptions" + } + ], + "nullable": true, + "default": null + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTool" + }, + "description": "A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported." + }, + "tool_choice": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionToolChoiceOption" + }, + "parallel_tool_calls": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ParallelToolCalls" + } + ], + "default": true + }, + "function_call": { + "anyOf": [ + { + "type": "string", + "enum": [ + "none", + "auto" + ] + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionFunctionCallOption" + } + ], + "description": "Deprecated in favor of `tool_choice`.\n\nControls which (if any) function is called by the model.\n\n`none` means the model will not call a function and instead generates a\nmessage.\n\n`auto` means the model can pick between generating a message or calling a\nfunction.\n\nSpecifying a particular function via `{\"name\": \"my_function\"}` forces the\nmodel to call that function.\n\n`none` is the default when no functions are present. `auto` is the default\nif functions are present.", + "deprecated": true + }, + "functions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionFunctions" + }, + "minItems": 1, + "maxItems": 128, + "description": "Deprecated in favor of `tools`.\n\nA list of functions the model may generate JSON inputs for.", + "deprecated": true + }, + "model": { + "type": "string", + "description": "The model deployment identifier to use for the chat completion request." + }, + "data_sources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AzureChatDataSource" + }, + "description": "The data sources to use for the On Your Data feature, exclusive to Azure OpenAI." + }, + "user_security_context": { + "$ref": "#/components/schemas/AzureUserSecurityContext" + } + }, + "description": "The extended request model for chat completions against the Azure OpenAI service.\nThis adds the ability to provide data sources for the On Your Data feature." + }, + "AzureCreateChatCompletionResponse": { + "type": "object", + "required": [ + "id", + "created", + "model", + "object", + "choices" + ], + "properties": { + "id": { + "type": "string", + "description": "A unique identifier for the chat completion." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) of when the chat completion was created." + }, + "model": { + "type": "string", + "description": "The model used for the chat completion." + }, + "system_fingerprint": { + "type": "string", + "description": "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism." + }, + "object": { + "type": "string", + "enum": [ + "chat.completion" + ], + "description": "The object type, which is always `chat.completion`." + }, + "usage": { + "$ref": "#/components/schemas/OpenAI.CompletionUsage" + }, + "choices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "finish_reason": { + "type": "string", + "enum": [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call" + ], + "description": "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function." + }, + "index": { + "type": "integer", + "format": "int32", + "description": "The index of the choice in the list of choices." + }, + "logprobs": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTokenLogprob" + }, + "nullable": true, + "description": "A list of message content tokens with log probability information.", + "readOnly": true + }, + "refusal": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTokenLogprob" + }, + "nullable": true, + "description": "A list of message refusal tokens with log probability information.", + "readOnly": true + } + }, + "required": [ + "content", + "refusal" + ], + "nullable": true, + "description": "Log probability information for the choice." + }, + "content_filter_results": { + "$ref": "#/components/schemas/AzureContentFilterResultForChoice" + }, + "message": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatCompletionResponseMessage" + } + ], + "description": "The chat completion response message." + } + }, + "required": [ + "finish_reason", + "index", + "logprobs", + "message" + ] + } + }, + "prompt_filter_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "prompt_index": { + "type": "integer", + "format": "int32", + "description": "The index of the input prompt that this content filter result corresponds to." + }, + "content_filter_results": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureContentFilterResultForPrompt" + } + ], + "description": "The content filter results associated with the indexed input prompt." + } + }, + "required": [ + "prompt_index", + "content_filter_results" + ] + }, + "description": "The Responsible AI content filter annotations associated with prompt inputs into chat completions." + } + }, + "description": "The extended top-level chat completion response model for the Azure OpenAI service.\nThis model adds Responsible AI content filter annotations for prompt input." + }, + "AzureCreateChatCompletionStreamResponse": { + "type": "object", + "required": [ + "id", + "choices", + "created", + "model", + "object" + ], + "properties": { + "id": { + "type": "string", + "description": "A unique identifier for the chat completion. Each chunk has the same ID." + }, + "choices": { + "type": "array", + "items": { + "type": "object", + "properties": { + "delta": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionStreamResponseDelta" + }, + "logprobs": { + "type": "object", + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTokenLogprob" + }, + "nullable": true, + "description": "A list of message content tokens with log probability information.", + "readOnly": true + }, + "refusal": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTokenLogprob" + }, + "nullable": true, + "description": "A list of message refusal tokens with log probability information.", + "readOnly": true + } + }, + "required": [ + "content", + "refusal" + ], + "nullable": true, + "description": "Log probability information for the choice." + }, + "finish_reason": { + "type": "string", + "enum": [ + "stop", + "length", + "tool_calls", + "content_filter", + "function_call" + ], + "nullable": true, + "description": "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function." + }, + "index": { + "type": "integer", + "format": "int32", + "description": "The index of the choice in the list of choices." + } + }, + "required": [ + "delta", + "finish_reason", + "index" + ] + }, + "description": "A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the\nlast chunk if you set `stream_options: {\"include_usage\": true}`." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) of when the chat completion was created. Each chunk has the same timestamp." + }, + "model": { + "type": "string", + "description": "The model to generate the completion." + }, + "system_fingerprint": { + "type": "string", + "description": "This fingerprint represents the backend configuration that the model runs with.\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism." + }, + "object": { + "type": "string", + "enum": [ + "chat.completion.chunk" + ], + "description": "The object type, which is always `chat.completion.chunk`." + }, + "usage": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.CompletionUsage" + } + ], + "nullable": true, + "description": "An optional field that will only be present when you set\n`stream_options: {\"include_usage\": true}` in your request. When present, it\ncontains a null value **except for the last chunk** which contains the\ntoken usage statistics for the entire request.\n\n**NOTE:** If the stream is interrupted or cancelled, you may not\nreceive the final usage chunk which contains the total token usage for\nthe request." + }, + "delta": { + "$ref": "#/components/schemas/AzureChatCompletionStreamResponseDelta" + }, + "content_filter_results": { + "$ref": "#/components/schemas/AzureContentFilterResultForChoice" + } + } + }, + "AzureCreateEmbeddingRequest": { + "type": "object", + "required": [ + "model", + "input" + ], + "properties": { + "model": { + "type": "string", + "description": "The model to use for the embedding request." + }, + "input": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + } + ], + "description": "Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for all embedding models), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. In addition to the per-input token limit, all embedding models enforce a maximum of 300,000 tokens summed across all inputs in a single request." + }, + "encoding_format": { + "type": "string", + "enum": [ + "float", + "base64" + ], + "description": "The format to return the embeddings in. Can be either `float` or [`base64`](https://pypi.org/project/pybase64/).", + "default": "float" + }, + "dimensions": { + "type": "integer", + "format": "int32", + "minimum": 1, + "description": "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models." + }, + "user": { + "type": "string", + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids)." + } + } + }, + "AzureCreateFileRequestMultiPart": { + "type": "object", + "required": [ + "file", + "expires_after", + "purpose" + ], + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "expires_after": { + "type": "object", + "properties": { + "seconds": { + "type": "integer", + "format": "int32" + }, + "anchor": { + "$ref": "#/components/schemas/AzureFileExpiryAnchor" + } + }, + "required": [ + "seconds", + "anchor" + ] + }, + "purpose": { + "type": "string", + "enum": [ + "assistants", + "batch", + "fine-tune", + "evals" + ], + "description": "The intended purpose of the uploaded file. One of: - `assistants`: Used in the Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for fine-tuning - `evals`: Used for eval data sets" + } + } + }, + "AzureCreateResponse": { + "type": "object", + "required": [ + "model" + ], + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "temperature": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 2, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.", + "default": 1 + }, + "top_p": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 1, + "description": "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.", + "default": 1 + }, + "user": { + "type": "string", + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids)." + }, + "top_logprobs": { + "type": "integer", + "format": "int32", + "minimum": 0, + "maximum": 20, + "description": "An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability." + }, + "previous_response_id": { + "type": "string", + "nullable": true, + "description": "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state)." + }, + "reasoning": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Reasoning" + } + ], + "nullable": true + }, + "background": { + "type": "boolean", + "nullable": true, + "description": "Whether to run the model response in the background.\n[Learn more](/docs/guides/background).", + "default": false + }, + "max_output_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning)." + }, + "max_tool_calls": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored." + }, + "text": { + "type": "object", + "properties": { + "format": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + }, + "description": "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)" + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Tool" + }, + "description": "An array of tools the model may call while generating a response. You \ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like file search.\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code." + }, + "tool_choice": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceOptions" + }, + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call." + }, + "prompt": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Prompt" + } + ], + "nullable": true + }, + "truncation": { + "type": "string", + "enum": [ + "auto", + "disabled" + ], + "nullable": true, + "description": "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.", + "default": "disabled" + }, + "input": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ImplicitUserMessage" + }, + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ] + } + } + ], + "description": "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Image inputs](/docs/guides/images)\n- [File inputs](/docs/guides/pdf-files)\n- [Conversation state](/docs/guides/conversation-state)\n- [Function calling](/docs/guides/function-calling)" + }, + "include": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Includable" + }, + "nullable": true, + "description": "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution\n in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning\n tokens in reasoning item outputs. This enables reasoning items to be used in\n multi-turn conversations when using the Responses API statelessly (like\n when the `store` parameter is set to `false`, or when an organization is\n enrolled in the zero data retention program)." + }, + "parallel_tool_calls": { + "type": "boolean", + "nullable": true, + "description": "Whether to allow the model to run tool calls in parallel.", + "default": true + }, + "store": { + "type": "boolean", + "nullable": true, + "description": "Whether to store the generated model response for later retrieval via\nAPI.", + "default": true + }, + "instructions": { + "type": "string", + "nullable": true, + "description": "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses." + }, + "stream": { + "type": "boolean", + "nullable": true, + "description": "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](/docs/api-reference/responses-streaming)\nfor more information.", + "default": false + }, + "model": { + "type": "string", + "description": "The model deployment to use for the creation of this response." + } + } + }, + "AzureErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "The distinct, machine-generated identifier for the error." + }, + "message": { + "type": "string", + "description": "A human-readable message associated with the error." + }, + "param": { + "type": "string", + "description": "If applicable, the request input parameter associated with the error" + }, + "type": { + "type": "string", + "enum": [ + "error" + ], + "description": "The object type, always 'error.'" + }, + "inner_error": {} + }, + "description": "The error details." + } + } + }, + "AzureEvalAPICompletionsSamplingParams": { + "type": "object", + "properties": { + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionTool" + } + }, + "parallel_tool_calls": { + "type": "boolean" + }, + "response_format": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureEvalAPIModelSamplingParams" + } + ] + }, + "AzureEvalAPIModelSamplingParams": { + "type": "object", + "properties": { + "seed": { + "type": "integer", + "format": "int32", + "description": "A seed value to initialize the randomness during sampling." + }, + "temperature": { + "type": "number", + "format": "float", + "description": "A higher temperature increases randomness in the outputs." + }, + "max_tokens": { + "type": "integer", + "format": "int32", + "description": "The maximum number of tokens in the generated output." + }, + "top_p": { + "type": "number", + "format": "float", + "description": "An alternative to temperature for nucleus sampling; 1.0 includes all tokens." + }, + "reasoning_effort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "description": "Controls the level of reasoning effort applied during generation." + } + } + }, + "AzureEvalAPIResponseSamplingParams": { + "type": "object", + "properties": { + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Tool" + } + }, + "parallel_tool_calls": { + "type": "boolean" + }, + "response_format": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureEvalAPIModelSamplingParams" + } + ] + }, + "AzureFileExpiryAnchor": { + "type": "string", + "enum": [ + "created_at" + ] + }, + "AzureFineTuneReinforcementMethod": { + "type": "object", + "required": [ + "grader" + ], + "properties": { + "grader": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.GraderStringCheck" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderTextSimilarity" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderScoreModel" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderMulti" + } + ] + }, + "response_format": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseFormatJSONSchemaRequest" + } + ], + "description": "Response format to be used while sampling during RFT training" + }, + "hyperparameters": { + "$ref": "#/components/schemas/OpenAI.FineTuneReinforcementHyperparameters" + } + } + }, + "AzureListFilesResponse": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AzureOpenAIFile" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean" + } + } + }, + "AzureOpenAIFile": { + "type": "object", + "required": [ + "id", + "bytes", + "created_at", + "filename", + "object", + "purpose", + "status" + ], + "properties": { + "id": { + "type": "string", + "description": "The file identifier, which can be referenced in the API endpoints." + }, + "bytes": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "The size of the file, in bytes." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the file was created." + }, + "expires_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the file will expire." + }, + "filename": { + "type": "string", + "description": "The name of the file." + }, + "object": { + "type": "string", + "enum": [ + "file" + ], + "description": "The object type, which is always `file`." + }, + "status_details": { + "type": "string", + "description": "Deprecated. For details on why a fine-tuning training file failed validation, see the `error` field on `fine_tuning.job`.", + "deprecated": true + }, + "purpose": { + "type": "string", + "enum": [ + "assistants", + "assistants_output", + "batch", + "batch_output", + "fine-tune", + "fine-tune-results", + "evals" + ], + "description": "The intended purpose of the file. Supported values are `assistants`, `assistants_output`, `batch`, `batch_output`, `fine-tune` and `fine-tune-results`." + }, + "status": { + "type": "string", + "enum": [ + "uploaded", + "pending", + "running", + "processed", + "error", + "deleting", + "deleted" + ] + } + } + }, + "AzurePiiSubCategoryResult": { + "type": "object", + "required": [ + "sub_category", + "filtered", + "detected", + "redacted" + ], + "properties": { + "sub_category": { + "type": "string", + "description": "The PIIHarmSubCategory that was evaluated." + }, + "filtered": { + "type": "boolean", + "description": "Whether the content detection resulted in a content filtering action for this subcategory." + }, + "detected": { + "type": "boolean", + "description": "Whether the labeled content subcategory was detected in the content." + }, + "redacted": { + "type": "boolean", + "description": "Whether the content was redacted for this subcategory." + } + }, + "description": "Result details for individual PIIHarmSubCategory(s)." + }, + "AzureResponse": { + "type": "object", + "required": [ + "metadata", + "temperature", + "top_p", + "user", + "id", + "object", + "created_at", + "error", + "incomplete_details", + "output", + "instructions", + "parallel_tool_calls", + "model" + ], + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "temperature": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 2, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + }, + "top_p": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 1, + "description": "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both." + }, + "user": { + "type": "string", + "nullable": true, + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids)." + }, + "top_logprobs": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability." + }, + "previous_response_id": { + "type": "string", + "nullable": true, + "description": "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state)." + }, + "reasoning": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Reasoning" + } + ], + "nullable": true + }, + "background": { + "type": "boolean", + "nullable": true, + "description": "Whether to run the model response in the background.\n[Learn more](/docs/guides/background).", + "default": false + }, + "max_output_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning)." + }, + "max_tool_calls": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored." + }, + "text": { + "type": "object", + "properties": { + "format": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + }, + "description": "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)" + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Tool" + }, + "description": "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](/docs/guides/tools-web-search)\n or [file search](/docs/guides/tools-file-search). Learn more about\n [built-in tools](/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code. Learn more about\n [function calling](/docs/guides/function-calling)." + }, + "tool_choice": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceOptions" + }, + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call." + }, + "prompt": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Prompt" + } + ], + "nullable": true + }, + "truncation": { + "type": "string", + "enum": [ + "auto", + "disabled" + ], + "nullable": true, + "description": "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.", + "default": "disabled" + }, + "id": { + "type": "string", + "description": "Unique identifier for this Response." + }, + "object": { + "type": "string", + "enum": [ + "response" + ], + "description": "The object type of this resource - always set to `response`." + }, + "status": { + "type": "string", + "enum": [ + "completed", + "failed", + "in_progress", + "cancelled", + "queued", + "incomplete" + ], + "description": "The status of the response generation. One of `completed`, `failed`,\n`in_progress`, `cancelled`, `queued`, or `incomplete`." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) of when this Response was created." + }, + "error": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseError" + } + ], + "nullable": true + }, + "incomplete_details": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "max_output_tokens", + "content_filter" + ], + "description": "The reason why the response is incomplete." + } + }, + "nullable": true, + "description": "Details about why the response is incomplete." + }, + "output": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemResource" + }, + "description": "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs." + }, + "instructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + } + ], + "nullable": true, + "description": "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses." + }, + "output_text": { + "type": "string", + "nullable": true, + "description": "SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs." + }, + "usage": { + "$ref": "#/components/schemas/OpenAI.ResponseUsage" + }, + "parallel_tool_calls": { + "type": "boolean", + "description": "Whether to allow the model to run tool calls in parallel.", + "default": true + }, + "model": { + "type": "string", + "description": "The model used to generate this response." + } + } + }, + "AzureSearchChatDataSource": { + "type": "object", + "required": [ + "type", + "parameters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "azure_search" + ], + "description": "The discriminated type identifier, which is always 'azure_search'." + }, + "parameters": { + "type": "object", + "properties": { + "top_n_documents": { + "type": "integer", + "format": "int32", + "description": "The configured number of documents to feature in the query." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of the indexed data." + }, + "strictness": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 5, + "description": "The configured strictness of the search relevance filtering.\nHigher strictness will increase precision but lower recall of the answer." + }, + "max_search_queries": { + "type": "integer", + "format": "int32", + "description": "The maximum number of rewritten queries that should be sent to the search provider for a single user message.\nBy default, the system will make an automatic determination." + }, + "allow_partial_result": { + "type": "boolean", + "description": "If set to true, the system will allow partial search results to be used and the request will fail if all\npartial queries fail. If not specified or specified as false, the request will fail if any search query fails.", + "default": false + }, + "include_contexts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ] + }, + "maxItems": 3, + "description": "The output context properties to include on the response.\nBy default, citations and intent will be requested.", + "default": [ + "citations", + "intent" + ] + }, + "endpoint": { + "type": "string", + "format": "uri", + "description": "The absolute endpoint path for the Azure Search resource to use." + }, + "index_name": { + "type": "string", + "description": "The name of the index to use, as specified in the Azure Search resource." + }, + "authentication": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceApiKeyAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceSystemAssignedManagedIdentityAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceUserAssignedManagedIdentityAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceAccessTokenAuthenticationOptions" + } + ], + "description": "The authentication mechanism to use with Azure Search." + }, + "fields_mapping": { + "type": "object", + "properties": { + "title_field": { + "type": "string", + "description": "The name of the index field to use as a title." + }, + "url_field": { + "type": "string", + "description": "The name of the index field to use as a URL." + }, + "filepath_field": { + "type": "string", + "description": "The name of the index field to use as a filepath." + }, + "content_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The names of index fields that should be treated as content." + }, + "content_fields_separator": { + "type": "string", + "description": "The separator pattern that content fields should use." + }, + "vector_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The names of fields that represent vector data." + }, + "image_vector_fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The names of fields that represent image vector data." + } + }, + "description": "The field mappings to use with the Azure Search resource." + }, + "query_type": { + "type": "string", + "enum": [ + "simple", + "semantic", + "vector", + "vector_simple_hybrid", + "vector_semantic_hybrid" + ], + "description": "The query type for the Azure Search resource to use." + }, + "semantic_configuration": { + "type": "string", + "description": "Additional semantic configuration for the query." + }, + "filter": { + "type": "string", + "description": "A filter to apply to the search." + }, + "embedding_dependency": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceDeploymentNameVectorizationSource" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceIntegratedVectorizationSource" + } + ], + "description": "The vectorization source to use with Azure Search.\nSupported sources for Azure Search include endpoint, deployment name, and integrated." + } + }, + "required": [ + "endpoint", + "index_name", + "authentication" + ], + "description": "The parameter information to control the use of the Azure Search data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSource" + } + ], + "description": "Represents a data source configuration that will use an Azure Search resource." + }, + "AzureUserSecurityContext": { + "type": "object", + "properties": { + "application_name": { + "type": "string", + "description": "The name of the application. Sensitive personal information should not be included in this field." + }, + "end_user_id": { + "type": "string", + "description": "This identifier is the Microsoft Entra ID (formerly Azure Active Directory) user object ID used to authenticate end-users within the generative AI application. Sensitive personal information should not be included in this field." + }, + "end_user_tenant_id": { + "type": "string", + "description": "The Microsoft 365 tenant ID the end user belongs to. It's required when the generative AI application is multitenant." + }, + "source_ip": { + "type": "string", + "description": "Captures the original client's IP address." + } + }, + "description": "User security context contains several parameters that describe the application itself, and the end user that interacts with the application. These fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. [Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud." + }, + "ChatCompletionMessageToolCallsItem": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionMessageToolCall" + }, + "description": "The tool calls generated by the model, such as function calls." + }, + "CopiedAccountDetails": { + "type": "object", + "required": [ + "destinationResourceId", + "region", + "status" + ], + "properties": { + "destinationResourceId": { + "type": "string", + "description": "The ID of the destination resource where the model was copied to." + }, + "region": { + "type": "string", + "description": "The region where the model was copied to." + }, + "status": { + "type": "string", + "enum": [ + "Completed", + "Failed", + "InProgress" + ], + "description": "The status of the copy operation." + } + } + }, + "CopyModelRequest": { + "type": "object", + "required": [ + "destinationResourceId", + "region" + ], + "properties": { + "destinationResourceId": { + "type": "string", + "description": "The ID of the destination Resource to copy." + }, + "region": { + "type": "string", + "description": "The region to copy the model to." + } + } + }, + "CopyModelResponse": { + "type": "object", + "required": [ + "checkpointedModelName", + "fineTuningJobId", + "copiedAccountDetails" + ], + "properties": { + "checkpointedModelName": { + "type": "string", + "description": "The ID of the copied model." + }, + "fineTuningJobId": { + "type": "string", + "description": "The ID of the fine-tuning job that the checkpoint was copied from." + }, + "copiedAccountDetails": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CopiedAccountDetails" + }, + "description": "The ID of the destination resource id where it was copied" + } + } + }, + "ElasticsearchChatDataSource": { + "type": "object", + "required": [ + "type", + "parameters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "elasticsearch" + ], + "description": "The discriminated type identifier, which is always 'elasticsearch'." + }, + "parameters": { + "type": "object", + "properties": { + "top_n_documents": { + "type": "integer", + "format": "int32", + "description": "The configured number of documents to feature in the query." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of the indexed data." + }, + "strictness": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 5, + "description": "The configured strictness of the search relevance filtering.\nHigher strictness will increase precision but lower recall of the answer." + }, + "max_search_queries": { + "type": "integer", + "format": "int32", + "description": "The maximum number of rewritten queries that should be sent to the search provider for a single user message.\nBy default, the system will make an automatic determination." + }, + "allow_partial_result": { + "type": "boolean", + "description": "If set to true, the system will allow partial search results to be used and the request will fail if all\npartial queries fail. If not specified or specified as false, the request will fail if any search query fails.", + "default": false + }, + "include_contexts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ] + }, + "maxItems": 3, + "description": "The output context properties to include on the response.\nBy default, citations and intent will be requested.", + "default": [ + "citations", + "intent" + ] + }, + "endpoint": { + "type": "string", + "format": "uri" + }, + "index_name": { + "type": "string" + }, + "authentication": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceKeyAndKeyIdAuthenticationOptions" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceEncodedApiKeyAuthenticationOptions" + } + ] + }, + "fields_mapping": { + "type": "object", + "properties": { + "title_field": { + "type": "string" + }, + "url_field": { + "type": "string" + }, + "filepath_field": { + "type": "string" + }, + "content_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "content_fields_separator": { + "type": "string" + }, + "vector_fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "query_type": { + "type": "string", + "enum": [ + "simple", + "vector" + ] + }, + "embedding_dependency": { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + }, + "required": [ + "endpoint", + "index_name", + "authentication" + ], + "description": "The parameter information to control the use of the Elasticsearch data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSource" + } + ] + }, + "MongoDBChatDataSource": { + "type": "object", + "required": [ + "type", + "parameters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mongo_db" + ], + "description": "The discriminated type identifier, which is always 'mongo_db'." + }, + "parameters": { + "type": "object", + "properties": { + "top_n_documents": { + "type": "integer", + "format": "int32", + "description": "The configured number of documents to feature in the query." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of the indexed data." + }, + "strictness": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 5, + "description": "The configured strictness of the search relevance filtering.\nHigher strictness will increase precision but lower recall of the answer." + }, + "max_search_queries": { + "type": "integer", + "format": "int32", + "description": "The maximum number of rewritten queries that should be sent to the search provider for a single user message.\nBy default, the system will make an automatic determination." + }, + "allow_partial_result": { + "type": "boolean", + "description": "If set to true, the system will allow partial search results to be used and the request will fail if all\npartial queries fail. If not specified or specified as false, the request will fail if any search query fails.", + "default": false + }, + "include_contexts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ] + }, + "maxItems": 3, + "description": "The output context properties to include on the response.\nBy default, citations and intent will be requested.", + "default": [ + "citations", + "intent" + ] + }, + "endpoint": { + "type": "string", + "description": "The name of the MongoDB cluster endpoint." + }, + "database_name": { + "type": "string", + "description": "The name of the MongoDB database." + }, + "collection_name": { + "type": "string", + "description": "The name of the MongoDB collection." + }, + "app_name": { + "type": "string", + "description": "The name of the MongoDB application." + }, + "index_name": { + "type": "string", + "description": "The name of the MongoDB index." + }, + "authentication": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceUsernameAndPasswordAuthenticationOptions" + } + ], + "description": "The authentication mechanism to use with Pinecone.\nSupported authentication mechanisms for Pinecone include: username and password." + }, + "embedding_dependency": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceEndpointVectorizationSource" + }, + { + "$ref": "#/components/schemas/AzureChatDataSourceDeploymentNameVectorizationSource" + } + ], + "description": "The vectorization source to use as an embedding dependency for the MongoDB data source.\nSupported vectorization sources for MongoDB include: endpoint, deployment name." + }, + "fields_mapping": { + "type": "object", + "properties": { + "content_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "vector_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "title_field": { + "type": "string" + }, + "url_field": { + "type": "string" + }, + "filepath_field": { + "type": "string" + }, + "content_fields_separator": { + "type": "string" + } + }, + "required": [ + "content_fields", + "vector_fields" + ], + "description": "Field mappings to apply to data used by the MongoDB data source.\nNote that content and vector field mappings are required for MongoDB." + } + }, + "required": [ + "endpoint", + "database_name", + "collection_name", + "app_name", + "index_name", + "authentication", + "embedding_dependency", + "fields_mapping" + ], + "description": "The parameter information to control the use of the MongoDB data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSource" + } + ] + }, + "OpenAI.Annotation": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.AnnotationType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "file_citation": "#/components/schemas/OpenAI.AnnotationFileCitation", + "url_citation": "#/components/schemas/OpenAI.AnnotationUrlCitation", + "file_path": "#/components/schemas/OpenAI.AnnotationFilePath" + } + } + }, + "OpenAI.AnnotationFileCitation": { + "type": "object", + "required": [ + "type", + "file_id", + "index", + "filename" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_citation" + ], + "description": "The type of the file citation. Always `file_citation`." + }, + "file_id": { + "type": "string", + "description": "The ID of the file." + }, + "index": { + "type": "integer", + "format": "int32", + "description": "The index of the file in the list of files." + }, + "filename": { + "type": "string", + "description": "The filename of the file cited." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Annotation" + } + ], + "description": "A citation to a file." + }, + "OpenAI.AnnotationFilePath": { + "type": "object", + "required": [ + "type", + "file_id", + "index" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_path" + ], + "description": "The type of the file path. Always `file_path`." + }, + "file_id": { + "type": "string", + "description": "The ID of the file." + }, + "index": { + "type": "integer", + "format": "int32", + "description": "The index of the file in the list of files." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Annotation" + } + ], + "description": "A path to a file." + }, + "OpenAI.AnnotationType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "file_citation", + "url_citation", + "file_path", + "container_file_citation" + ] + } + ] + }, + "OpenAI.AnnotationUrlCitation": { + "type": "object", + "required": [ + "type", + "url", + "start_index", + "end_index", + "title" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "url_citation" + ], + "description": "The type of the URL citation. Always `url_citation`." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The URL of the web resource." + }, + "start_index": { + "type": "integer", + "format": "int32", + "description": "The index of the first character of the URL citation in the message." + }, + "end_index": { + "type": "integer", + "format": "int32", + "description": "The index of the last character of the URL citation in the message." + }, + "title": { + "type": "string", + "description": "The title of the web resource." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Annotation" + } + ], + "description": "A citation for a web resource used to generate a model response." + }, + "OpenAI.ApproximateLocation": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "approximate" + ] + }, + "country": { + "type": "string", + "nullable": true + }, + "region": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "timezone": { + "type": "string", + "nullable": true + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Location" + } + ] + }, + "OpenAI.AutoChunkingStrategyRequestParam": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "auto" + ], + "description": "Always `auto`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyRequestParam" + } + ], + "description": "The default strategy. This strategy currently uses a `max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + }, + "OpenAI.ChatCompletionFunctionCallOption": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "description": "Specifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function." + }, + "OpenAI.ChatCompletionFunctions": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "description": "A description of what the function does, used by the model to choose when and how to call the function." + }, + "name": { + "type": "string", + "description": "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "parameters": { + "description": "The parameters the functions accepts, described as a JSON Schema object.\nSee the [JSON Schema reference](https://json-schema.org/understanding-json-schema/)\nfor documentation about the format.\n\nOmitting `parameters` defines a function with an empty parameter list." + } + }, + "deprecated": true + }, + "OpenAI.ChatCompletionMessageAudioChunk": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "transcript": { + "type": "string" + }, + "data": { + "type": "string", + "format": "base64" + }, + "expires_at": { + "type": "integer", + "format": "unixtime" + } + } + }, + "OpenAI.ChatCompletionMessageToolCall": { + "type": "object", + "required": [ + "id", + "type", + "function" + ], + "properties": { + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + } + }, + "required": [ + "name", + "arguments" + ], + "description": "The function that the model called." + } + } + }, + "OpenAI.ChatCompletionMessageToolCallChunk": { + "type": "object", + "required": [ + "index" + ], + "properties": { + "index": { + "type": "integer", + "format": "int32" + }, + "id": { + "type": "string", + "description": "The ID of the tool call." + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "arguments": { + "type": "string", + "description": "The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function." + } + } + } + } + }, + "OpenAI.ChatCompletionNamedToolChoice": { + "type": "object", + "required": [ + "type", + "function" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "required": [ + "name" + ] + } + }, + "description": "Specifies a tool the model should use. Use to force the model to call a specific function." + }, + "OpenAI.ChatCompletionRequestAssistantMessage": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestAssistantMessageContentPart" + } + } + ], + "nullable": true, + "description": "The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified." + }, + "refusal": { + "type": "string", + "nullable": true, + "description": "The refusal message by the assistant." + }, + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the messages author, in this case `assistant`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + }, + "audio": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for a previous audio response from the model." + } + }, + "required": [ + "id" + ], + "nullable": true, + "description": "Data about a previous audio response from the model." + }, + "tool_calls": { + "$ref": "#/components/schemas/ChatCompletionMessageToolCallsItem" + }, + "function_call": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "arguments": { + "type": "string" + } + }, + "required": [ + "name", + "arguments" + ], + "nullable": true, + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "deprecated": true + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ], + "description": "Messages sent by the model in response to user messages." + }, + "OpenAI.ChatCompletionRequestAssistantMessageContentPart": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartRefusal" + } + ] + }, + "OpenAI.ChatCompletionRequestDeveloperMessage": { + "type": "object", + "required": [ + "content", + "role" + ], + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + } + } + ], + "description": "The contents of the developer message." + }, + "role": { + "type": "string", + "enum": [ + "developer" + ], + "description": "The role of the messages author, in this case `developer`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ], + "description": "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, `developer` messages\nreplace the previous `system` messages." + }, + "OpenAI.ChatCompletionRequestFunctionMessage": { + "type": "object", + "required": [ + "role", + "content", + "name" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "function" + ], + "description": "The role of the messages author, in this case `function`." + }, + "content": { + "type": "string", + "nullable": true, + "description": "The contents of the function message." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ], + "deprecated": true + }, + "OpenAI.ChatCompletionRequestMessage": { + "type": "object", + "required": [ + "role" + ], + "properties": { + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRole" + } + ], + "description": "The role of the author of this message." + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + } + ], + "nullable": true, + "description": "The content of the message. Valid content part types vary per message role." + } + }, + "discriminator": { + "propertyName": "role", + "mapping": { + "system": "#/components/schemas/OpenAI.ChatCompletionRequestSystemMessage", + "developer": "#/components/schemas/OpenAI.ChatCompletionRequestDeveloperMessage", + "user": "#/components/schemas/OpenAI.ChatCompletionRequestUserMessage", + "assistant": "#/components/schemas/OpenAI.ChatCompletionRequestAssistantMessage", + "tool": "#/components/schemas/OpenAI.ChatCompletionRequestToolMessage", + "function": "#/components/schemas/OpenAI.ChatCompletionRequestFunctionMessage" + } + } + }, + "OpenAI.ChatCompletionRequestMessageContentPart": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText", + "image_url": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartImage", + "refusal": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartRefusal", + "file": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartFile", + "input_audio": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartAudio" + } + } + }, + "OpenAI.ChatCompletionRequestMessageContentPartAudio": { + "type": "object", + "required": [ + "type", + "input_audio" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_audio" + ], + "description": "The type of the content part. Always `input_audio`." + }, + "input_audio": { + "type": "object", + "properties": { + "data": { + "type": "string", + "format": "base64", + "description": "Base64 encoded audio data." + }, + "format": { + "type": "string", + "enum": [ + "wav", + "mp3" + ], + "description": "The format of the encoded audio data. Currently supports \"wav\" and \"mp3\"." + } + }, + "required": [ + "data", + "format" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + ], + "description": "" + }, + "OpenAI.ChatCompletionRequestMessageContentPartFile": { + "type": "object", + "required": [ + "type", + "file" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file" + ], + "description": "The type of the content part. Always `file`." + }, + "file": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "description": "The name of the file, used when passing the file to the model as a\nstring." + }, + "file_data": { + "type": "string", + "description": "The base64 encoded file data, used when passing the file to the model\nas a string." + }, + "file_id": { + "type": "string", + "description": "The ID of an uploaded file to use as input." + } + } + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + ], + "description": "Learn about [file inputs](/docs/guides/text) for text generation." + }, + "OpenAI.ChatCompletionRequestMessageContentPartImage": { + "type": "object", + "required": [ + "type", + "image_url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_url" + ], + "description": "The type of the content part." + }, + "image_url": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "Either a URL of the image or the base64 encoded image data." + }, + "detail": { + "type": "string", + "enum": [ + "auto", + "low", + "high" + ], + "description": "Specifies the detail level of the image. Learn more in the [Vision guide](/docs/guides/vision#low-or-high-fidelity-image-understanding).", + "default": "auto" + } + }, + "required": [ + "url" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + ], + "description": "" + }, + "OpenAI.ChatCompletionRequestMessageContentPartRefusal": { + "type": "object", + "required": [ + "type", + "refusal" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "refusal" + ], + "description": "The type of the content part." + }, + "refusal": { + "type": "string", + "description": "The refusal message generated by the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + ] + }, + "OpenAI.ChatCompletionRequestMessageContentPartText": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ], + "description": "The type of the content part." + }, + "text": { + "type": "string", + "description": "The text content." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPart" + } + ], + "description": "" + }, + "OpenAI.ChatCompletionRequestMessageContentPartType": { + "type": "string", + "enum": [ + "text", + "file", + "input_audio", + "image_url", + "refusal" + ] + }, + "OpenAI.ChatCompletionRequestSystemMessage": { + "type": "object", + "required": [ + "content", + "role" + ], + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestSystemMessageContentPart" + } + } + ], + "description": "The contents of the system message." + }, + "role": { + "type": "string", + "enum": [ + "system" + ], + "description": "The role of the messages author, in this case `system`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ], + "description": "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, use `developer` messages\nfor this purpose instead." + }, + "OpenAI.ChatCompletionRequestSystemMessageContentPart": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + }, + "OpenAI.ChatCompletionRequestToolMessage": { + "type": "object", + "required": [ + "role", + "content", + "tool_call_id" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "tool" + ], + "description": "The role of the messages author, in this case `tool`." + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestToolMessageContentPart" + } + } + ], + "description": "The contents of the tool message." + }, + "tool_call_id": { + "type": "string", + "description": "Tool call that this message is responding to." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ] + }, + "OpenAI.ChatCompletionRequestToolMessageContentPart": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + }, + "OpenAI.ChatCompletionRequestUserMessage": { + "type": "object", + "required": [ + "content", + "role" + ], + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestUserMessageContentPart" + } + } + ], + "description": "The contents of the user message." + }, + "role": { + "type": "string", + "enum": [ + "user" + ], + "description": "The role of the messages author, in this case `user`." + }, + "name": { + "type": "string", + "description": "An optional name for the participant. Provides the model information to differentiate between participants of the same role." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessage" + } + ], + "description": "Messages sent by an end user, containing prompts or additional context\ninformation." + }, + "OpenAI.ChatCompletionRequestUserMessageContentPart": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartImage" + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartAudio" + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartFile" + } + ] + }, + "OpenAI.ChatCompletionRole": { + "type": "string", + "enum": [ + "system", + "developer", + "user", + "assistant", + "tool", + "function" + ], + "description": "The role of the author of a message" + }, + "OpenAI.ChatCompletionStreamOptions": { + "type": "object", + "properties": { + "include_usage": { + "type": "boolean", + "description": "If set, an additional chunk will be streamed before the `data: [DONE]`\nmessage. The `usage` field on this chunk shows the token usage statistics\nfor the entire request, and the `choices` field will always be an empty\narray.\n\nAll other chunks will also include a `usage` field, but with a null\nvalue. **NOTE:** If the stream is interrupted, you may not receive the\nfinal usage chunk which contains the total token usage for the request." + } + }, + "description": "Options for streaming response. Only set this when you set `stream: true`." + }, + "OpenAI.ChatCompletionStreamResponseDelta": { + "type": "object", + "properties": { + "audio": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionMessageAudioChunk" + } + ], + "description": "Response audio associated with the streaming chat delta payload." + }, + "content": { + "type": "string", + "nullable": true, + "description": "The contents of the chunk message." + }, + "function_call": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "arguments": { + "type": "string" + } + }, + "description": "Deprecated and replaced by `tool_calls`. The name and arguments of a function that should be called, as generated by the model.", + "deprecated": true + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionMessageToolCallChunk" + }, + "readOnly": true + }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRole" + } + ], + "description": "The role of the author of this message." + }, + "refusal": { + "type": "string", + "nullable": true, + "description": "The refusal message generated by the model." + } + }, + "description": "A chat completion delta generated by streamed model responses." + }, + "OpenAI.ChatCompletionTokenLogprob": { + "type": "object", + "required": [ + "token", + "logprob", + "bytes", + "top_logprobs" + ], + "properties": { + "token": { + "type": "string", + "description": "The token." + }, + "logprob": { + "type": "number", + "format": "float", + "description": "The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely." + }, + "bytes": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "nullable": true, + "description": "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token." + }, + "top_logprobs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "token": { + "type": "string", + "description": "The token." + }, + "logprob": { + "type": "number", + "format": "float", + "description": "The log probability of this token, if it is within the top 20 most likely tokens. Otherwise, the value `-9999.0` is used to signify that the token is very unlikely." + }, + "bytes": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "nullable": true, + "description": "A list of integers representing the UTF-8 bytes representation of the token. Useful in instances where characters are represented by multiple tokens and their byte representations must be combined to generate the correct text representation. Can be `null` if there is no bytes representation for the token." + } + }, + "required": [ + "token", + "logprob", + "bytes" + ] + }, + "description": "List of the most likely tokens and their log probability, at this token position. In rare cases, there may be fewer than the number of requested `top_logprobs` returned." + } + } + }, + "OpenAI.ChatCompletionTool": { + "type": "object", + "required": [ + "type", + "function" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the tool. Currently, only `function` is supported." + }, + "function": { + "$ref": "#/components/schemas/OpenAI.FunctionObject" + } + } + }, + "OpenAI.ChatCompletionToolChoiceOption": { + "anyOf": [ + { + "type": "string", + "enum": [ + "none", + "auto", + "required" + ] + }, + { + "$ref": "#/components/schemas/OpenAI.ChatCompletionNamedToolChoice" + } + ], + "description": "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tool and instead generates a message.\n`auto` means the model can pick between generating a message or calling one or more tools.\n`required` means the model must call one or more tools.\nSpecifying a particular tool via `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n\n`none` is the default when no tools are present. `auto` is the default if tools are present." + }, + "OpenAI.ChatOutputPrediction": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ChatOutputPredictionType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "content": "#/components/schemas/OpenAI.ChatOutputPredictionContent" + } + }, + "description": "Base representation of predicted output from a model." + }, + "OpenAI.ChatOutputPredictionContent": { + "type": "object", + "required": [ + "type", + "content" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "content" + ], + "description": "The type of the predicted content you want to provide. This type is\ncurrently always `content`." + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ChatCompletionRequestMessageContentPartText" + } + } + ], + "description": "The content that should be matched when generating a model response.\nIf generated tokens would match this content, the entire model response\ncan be returned much more quickly." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChatOutputPrediction" + } + ], + "description": "Static predicted output content, such as the content of a text file that is\nbeing regenerated." + }, + "OpenAI.ChatOutputPredictionType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "content" + ] + } + ] + }, + "OpenAI.ChunkingStrategyRequestParam": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "auto", + "static" + ], + "description": "The type of chunking strategy." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "static": "#/components/schemas/OpenAI.StaticChunkingStrategyRequestParam" + } + }, + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy." + }, + "OpenAI.ChunkingStrategyResponseParam": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "static", + "other" + ] + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "other": "#/components/schemas/OpenAI.OtherChunkingStrategyResponseParam", + "static": "#/components/schemas/OpenAI.StaticChunkingStrategyResponseParam" + } + } + }, + "OpenAI.CodeInterpreterOutput": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterOutputType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "image": "#/components/schemas/OpenAI.CodeInterpreterOutputImage", + "logs": "#/components/schemas/OpenAI.CodeInterpreterOutputLogs" + } + } + }, + "OpenAI.CodeInterpreterOutputImage": { + "type": "object", + "required": [ + "type", + "url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image" + ], + "description": "The type of the output. Always 'image'." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The URL of the image output from the code interpreter." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterOutput" + } + ], + "description": "The image output from the code interpreter." + }, + "OpenAI.CodeInterpreterOutputLogs": { + "type": "object", + "required": [ + "type", + "logs" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "logs" + ], + "description": "The type of the output. Always 'logs'." + }, + "logs": { + "type": "string", + "description": "The logs output from the code interpreter." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterOutput" + } + ], + "description": "The logs output from the code interpreter." + }, + "OpenAI.CodeInterpreterOutputType": { + "type": "string", + "enum": [ + "logs", + "image" + ] + }, + "OpenAI.CodeInterpreterTool": { + "type": "object", + "required": [ + "type", + "container" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "code_interpreter" + ], + "description": "The type of the code interpreter tool. Always `code_interpreter`." + }, + "container": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterToolAuto" + } + ], + "description": "The code interpreter container. Can be a container ID or an object that\nspecifies uploaded file IDs to make available to your code." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "A tool that runs Python code to help generate a response to a prompt." + }, + "OpenAI.CodeInterpreterToolAuto": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "auto" + ], + "description": "Always `auto`." + }, + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "An optional list of uploaded files to make available to your code." + } + }, + "description": "Configuration for a code interpreter container. Optionally specify the IDs\nof the files to run the code on." + }, + "OpenAI.CodeInterpreterToolCallItemParam": { + "type": "object", + "required": [ + "type", + "container_id", + "code", + "outputs" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "code_interpreter_call" + ] + }, + "container_id": { + "type": "string", + "description": "The ID of the container used to run the code." + }, + "code": { + "type": "string", + "nullable": true, + "description": "The code to run, or null if not available." + }, + "outputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterOutput" + }, + "nullable": true, + "description": "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A tool call to run code.\n" + }, + "OpenAI.CodeInterpreterToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "container_id", + "code", + "outputs" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "code_interpreter_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete", + "interpreting", + "failed" + ] + }, + "container_id": { + "type": "string", + "description": "The ID of the container used to run the code." + }, + "code": { + "type": "string", + "nullable": true, + "description": "The code to run, or null if not available." + }, + "outputs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.CodeInterpreterOutput" + }, + "nullable": true, + "description": "The outputs generated by the code interpreter, such as logs or images.\nCan be null if no outputs are available." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A tool call to run code.\n" + }, + "OpenAI.ComparisonFilter": { + "type": "object", + "required": [ + "type", + "key", + "value" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "eq", + "ne", + "gt", + "gte", + "lt", + "lte" + ], + "description": "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal", + "default": "eq" + }, + "key": { + "type": "string", + "description": "The key to compare against the value." + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number", + "format": "float" + }, + { + "type": "boolean" + } + ], + "description": "The value to compare against the attribute key; supports string, number, or boolean types." + } + }, + "description": "A filter used to compare a specified attribute key to a given value using a defined comparison operation." + }, + "OpenAI.CompletionUsage": { + "type": "object", + "required": [ + "completion_tokens", + "prompt_tokens", + "total_tokens" + ], + "properties": { + "completion_tokens": { + "type": "integer", + "format": "int32", + "description": "Number of tokens in the generated completion.", + "default": 0 + }, + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "Number of tokens in the prompt.", + "default": 0 + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "Total number of tokens used in the request (prompt + completion).", + "default": 0 + }, + "completion_tokens_details": { + "type": "object", + "properties": { + "accepted_prediction_tokens": { + "type": "integer", + "format": "int32", + "description": "When using Predicted Outputs, the number of tokens in the\nprediction that appeared in the completion.", + "default": 0 + }, + "audio_tokens": { + "type": "integer", + "format": "int32", + "description": "Audio input tokens generated by the model.", + "default": 0 + }, + "reasoning_tokens": { + "type": "integer", + "format": "int32", + "description": "Tokens generated by the model for reasoning.", + "default": 0 + }, + "rejected_prediction_tokens": { + "type": "integer", + "format": "int32", + "description": "When using Predicted Outputs, the number of tokens in the\nprediction that did not appear in the completion. However, like\nreasoning tokens, these tokens are still counted in the total\ncompletion tokens for purposes of billing, output, and context window\nlimits.", + "default": 0 + } + }, + "description": "Breakdown of tokens used in a completion." + }, + "prompt_tokens_details": { + "type": "object", + "properties": { + "audio_tokens": { + "type": "integer", + "format": "int32", + "description": "Audio input tokens present in the prompt.", + "default": 0 + }, + "cached_tokens": { + "type": "integer", + "format": "int32", + "description": "Cached tokens present in the prompt.", + "default": 0 + } + }, + "description": "Breakdown of tokens used in the prompt." + } + }, + "description": "Usage statistics for the completion request." + }, + "OpenAI.CompoundFilter": { + "type": "object", + "required": [ + "type", + "filters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "and", + "or" + ], + "description": "Type of operation: `and` or `or`." + }, + "filters": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComparisonFilter" + }, + { + "$ref": "#/components/schemas/OpenAI.CompoundFilter" + } + ] + }, + "description": "Array of filters to combine. Items can be `ComparisonFilter` or `CompoundFilter`." + } + }, + "description": "Combine multiple filters using `and` or `or`." + }, + "OpenAI.ComputerAction": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ComputerActionType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "click": "#/components/schemas/OpenAI.ComputerActionClick", + "double_click": "#/components/schemas/OpenAI.ComputerActionDoubleClick", + "drag": "#/components/schemas/OpenAI.ComputerActionDrag", + "move": "#/components/schemas/OpenAI.ComputerActionMove", + "screenshot": "#/components/schemas/OpenAI.ComputerActionScreenshot", + "scroll": "#/components/schemas/OpenAI.ComputerActionScroll", + "type": "#/components/schemas/OpenAI.ComputerActionTypeKeys", + "wait": "#/components/schemas/OpenAI.ComputerActionWait", + "keypress": "#/components/schemas/OpenAI.ComputerActionKeyPress" + } + } + }, + "OpenAI.ComputerActionClick": { + "type": "object", + "required": [ + "type", + "button", + "x", + "y" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "click" + ], + "description": "Specifies the event type. For a click action, this property is\nalways set to `click`." + }, + "button": { + "type": "string", + "enum": [ + "left", + "right", + "wheel", + "back", + "forward" + ], + "description": "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`." + }, + "x": { + "type": "integer", + "format": "int32", + "description": "The x-coordinate where the click occurred." + }, + "y": { + "type": "integer", + "format": "int32", + "description": "The y-coordinate where the click occurred." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A click action." + }, + "OpenAI.ComputerActionDoubleClick": { + "type": "object", + "required": [ + "type", + "x", + "y" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "double_click" + ], + "description": "Specifies the event type. For a double click action, this property is\nalways set to `double_click`." + }, + "x": { + "type": "integer", + "format": "int32", + "description": "The x-coordinate where the double click occurred." + }, + "y": { + "type": "integer", + "format": "int32", + "description": "The y-coordinate where the double click occurred." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A double click action." + }, + "OpenAI.ComputerActionDrag": { + "type": "object", + "required": [ + "type", + "path" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "drag" + ], + "description": "Specifies the event type. For a drag action, this property is\nalways set to `drag`." + }, + "path": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Coordinate" + }, + "description": "An array of coordinates representing the path of the drag action. Coordinates will appear as an array\nof objects, eg\n```\n[\n { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A drag action." + }, + "OpenAI.ComputerActionKeyPress": { + "type": "object", + "required": [ + "type", + "keys" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "keypress" + ], + "description": "Specifies the event type. For a keypress action, this property is\nalways set to `keypress`." + }, + "keys": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The combination of keys the model is requesting to be pressed. This is an\narray of strings, each representing a key." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A collection of keypresses the model would like to perform." + }, + "OpenAI.ComputerActionMove": { + "type": "object", + "required": [ + "type", + "x", + "y" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "move" + ], + "description": "Specifies the event type. For a move action, this property is\nalways set to `move`." + }, + "x": { + "type": "integer", + "format": "int32", + "description": "The x-coordinate to move to." + }, + "y": { + "type": "integer", + "format": "int32", + "description": "The y-coordinate to move to." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A mouse move action." + }, + "OpenAI.ComputerActionScreenshot": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "screenshot" + ], + "description": "Specifies the event type. For a screenshot action, this property is\nalways set to `screenshot`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A screenshot action." + }, + "OpenAI.ComputerActionScroll": { + "type": "object", + "required": [ + "type", + "x", + "y", + "scroll_x", + "scroll_y" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "scroll" + ], + "description": "Specifies the event type. For a scroll action, this property is\nalways set to `scroll`." + }, + "x": { + "type": "integer", + "format": "int32", + "description": "The x-coordinate where the scroll occurred." + }, + "y": { + "type": "integer", + "format": "int32", + "description": "The y-coordinate where the scroll occurred." + }, + "scroll_x": { + "type": "integer", + "format": "int32", + "description": "The horizontal scroll distance." + }, + "scroll_y": { + "type": "integer", + "format": "int32", + "description": "The vertical scroll distance." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A scroll action." + }, + "OpenAI.ComputerActionType": { + "type": "string", + "enum": [ + "screenshot", + "click", + "double_click", + "scroll", + "type", + "wait", + "keypress", + "drag", + "move" + ] + }, + "OpenAI.ComputerActionTypeKeys": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "type" + ], + "description": "Specifies the event type. For a type action, this property is\nalways set to `type`." + }, + "text": { + "type": "string", + "description": "The text to type." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "An action to type in text." + }, + "OpenAI.ComputerActionWait": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "wait" + ], + "description": "Specifies the event type. For a wait action, this property is\nalways set to `wait`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + } + ], + "description": "A wait action." + }, + "OpenAI.ComputerToolCallItemParam": { + "type": "object", + "required": [ + "type", + "call_id", + "action", + "pending_safety_checks" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_call" + ] + }, + "call_id": { + "type": "string", + "description": "An identifier used when responding to the tool call with output." + }, + "action": { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + }, + "pending_safety_checks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallSafetyCheck" + }, + "description": "The pending safety checks for the computer call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n" + }, + "OpenAI.ComputerToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "call_id", + "action", + "pending_safety_checks" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ], + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + }, + "call_id": { + "type": "string", + "description": "An identifier used when responding to the tool call with output." + }, + "action": { + "$ref": "#/components/schemas/OpenAI.ComputerAction" + }, + "pending_safety_checks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallSafetyCheck" + }, + "description": "The pending safety checks for the computer call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n" + }, + "OpenAI.ComputerToolCallOutputItemOutput": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallOutputItemOutputType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "computer_screenshot": "#/components/schemas/OpenAI.ComputerToolCallOutputItemOutputComputerScreenshot" + } + } + }, + "OpenAI.ComputerToolCallOutputItemOutputComputerScreenshot": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_screenshot" + ] + }, + "image_url": { + "type": "string" + }, + "file_id": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallOutputItemOutput" + } + ] + }, + "OpenAI.ComputerToolCallOutputItemOutputType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "computer_screenshot" + ] + } + ], + "description": "A computer screenshot image used with the computer use tool." + }, + "OpenAI.ComputerToolCallOutputItemParam": { + "type": "object", + "required": [ + "type", + "call_id", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_call_output" + ] + }, + "call_id": { + "type": "string", + "description": "The ID of the computer tool call that produced the output." + }, + "acknowledged_safety_checks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallSafetyCheck" + }, + "description": "The safety checks reported by the API that have been acknowledged by the\ndeveloper." + }, + "output": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallOutputItemOutput" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "The output of a computer tool call.\n" + }, + "OpenAI.ComputerToolCallOutputItemResource": { + "type": "object", + "required": [ + "type", + "status", + "call_id", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_call_output" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ], + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + }, + "call_id": { + "type": "string", + "description": "The ID of the computer tool call that produced the output." + }, + "acknowledged_safety_checks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallSafetyCheck" + }, + "description": "The safety checks reported by the API that have been acknowledged by the\ndeveloper." + }, + "output": { + "$ref": "#/components/schemas/OpenAI.ComputerToolCallOutputItemOutput" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The output of a computer tool call.\n" + }, + "OpenAI.ComputerToolCallSafetyCheck": { + "type": "object", + "required": [ + "id", + "code", + "message" + ], + "properties": { + "id": { + "type": "string", + "description": "The ID of the pending safety check." + }, + "code": { + "type": "string", + "description": "The type of the pending safety check." + }, + "message": { + "type": "string", + "description": "Details about the pending safety check." + } + }, + "description": "A pending safety check for the computer call." + }, + "OpenAI.ComputerUsePreviewTool": { + "type": "object", + "required": [ + "type", + "environment", + "display_width", + "display_height" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_use_preview" + ], + "description": "The type of the computer use tool. Always `computer_use_preview`." + }, + "environment": { + "type": "string", + "enum": [ + "windows", + "mac", + "linux", + "ubuntu", + "browser" + ], + "description": "The type of computer environment to control." + }, + "display_width": { + "type": "integer", + "format": "int32", + "description": "The width of the computer display." + }, + "display_height": { + "type": "integer", + "format": "int32", + "description": "The height of the computer display." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "A tool that controls a virtual computer. Learn more about the [computer tool](https://platform.openai.com/docs/guides/tools-computer-use)." + }, + "OpenAI.ContainerFileListResource": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of object returned, must be 'list'." + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ContainerFileResource" + }, + "description": "A list of container files." + }, + "first_id": { + "type": "string", + "description": "The ID of the first file in the list." + }, + "last_id": { + "type": "string", + "description": "The ID of the last file in the list." + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more files available." + } + } + }, + "OpenAI.ContainerFileResource": { + "type": "object", + "required": [ + "id", + "object", + "container_id", + "created_at", + "bytes", + "path", + "source" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the file." + }, + "object": { + "type": "string", + "description": "The type of this object (`container.file`)." + }, + "container_id": { + "type": "string", + "description": "The container this file belongs to." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) when the file was created." + }, + "bytes": { + "type": "integer", + "format": "int32", + "description": "Size of the file in bytes." + }, + "path": { + "type": "string", + "description": "Path of the file in the container." + }, + "source": { + "type": "string", + "description": "Source of the file (e.g., `user`, `assistant`)." + } + } + }, + "OpenAI.ContainerListResource": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of object returned, must be 'list'." + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ContainerResource" + }, + "description": "A list of containers." + }, + "first_id": { + "type": "string", + "description": "The ID of the first container in the list." + }, + "last_id": { + "type": "string", + "description": "The ID of the last container in the list." + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more containers available." + } + } + }, + "OpenAI.ContainerResource": { + "type": "object", + "required": [ + "id", + "object", + "name", + "created_at", + "status" + ], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the container." + }, + "object": { + "type": "string", + "description": "The type of this object." + }, + "name": { + "type": "string", + "description": "Name of the container." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) when the container was created." + }, + "status": { + "type": "string", + "description": "Status of the container (e.g., active, deleted)." + }, + "expires_after": { + "type": "object", + "properties": { + "anchor": { + "type": "string", + "enum": [ + "last_active_at" + ], + "description": "The reference point for the expiration." + }, + "minutes": { + "type": "integer", + "format": "int32", + "description": "The number of minutes after the anchor before the container expires." + } + }, + "description": "The container will expire after this time period.\nThe anchor is the reference point for the expiration.\nThe minutes is the number of minutes after the anchor before the container expires." + } + } + }, + "OpenAI.Coordinate": { + "type": "object", + "required": [ + "x", + "y" + ], + "properties": { + "x": { + "type": "integer", + "format": "int32", + "description": "The x-coordinate." + }, + "y": { + "type": "integer", + "format": "int32", + "description": "The y-coordinate." + } + }, + "description": "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`." + }, + "OpenAI.CreateContainerBody": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the container to create." + }, + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "IDs of files to copy to the container." + }, + "expires_after": { + "type": "object", + "properties": { + "anchor": { + "type": "string", + "enum": [ + "last_active_at" + ], + "description": "Time anchor for the expiration time. Currently only 'last_active_at' is supported." + }, + "minutes": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "anchor", + "minutes" + ], + "description": "Container expiration time in seconds relative to the 'anchor' time." + } + } + }, + "OpenAI.CreateContainerFileBodyMultiPart": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "Name of the file to create." + }, + "file": { + "type": "string", + "format": "binary" + } + } + }, + "OpenAI.CreateEmbeddingResponse": { + "type": "object", + "required": [ + "data", + "model", + "object", + "usage" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Embedding" + }, + "description": "The list of embeddings generated by the model." + }, + "model": { + "type": "string", + "description": "The name of the model used to generate the embedding." + }, + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The object type, which is always \"list\"." + }, + "usage": { + "type": "object", + "properties": { + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens used by the prompt." + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens used by the request." + } + }, + "required": [ + "prompt_tokens", + "total_tokens" + ], + "description": "The usage information for the request." + } + } + }, + "OpenAI.CreateEvalItem": { + "anyOf": [ + { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }, + "content": { + "type": "string", + "description": "The content of the message." + } + }, + "required": [ + "role", + "content" + ] + }, + { + "$ref": "#/components/schemas/OpenAI.EvalItem" + } + ], + "description": "A chat message that makes up the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + }, + "OpenAI.CreateEvalRunRequest": { + "type": "object", + "required": [ + "data_source" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the run." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "data_source": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceParams" + } + ], + "description": "Details about the run's data source." + } + } + }, + "OpenAI.CreateFineTuningJobRequest": { + "type": "object", + "required": [ + "model", + "training_file" + ], + "properties": { + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "babbage-002", + "davinci-002", + "gpt-3.5-turbo", + "gpt-4o-mini" + ] + } + ], + "description": "The name of the model to fine-tune. You can select one of the\n[supported models](/docs/guides/fine-tuning#which-models-can-be-fine-tuned).", + "x-oaiTypeLabel": "string" + }, + "training_file": { + "type": "string", + "description": "The ID of an uploaded file that contains training data.\n\nSee [upload file](/docs/api-reference/files/create) for how to upload a file.\n\nYour dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.\n\nThe contents of the file should differ depending on if the model uses the [chat](/docs/api-reference/fine-tuning/chat-input), [completions](/docs/api-reference/fine-tuning/completions-input) format, or if the fine-tuning method uses the [preference](/docs/api-reference/fine-tuning/preference-input) format.\n\nSee the [fine-tuning guide](/docs/guides/model-optimization) for more details." + }, + "hyperparameters": { + "type": "object", + "properties": { + "batch_size": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.", + "default": "auto" + }, + "learning_rate_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.", + "default": "auto" + }, + "n_epochs": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.", + "default": "auto" + } + }, + "description": "The hyperparameters used for the fine-tuning job.\nThis value is now deprecated in favor of `method`, and should be passed in under the `method` parameter.", + "deprecated": true + }, + "suffix": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 64, + "description": "A string of up to 64 characters that will be added to your fine-tuned model name.\n\nFor example, a `suffix` of \"custom-model-name\" would produce a model name like `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`.", + "default": null + }, + "validation_file": { + "type": "string", + "nullable": true, + "description": "The ID of an uploaded file that contains validation data.\n\nIf you provide this file, the data is used to generate validation\nmetrics periodically during fine-tuning. These metrics can be viewed in\nthe fine-tuning results file.\nThe same data should not be present in both train and validation files.\n\nYour dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.\n\nSee the [fine-tuning guide](/docs/guides/model-optimization) for more details." + }, + "integrations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.CreateFineTuningJobRequestIntegration" + }, + "nullable": true, + "description": "A list of integrations to enable for your fine-tuning job." + }, + "seed": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0, + "maximum": 2147483647, + "description": "The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases.\nIf a seed is not specified, one will be generated for you." + }, + "method": { + "$ref": "#/components/schemas/OpenAI.FineTuneMethod" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + } + }, + "OpenAI.CreateFineTuningJobRequestIntegration": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "wandb" + ] + } + ] + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "wandb": "#/components/schemas/OpenAI.CreateFineTuningJobRequestWandbIntegration" + } + } + }, + "OpenAI.CreateFineTuningJobRequestWandbIntegration": { + "type": "object", + "required": [ + "type", + "wandb" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "wandb" + ] + }, + "wandb": { + "type": "object", + "properties": { + "project": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "entity": { + "type": "string", + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "project" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.CreateFineTuningJobRequestIntegration" + } + ] + }, + "OpenAI.CreateVectorStoreFileBatchRequest": { + "type": "object", + "required": [ + "file_ids" + ], + "properties": { + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 500, + "description": "A list of [File](/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files." + }, + "chunking_strategy": { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyRequestParam" + }, + "attributes": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + } + ], + "nullable": true + } + } + }, + "OpenAI.CreateVectorStoreFileRequest": { + "type": "object", + "required": [ + "file_id" + ], + "properties": { + "file_id": { + "type": "string", + "description": "A [File](/docs/api-reference/files) ID that the vector store should use. Useful for tools like `file_search` that can access files." + }, + "chunking_strategy": { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyRequestParam" + }, + "attributes": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + } + ], + "nullable": true + } + } + }, + "OpenAI.CreateVectorStoreRequest": { + "type": "object", + "properties": { + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 500, + "description": "A list of [File](/docs/api-reference/files) IDs that the vector store should use. Useful for tools like `file_search` that can access files." + }, + "name": { + "type": "string", + "description": "The name of the vector store." + }, + "expires_after": { + "$ref": "#/components/schemas/OpenAI.VectorStoreExpirationAfter" + }, + "chunking_strategy": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.AutoChunkingStrategyRequestParam" + }, + { + "$ref": "#/components/schemas/OpenAI.StaticChunkingStrategyRequestParam" + } + ], + "description": "The chunking strategy used to chunk the file(s). If not set, will use the `auto` strategy. Only applicable if `file_ids` is non-empty." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + } + }, + "OpenAI.DeleteFileResponse": { + "type": "object", + "required": [ + "id", + "object", + "deleted" + ], + "properties": { + "id": { + "type": "string" + }, + "object": { + "type": "string", + "enum": [ + "file" + ] + }, + "deleted": { + "type": "boolean" + } + } + }, + "OpenAI.DeleteVectorStoreFileResponse": { + "type": "object", + "required": [ + "id", + "deleted", + "object" + ], + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "vector_store.file.deleted" + ] + } + } + }, + "OpenAI.DeleteVectorStoreResponse": { + "type": "object", + "required": [ + "id", + "deleted", + "object" + ], + "properties": { + "id": { + "type": "string" + }, + "deleted": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "vector_store.deleted" + ] + } + } + }, + "OpenAI.Embedding": { + "type": "object", + "required": [ + "index", + "embedding", + "object" + ], + "properties": { + "index": { + "type": "integer", + "format": "int32", + "description": "The index of the embedding in the list of embeddings." + }, + "embedding": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "number" + } + }, + { + "type": "string" + } + ], + "description": "The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](/docs/guides/embeddings)." + }, + "object": { + "type": "string", + "enum": [ + "embedding" + ], + "description": "The object type, which is always \"embedding\"." + } + }, + "description": "Represents an embedding vector returned by embedding endpoint." + }, + "OpenAI.Eval": { + "type": "object", + "required": [ + "object", + "id", + "name", + "data_source_config", + "testing_criteria", + "created_at", + "metadata" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "eval" + ], + "description": "The object type.", + "default": "eval" + }, + "id": { + "type": "string", + "description": "Unique identifier for the evaluation." + }, + "name": { + "type": "string", + "description": "The name of the evaluation." + }, + "data_source_config": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigResource" + } + ], + "description": "Configuration of data sources used in runs of the evaluation." + }, + "testing_criteria": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalGraderResource" + }, + "nullable": true, + "description": "A list of testing criteria.", + "default": null + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the eval was created." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + }, + "description": "An Eval object with a data source config and testing criteria.\nAn Eval represents a task to be done for your LLM integration.\nLike:\n- Improve the quality of my chatbot\n- See how well my chatbot handles customer support\n- Check if o4-mini is better at my usecase than gpt-4o" + }, + "OpenAI.EvalApiError": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string", + "description": "The error code." + }, + "message": { + "type": "string", + "description": "The error message." + } + }, + "description": "An object representing an error response from the Eval API." + }, + "OpenAI.EvalCompletionsRunDataSourceParams": { + "type": "object", + "required": [ + "type", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "completions" + ], + "description": "The type of run data source. Always `completions`." + }, + "input_messages": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "template" + ], + "description": "The type of input messages. Always `template`." + }, + "template": { + "type": "array", + "items": { + "anyOf": [ + {}, + { + "$ref": "#/components/schemas/OpenAI.EvalItem" + } + ] + }, + "description": "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + } + }, + "required": [ + "type", + "template" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "item_reference" + ], + "description": "The type of input messages. Always `item_reference`." + }, + "item_reference": { + "type": "string", + "description": "A reference to a variable in the `item` namespace. Ie, \"item.input_trajectory\"" + } + }, + "required": [ + "type", + "item_reference" + ] + } + ], + "description": "Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace." + }, + "sampling_params": { + "$ref": "#/components/schemas/AzureEvalAPICompletionsSamplingParams" + }, + "model": { + "type": "string", + "description": "The name of the model to use for generating completions (e.g. \"o3-mini\")." + }, + "source": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileContentDataContentSource" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileIdDataContentSource" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalRunStoredCompletionsDataContentSource" + } + ], + "description": "Determines what populates the `item` namespace in this run's data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceParams" + } + ], + "description": "A CompletionsRunDataSource object describing a model sampling configuration." + }, + "OpenAI.EvalCustomDataSourceConfigParams": { + "type": "object", + "required": [ + "type", + "item_schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "custom" + ], + "description": "The type of data source. Always `custom`." + }, + "item_schema": { + "type": "object", + "additionalProperties": {}, + "description": "The json schema for each row in the data source." + }, + "include_sample_schema": { + "type": "boolean", + "description": "Whether the eval should expect you to populate the sample namespace (ie, by generating responses off of your data source)", + "default": false + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigParams" + } + ], + "description": "A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs.\nThis schema is used to define the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run" + }, + "OpenAI.EvalCustomDataSourceConfigResource": { + "type": "object", + "required": [ + "type", + "schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "custom" + ], + "description": "The type of data source. Always `custom`." + }, + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigResource" + } + ], + "description": "A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces.\nThe response schema defines the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run" + }, + "OpenAI.EvalDataSourceConfigParams": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "custom": "#/components/schemas/OpenAI.EvalCustomDataSourceConfigParams", + "logs": "#/components/schemas/OpenAI.EvalLogsDataSourceConfigParams", + "stored_completions": "#/components/schemas/OpenAI.EvalStoredCompletionsDataSourceConfigParams" + } + } + }, + "OpenAI.EvalDataSourceConfigResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "custom": "#/components/schemas/OpenAI.EvalCustomDataSourceConfigResource", + "stored_completions": "#/components/schemas/OpenAI.EvalStoredCompletionsDataSourceConfigResource", + "logs": "#/components/schemas/OpenAI.EvalLogsDataSourceConfigResource" + } + } + }, + "OpenAI.EvalDataSourceConfigType": { + "type": "string", + "enum": [ + "custom", + "logs", + "stored_completions" + ] + }, + "OpenAI.EvalGraderLabelModelParams": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input", + "labels", + "passing_labels" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "label_model" + ], + "description": "The object type, which is always `label_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation. Must support structured outputs." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.CreateEvalItem" + }, + "description": "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + }, + "labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels to classify to each item in the evaluation." + }, + "passing_labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels that indicate a passing result. Must be a subset of labels." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + } + ], + "description": "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation." + }, + "OpenAI.EvalGraderLabelModelResource": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input", + "labels", + "passing_labels" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "label_model" + ], + "description": "The object type, which is always `label_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation. Must support structured outputs." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalItem" + } + }, + "labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels to assign to each item in the evaluation." + }, + "passing_labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels that indicate a passing result. Must be a subset of labels." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderResource" + } + ] + }, + "OpenAI.EvalGraderParams": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.GraderType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "label_model": "#/components/schemas/OpenAI.EvalGraderLabelModelParams", + "string_check": "#/components/schemas/OpenAI.EvalGraderStringCheckParams", + "text_similarity": "#/components/schemas/OpenAI.EvalGraderTextSimilarityParams", + "python": "#/components/schemas/OpenAI.EvalGraderPythonParams", + "score_model": "#/components/schemas/OpenAI.EvalGraderScoreModelParams" + } + } + }, + "OpenAI.EvalGraderPythonParams": { + "type": "object", + "required": [ + "type", + "name", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "python" + ], + "description": "The object type, which is always `python`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "source": { + "type": "string", + "description": "The source code of the python script." + }, + "image_tag": { + "type": "string", + "description": "The image tag to use for the python script." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + } + ] + }, + "OpenAI.EvalGraderPythonResource": { + "type": "object", + "required": [ + "type", + "name", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "python" + ], + "description": "The object type, which is always `python`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "source": { + "type": "string", + "description": "The source code of the python script." + }, + "image_tag": { + "type": "string", + "description": "The image tag to use for the python script." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderResource" + } + ] + }, + "OpenAI.EvalGraderResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.GraderType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "label_model": "#/components/schemas/OpenAI.EvalGraderLabelModelResource", + "text_similarity": "#/components/schemas/OpenAI.EvalGraderTextSimilarityResource", + "python": "#/components/schemas/OpenAI.EvalGraderPythonResource", + "score_model": "#/components/schemas/OpenAI.EvalGraderScoreModelResource" + } + } + }, + "OpenAI.EvalGraderScoreModelParams": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "score_model" + ], + "description": "The object type, which is always `score_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation." + }, + "sampling_params": { + "description": "The sampling parameters for the model." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalItem" + }, + "description": "The input text. This may include template strings." + }, + "range": { + "type": "array", + "items": { + "type": "number", + "format": "float" + }, + "description": "The range of the score. Defaults to `[0, 1]`." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + } + ] + }, + "OpenAI.EvalGraderScoreModelResource": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "score_model" + ], + "description": "The object type, which is always `score_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation." + }, + "sampling_params": { + "description": "The sampling parameters for the model." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalItem" + }, + "description": "The input text. This may include template strings." + }, + "range": { + "type": "array", + "items": { + "type": "number", + "format": "float" + }, + "description": "The range of the score. Defaults to `[0, 1]`." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderResource" + } + ] + }, + "OpenAI.EvalGraderStringCheckParams": { + "type": "object", + "required": [ + "type", + "name", + "input", + "reference", + "operation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "string_check" + ], + "description": "The object type, which is always `string_check`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "input": { + "type": "string", + "description": "The input text. This may include template strings." + }, + "reference": { + "type": "string", + "description": "The reference text. This may include template strings." + }, + "operation": { + "type": "string", + "enum": [ + "eq", + "ne", + "like", + "ilike" + ], + "description": "The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + } + ] + }, + "OpenAI.EvalGraderTextSimilarityParams": { + "type": "object", + "required": [ + "type", + "name", + "input", + "reference", + "evaluation_metric", + "pass_threshold" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text_similarity" + ], + "description": "The type of grader." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "input": { + "type": "string", + "description": "The text being graded." + }, + "reference": { + "type": "string", + "description": "The text being graded against." + }, + "evaluation_metric": { + "type": "string", + "enum": [ + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l" + ], + "description": "The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderParams" + } + ] + }, + "OpenAI.EvalGraderTextSimilarityResource": { + "type": "object", + "required": [ + "type", + "name", + "input", + "reference", + "evaluation_metric", + "pass_threshold" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text_similarity" + ], + "description": "The type of grader." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "input": { + "type": "string", + "description": "The text being graded." + }, + "reference": { + "type": "string", + "description": "The text being graded against." + }, + "evaluation_metric": { + "type": "string", + "enum": [ + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l" + ], + "description": "The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`." + }, + "pass_threshold": { + "type": "number", + "format": "float", + "description": "The threshold for the score." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalGraderResource" + } + ] + }, + "OpenAI.EvalItem": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "assistant", + "system", + "developer" + ], + "description": "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`." + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalItemContent" + } + ], + "description": "Text inputs to the model - can contain template strings." + }, + "type": { + "type": "string", + "enum": [ + "message" + ], + "description": "The type of the message input. Always `message`." + } + }, + "description": "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions." + }, + "OpenAI.EvalItemContent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalItemContentType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "input_text": "#/components/schemas/OpenAI.EvalItemContentInputText", + "output_text": "#/components/schemas/OpenAI.EvalItemContentOutputText" + } + } + }, + "OpenAI.EvalItemContentInputText": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_text" + ] + }, + "text": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalItemContent" + } + ] + }, + "OpenAI.EvalItemContentOutputText": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "output_text" + ] + }, + "text": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalItemContent" + } + ] + }, + "OpenAI.EvalItemContentType": { + "type": "string", + "enum": [ + "input_text", + "output_text" + ] + }, + "OpenAI.EvalJsonlRunDataSourceParams": { + "type": "object", + "required": [ + "type", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "jsonl" + ], + "description": "The type of data source. Always `jsonl`." + }, + "source": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileContentDataContentSource" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileIdDataContentSource" + } + ], + "description": "Determines what populates the `item` namespace in the data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceParams" + } + ], + "description": "A JsonlRunDataSource object with that specifies a JSONL file that matches the eval" + }, + "OpenAI.EvalList": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of this object. It is always set to \"list\".", + "default": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Eval" + }, + "description": "An array of eval objects." + }, + "first_id": { + "type": "string", + "description": "The identifier of the first eval in the data array." + }, + "last_id": { + "type": "string", + "description": "The identifier of the last eval in the data array." + }, + "has_more": { + "type": "boolean", + "description": "Indicates whether there are more evals available." + } + }, + "description": "An object representing a list of evals." + }, + "OpenAI.EvalLogsDataSourceConfigParams": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "logs" + ], + "description": "The type of data source. Always `logs`." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigParams" + } + ], + "description": "A data source config which specifies the metadata property of your logs query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc." + }, + "OpenAI.EvalLogsDataSourceConfigResource": { + "type": "object", + "required": [ + "type", + "metadata", + "schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "logs" + ], + "description": "The type of data source. Always `logs`." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigResource" + } + ], + "description": "A LogsDataSourceConfig which specifies the metadata property of your logs query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.\nThe schema returned by this data source config is used to defined what variables are available in your evals.\n`item` and `sample` are both defined when using this data source config." + }, + "OpenAI.EvalResponsesRunDataSourceParams": { + "type": "object", + "required": [ + "type", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "responses" + ], + "description": "The type of run data source. Always `responses`." + }, + "input_messages": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "template" + ], + "description": "The type of input messages. Always `template`." + }, + "template": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }, + "content": { + "type": "string", + "description": "The content of the message." + } + }, + "required": [ + "role", + "content" + ] + }, + { + "$ref": "#/components/schemas/OpenAI.EvalItem" + } + ] + }, + "description": "A list of chat messages forming the prompt or context. May include variable references to the `item` namespace, ie {{item.name}}." + } + }, + "required": [ + "type", + "template" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "item_reference" + ], + "description": "The type of input messages. Always `item_reference`." + }, + "item_reference": { + "type": "string", + "description": "A reference to a variable in the `item` namespace. Ie, \"item.name\"" + } + }, + "required": [ + "type", + "item_reference" + ] + } + ], + "description": "Used when sampling from a model. Dictates the structure of the messages passed into the model. Can either be a reference to a prebuilt trajectory (ie, `item.input_trajectory`), or a template with variable references to the `item` namespace." + }, + "sampling_params": { + "$ref": "#/components/schemas/AzureEvalAPIResponseSamplingParams" + }, + "model": { + "type": "string", + "description": "The name of the model to use for generating completions (e.g. \"o3-mini\")." + }, + "source": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileContentDataContentSource" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalRunFileIdDataContentSource" + }, + { + "$ref": "#/components/schemas/OpenAI.EvalRunResponsesDataContentSource" + } + ], + "description": "Determines what populates the `item` namespace in this run's data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceParams" + } + ], + "description": "A ResponsesRunDataSource object describing a model sampling configuration." + }, + "OpenAI.EvalRun": { + "type": "object", + "required": [ + "object", + "id", + "eval_id", + "status", + "model", + "name", + "created_at", + "report_url", + "result_counts", + "per_model_usage", + "per_testing_criteria_results", + "data_source", + "metadata", + "error" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "eval.run" + ], + "description": "The type of the object. Always \"eval.run\".", + "default": "eval.run" + }, + "id": { + "type": "string", + "description": "Unique identifier for the evaluation run." + }, + "eval_id": { + "type": "string", + "description": "The identifier of the associated evaluation." + }, + "status": { + "type": "string", + "description": "The status of the evaluation run." + }, + "model": { + "type": "string", + "description": "The model that is evaluated, if applicable." + }, + "name": { + "type": "string", + "description": "The name of the evaluation run." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) when the evaluation run was created." + }, + "report_url": { + "type": "string", + "description": "The URL to the rendered evaluation run report on the UI dashboard." + }, + "result_counts": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int32", + "description": "Total number of executed output items." + }, + "errored": { + "type": "integer", + "format": "int32", + "description": "Number of output items that resulted in an error." + }, + "failed": { + "type": "integer", + "format": "int32", + "description": "Number of output items that failed to pass the evaluation." + }, + "passed": { + "type": "integer", + "format": "int32", + "description": "Number of output items that passed the evaluation." + } + }, + "required": [ + "total", + "errored", + "failed", + "passed" + ], + "description": "Counters summarizing the outcomes of the evaluation run." + }, + "per_model_usage": { + "type": "array", + "items": { + "type": "object", + "properties": { + "model_name": { + "type": "string", + "description": "The name of the model." + }, + "invocation_count": { + "type": "integer", + "format": "int32", + "description": "The number of invocations." + }, + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of prompt tokens used." + }, + "completion_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of completion tokens generated." + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens used." + }, + "cached_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens retrieved from cache." + } + }, + "required": [ + "model_name", + "invocation_count", + "prompt_tokens", + "completion_tokens", + "total_tokens", + "cached_tokens" + ] + }, + "description": "Usage statistics for each model during the evaluation run." + }, + "per_testing_criteria_results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "testing_criteria": { + "type": "string", + "description": "A description of the testing criteria." + }, + "passed": { + "type": "integer", + "format": "int32", + "description": "Number of tests passed for this criteria." + }, + "failed": { + "type": "integer", + "format": "int32", + "description": "Number of tests failed for this criteria." + } + }, + "required": [ + "testing_criteria", + "passed", + "failed" + ] + }, + "description": "Results per testing criteria applied during the evaluation run." + }, + "data_source": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceResource" + } + ], + "description": "Information about the run's data source." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "error": { + "$ref": "#/components/schemas/OpenAI.EvalApiError" + } + }, + "description": "A schema representing an evaluation run." + }, + "OpenAI.EvalRunDataContentSource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalRunDataContentSourceType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "file_id": "#/components/schemas/OpenAI.EvalRunFileIdDataContentSource", + "stored_completions": "#/components/schemas/OpenAI.EvalRunStoredCompletionsDataContentSource", + "responses": "#/components/schemas/OpenAI.EvalRunResponsesDataContentSource" + } + } + }, + "OpenAI.EvalRunDataContentSourceType": { + "type": "string", + "enum": [ + "file_id", + "file_content", + "stored_completions", + "responses" + ] + }, + "OpenAI.EvalRunDataSourceCompletionsResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "completions" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceResource" + } + ] + }, + "OpenAI.EvalRunDataSourceJsonlResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "jsonl" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceResource" + } + ] + }, + "OpenAI.EvalRunDataSourceParams": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "jsonl": "#/components/schemas/OpenAI.EvalJsonlRunDataSourceParams", + "completions": "#/components/schemas/OpenAI.EvalCompletionsRunDataSourceParams", + "responses": "#/components/schemas/OpenAI.EvalResponsesRunDataSourceParams" + } + } + }, + "OpenAI.EvalRunDataSourceResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceType" + } + } + }, + "OpenAI.EvalRunDataSourceResponsesResource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "responses" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataSourceResource" + } + ] + }, + "OpenAI.EvalRunDataSourceType": { + "type": "string", + "enum": [ + "jsonl", + "completions", + "responses" + ] + }, + "OpenAI.EvalRunFileContentDataContentSource": { + "type": "object", + "required": [ + "type", + "content" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_content" + ], + "description": "The type of jsonl source. Always `file_content`." + }, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "item": { + "type": "object", + "additionalProperties": {} + }, + "sample": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "item" + ] + }, + "description": "The content of the jsonl file." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataContentSource" + } + ] + }, + "OpenAI.EvalRunFileIdDataContentSource": { + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_id" + ], + "description": "The type of jsonl source. Always `file_id`." + }, + "id": { + "type": "string", + "description": "The identifier of the file." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataContentSource" + } + ] + }, + "OpenAI.EvalRunList": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of this object. It is always set to \"list\".", + "default": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalRun" + }, + "description": "An array of eval run objects." + }, + "first_id": { + "type": "string", + "description": "The identifier of the first eval run in the data array." + }, + "last_id": { + "type": "string", + "description": "The identifier of the last eval run in the data array." + }, + "has_more": { + "type": "boolean", + "description": "Indicates whether there are more evals available." + } + }, + "description": "An object representing a list of runs for an evaluation." + }, + "OpenAI.EvalRunOutputItem": { + "type": "object", + "required": [ + "object", + "id", + "run_id", + "eval_id", + "created_at", + "status", + "datasource_item_id", + "datasource_item", + "results", + "sample" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "eval.run.output_item" + ], + "description": "The type of the object. Always \"eval.run.output_item\".", + "default": "eval.run.output_item" + }, + "id": { + "type": "string", + "description": "Unique identifier for the evaluation run output item." + }, + "run_id": { + "type": "string", + "description": "The identifier of the evaluation run associated with this output item." + }, + "eval_id": { + "type": "string", + "description": "The identifier of the evaluation group." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) when the evaluation run was created." + }, + "status": { + "type": "string", + "description": "The status of the evaluation run." + }, + "datasource_item_id": { + "type": "integer", + "format": "int32", + "description": "The identifier for the data source item." + }, + "datasource_item": { + "type": "object", + "additionalProperties": {}, + "description": "Details of the input data source item." + }, + "results": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + }, + "description": "A list of results from the evaluation run." + }, + "sample": { + "type": "object", + "properties": { + "input": { + "type": "array", + "items": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "The role of the message sender (e.g., system, user, developer)." + }, + "content": { + "type": "string", + "description": "The content of the message." + } + }, + "required": [ + "role", + "content" + ] + }, + "description": "An array of input messages." + }, + "output": { + "type": "array", + "items": { + "type": "object", + "properties": { + "role": { + "type": "string", + "description": "The role of the message (e.g. \"system\", \"assistant\", \"user\")." + }, + "content": { + "type": "string", + "description": "The content of the message." + } + } + }, + "description": "An array of output messages." + }, + "finish_reason": { + "type": "string", + "description": "The reason why the sample generation was finished." + }, + "model": { + "type": "string", + "description": "The model used for generating the sample." + }, + "usage": { + "type": "object", + "properties": { + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens used." + }, + "completion_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of completion tokens generated." + }, + "prompt_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of prompt tokens used." + }, + "cached_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens retrieved from cache." + } + }, + "required": [ + "total_tokens", + "completion_tokens", + "prompt_tokens", + "cached_tokens" + ], + "description": "Token usage details for the sample." + }, + "error": { + "$ref": "#/components/schemas/OpenAI.EvalApiError" + }, + "temperature": { + "type": "number", + "format": "float", + "description": "The sampling temperature used." + }, + "max_completion_tokens": { + "type": "integer", + "format": "int32", + "description": "The maximum number of tokens allowed for completion." + }, + "top_p": { + "type": "number", + "format": "float", + "description": "The top_p value used for sampling." + }, + "seed": { + "type": "integer", + "format": "int32", + "description": "The seed used for generating the sample." + } + }, + "required": [ + "input", + "output", + "finish_reason", + "model", + "usage", + "error", + "temperature", + "max_completion_tokens", + "top_p", + "seed" + ], + "description": "A sample containing the input and output of the evaluation run." + } + }, + "description": "A schema representing an evaluation run output item." + }, + "OpenAI.EvalRunOutputItemList": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of this object. It is always set to \"list\".", + "default": "list" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalRunOutputItem" + }, + "description": "An array of eval run output item objects." + }, + "first_id": { + "type": "string", + "description": "The identifier of the first eval run output item in the data array." + }, + "last_id": { + "type": "string", + "description": "The identifier of the last eval run output item in the data array." + }, + "has_more": { + "type": "boolean", + "description": "Indicates whether there are more eval run output items available." + } + }, + "description": "An object representing a list of output items for an evaluation run." + }, + "OpenAI.EvalRunResponsesDataContentSource": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "responses" + ], + "description": "The type of run data source. Always `responses`." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "model": { + "type": "string", + "nullable": true, + "description": "The name of the model to find responses for. This is a query parameter used to select responses." + }, + "instructions_search": { + "type": "string", + "nullable": true, + "description": "Optional string to search the 'instructions' field. This is a query parameter used to select responses." + }, + "created_after": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0, + "description": "Only include items created after this timestamp (inclusive). This is a query parameter used to select responses." + }, + "created_before": { + "type": "integer", + "format": "int32", + "nullable": true, + "minimum": 0, + "description": "Only include items created before this timestamp (inclusive). This is a query parameter used to select responses." + }, + "reasoning_effort": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningEffort" + } + ], + "nullable": true, + "description": "Optional reasoning effort parameter. This is a query parameter used to select responses.", + "default": "medium" + }, + "temperature": { + "type": "number", + "format": "float", + "nullable": true, + "description": "Sampling temperature. This is a query parameter used to select responses." + }, + "top_p": { + "type": "number", + "format": "float", + "nullable": true, + "description": "Nucleus sampling parameter. This is a query parameter used to select responses." + }, + "users": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "List of user identifiers. This is a query parameter used to select responses." + }, + "tools": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "description": "List of tool names. This is a query parameter used to select responses." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataContentSource" + } + ], + "description": "A EvalResponsesSource object describing a run data source configuration." + }, + "OpenAI.EvalRunStoredCompletionsDataContentSource": { + "type": "object", + "required": [ + "type", + "metadata" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "stored_completions" + ], + "description": "The type of source. Always `stored_completions`." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "model": { + "type": "string", + "nullable": true, + "description": "An optional model to filter by (e.g., 'gpt-4o')." + }, + "created_after": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An optional Unix timestamp to filter items created after this time." + }, + "created_before": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An optional Unix timestamp to filter items created before this time." + }, + "limit": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An optional maximum number of items to return." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalRunDataContentSource" + } + ], + "description": "A StoredCompletionsRunDataSource configuration describing a set of filters" + }, + "OpenAI.EvalStoredCompletionsDataSourceConfigParams": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "stored_completions" + ], + "description": "The type of data source. Always `stored_completions`." + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Metadata filters for the stored completions data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigParams" + } + ], + "description": "Deprecated in favor of LogsDataSourceConfig.", + "deprecated": true + }, + "OpenAI.EvalStoredCompletionsDataSourceConfigResource": { + "type": "object", + "required": [ + "type", + "metadata", + "schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "stored_completions" + ], + "description": "The type of data source. Always `stored_completions`." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.EvalDataSourceConfigResource" + } + ], + "description": "Deprecated in favor of LogsDataSourceConfig.", + "deprecated": true + }, + "OpenAI.FileSearchTool": { + "type": "object", + "required": [ + "type", + "vector_store_ids" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_search" + ], + "description": "The type of the file search tool. Always `file_search`." + }, + "vector_store_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The IDs of the vector stores to search." + }, + "max_num_results": { + "type": "integer", + "format": "int32", + "description": "The maximum number of results to return. This number should be between 1 and 50 inclusive." + }, + "ranking_options": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.RankingOptions" + } + ], + "description": "Ranking options for search." + }, + "filters": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Filters" + } + ], + "nullable": true, + "description": "A filter to apply." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "A tool that searches for relevant content from uploaded files. Learn more about the [file search tool](https://platform.openai.com/docs/guides/tools-file-search)." + }, + "OpenAI.FileSearchToolCallItemParam": { + "type": "object", + "required": [ + "type", + "queries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_search_call" + ] + }, + "queries": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The queries used to search for files." + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "The unique ID of the file." + }, + "text": { + "type": "string", + "description": "The text that was retrieved from the file." + }, + "filename": { + "type": "string", + "description": "The name of the file." + }, + "attributes": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + }, + "score": { + "type": "number", + "format": "float", + "description": "The relevance score of the file - a value between 0 and 1." + } + } + }, + "nullable": true, + "description": "The results of the file search tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n" + }, + "OpenAI.FileSearchToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "queries" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_search_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "searching", + "completed", + "incomplete", + "failed" + ], + "description": "The status of the file search tool call. One of `in_progress`, \n`searching`, `incomplete` or `failed`," + }, + "queries": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The queries used to search for files." + }, + "results": { + "type": "array", + "items": { + "type": "object", + "properties": { + "file_id": { + "type": "string", + "description": "The unique ID of the file." + }, + "text": { + "type": "string", + "description": "The text that was retrieved from the file." + }, + "filename": { + "type": "string", + "description": "The name of the file." + }, + "attributes": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + }, + "score": { + "type": "number", + "format": "float", + "description": "The relevance score of the file - a value between 0 and 1." + } + } + }, + "nullable": true, + "description": "The results of the file search tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n" + }, + "OpenAI.Filters": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ComparisonFilter" + }, + { + "$ref": "#/components/schemas/OpenAI.CompoundFilter" + } + ] + }, + "OpenAI.FineTuneDPOHyperparameters": { + "type": "object", + "properties": { + "beta": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.", + "default": "auto" + }, + "batch_size": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.", + "default": "auto" + }, + "learning_rate_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.", + "default": "auto" + }, + "n_epochs": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.", + "default": "auto" + } + }, + "description": "The hyperparameters used for the DPO fine-tuning job." + }, + "OpenAI.FineTuneDPOMethod": { + "type": "object", + "properties": { + "hyperparameters": { + "$ref": "#/components/schemas/OpenAI.FineTuneDPOHyperparameters" + } + }, + "description": "Configuration for the DPO fine-tuning method." + }, + "OpenAI.FineTuneMethod": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "supervised", + "dpo", + "reinforcement" + ], + "description": "The type of method. Is either `supervised`, `dpo`, or `reinforcement`." + }, + "supervised": { + "$ref": "#/components/schemas/OpenAI.FineTuneSupervisedMethod" + }, + "dpo": { + "$ref": "#/components/schemas/OpenAI.FineTuneDPOMethod" + }, + "reinforcement": { + "$ref": "#/components/schemas/AzureFineTuneReinforcementMethod" + } + }, + "description": "The method used for fine-tuning." + }, + "OpenAI.FineTuneReinforcementHyperparameters": { + "type": "object", + "properties": { + "batch_size": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.", + "default": "auto" + }, + "learning_rate_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.", + "default": "auto" + }, + "n_epochs": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.", + "default": "auto" + }, + "reasoning_effort": { + "type": "string", + "enum": [ + "default", + "low", + "medium", + "high" + ], + "description": "Level of reasoning effort.", + "default": "default" + }, + "compute_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Multiplier on amount of compute used for exploring search space during training.", + "default": "auto" + }, + "eval_interval": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of training steps between evaluation runs.", + "default": "auto" + }, + "eval_samples": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "Number of evaluation samples to generate per training step.", + "default": "auto" + } + }, + "description": "The hyperparameters used for the reinforcement fine-tuning job." + }, + "OpenAI.FineTuneSupervisedHyperparameters": { + "type": "object", + "properties": { + "batch_size": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.", + "default": "auto" + }, + "learning_rate_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.", + "default": "auto" + }, + "n_epochs": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.", + "default": "auto" + } + }, + "description": "The hyperparameters used for the fine-tuning job." + }, + "OpenAI.FineTuneSupervisedMethod": { + "type": "object", + "properties": { + "hyperparameters": { + "$ref": "#/components/schemas/OpenAI.FineTuneSupervisedHyperparameters" + } + }, + "description": "Configuration for the supervised fine-tuning method." + }, + "OpenAI.FineTuningIntegration": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "wandb" + ] + } + ] + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "wandb": "#/components/schemas/OpenAI.FineTuningIntegrationWandb" + } + } + }, + "OpenAI.FineTuningIntegrationWandb": { + "type": "object", + "required": [ + "type", + "wandb" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "wandb" + ], + "description": "The type of the integration being enabled for the fine-tuning job" + }, + "wandb": { + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The name of the project that the new run will be created under." + }, + "name": { + "type": "string", + "nullable": true, + "description": "A display name to set for the run. If not set, we will use the Job ID as the name." + }, + "entity": { + "type": "string", + "nullable": true, + "description": "The entity to use for the run. This allows you to set the team or username of the WandB user that you would\nlike associated with the run. If not set, the default entity for the registered WandB API key is used." + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some\ndefault tags are generated by OpenAI: \"openai/finetune\", \"openai/{base-model}\", \"openai/{ftjob-abcdef}\"." + } + }, + "required": [ + "project" + ], + "description": "The settings for your integration with Weights and Biases. This payload specifies the project that\nmetrics will be sent to. Optionally, you can set an explicit display name for your run, add tags\nto your run, and set a default entity (team, username, etc) to be associated with your run." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.FineTuningIntegration" + } + ] + }, + "OpenAI.FineTuningJob": { + "type": "object", + "required": [ + "id", + "created_at", + "error", + "fine_tuned_model", + "finished_at", + "hyperparameters", + "model", + "object", + "organization_id", + "result_files", + "status", + "trained_tokens", + "training_file", + "validation_file", + "seed", + "metadata" + ], + "properties": { + "user_provided_suffix": { + "type": "string", + "nullable": true, + "description": "The descriptive suffix applied to the job, as specified in the job creation request." + }, + "id": { + "type": "string", + "description": "The object identifier, which can be referenced in the API endpoints." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was created." + }, + "error": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "A machine-readable error code." + }, + "message": { + "type": "string", + "description": "A human-readable error message." + }, + "param": { + "type": "string", + "nullable": true, + "description": "The parameter that was invalid, usually `training_file` or `validation_file`. This field will be null if the failure was not parameter-specific." + } + }, + "required": [ + "code", + "message", + "param" + ], + "nullable": true, + "description": "For fine-tuning jobs that have `failed`, this will contain more information on the cause of the failure." + }, + "fine_tuned_model": { + "type": "string", + "nullable": true, + "description": "The name of the fine-tuned model that is being created. The value will be null if the fine-tuning job is still running." + }, + "finished_at": { + "type": "integer", + "format": "unixtime", + "nullable": true, + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was finished. The value will be null if the fine-tuning job is still running." + }, + "hyperparameters": { + "type": "object", + "properties": { + "batch_size": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "nullable": true, + "description": "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.", + "default": "auto" + }, + "learning_rate_multiplier": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "number", + "format": "float" + } + ], + "description": "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.", + "default": "auto" + }, + "n_epochs": { + "anyOf": [ + { + "type": "string", + "enum": [ + "auto" + ] + }, + { + "type": "integer", + "format": "int32" + } + ], + "description": "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.", + "default": "auto" + } + }, + "description": "The hyperparameters used for the fine-tuning job. This value will only be returned when running `supervised` jobs." + }, + "model": { + "type": "string", + "description": "The base model that is being fine-tuned." + }, + "object": { + "type": "string", + "enum": [ + "fine_tuning.job" + ], + "description": "The object type, which is always \"fine_tuning.job\"." + }, + "organization_id": { + "type": "string", + "description": "The organization that owns the fine-tuning job." + }, + "result_files": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The compiled results file ID(s) for the fine-tuning job. You can retrieve the results with the [Files API](/docs/api-reference/files/retrieve-contents)." + }, + "status": { + "type": "string", + "enum": [ + "validating_files", + "queued", + "running", + "succeeded", + "failed", + "cancelled" + ], + "description": "The current status of the fine-tuning job, which can be either `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`." + }, + "trained_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "The total number of billable tokens processed by this fine-tuning job. The value will be null if the fine-tuning job is still running." + }, + "training_file": { + "type": "string", + "description": "The file ID used for training. You can retrieve the training data with the [Files API](/docs/api-reference/files/retrieve-contents)." + }, + "validation_file": { + "type": "string", + "nullable": true, + "description": "The file ID used for validation. You can retrieve the validation results with the [Files API](/docs/api-reference/files/retrieve-contents)." + }, + "integrations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.FineTuningIntegration" + }, + "nullable": true, + "description": "A list of integrations to enable for this fine-tuning job." + }, + "seed": { + "type": "integer", + "format": "int32", + "description": "The seed used for the fine-tuning job." + }, + "estimated_finish": { + "type": "integer", + "format": "unixtime", + "nullable": true, + "description": "The Unix timestamp (in seconds) for when the fine-tuning job is estimated to finish. The value will be null if the fine-tuning job is not running." + }, + "method": { + "$ref": "#/components/schemas/OpenAI.FineTuneMethod" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + }, + "description": "The `fine_tuning.job` object represents a fine-tuning job that has been created through the API." + }, + "OpenAI.FineTuningJobCheckpoint": { + "type": "object", + "required": [ + "id", + "created_at", + "fine_tuned_model_checkpoint", + "step_number", + "metrics", + "fine_tuning_job_id", + "object" + ], + "properties": { + "id": { + "type": "string", + "description": "The checkpoint identifier, which can be referenced in the API endpoints." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the checkpoint was created." + }, + "fine_tuned_model_checkpoint": { + "type": "string", + "description": "The name of the fine-tuned checkpoint model that is created." + }, + "step_number": { + "type": "integer", + "format": "int32", + "description": "The step number that the checkpoint was created at." + }, + "metrics": { + "type": "object", + "properties": { + "step": { + "type": "number", + "format": "float" + }, + "train_loss": { + "type": "number", + "format": "float" + }, + "train_mean_token_accuracy": { + "type": "number", + "format": "float" + }, + "valid_loss": { + "type": "number", + "format": "float" + }, + "valid_mean_token_accuracy": { + "type": "number", + "format": "float" + }, + "full_valid_loss": { + "type": "number", + "format": "float" + }, + "full_valid_mean_token_accuracy": { + "type": "number", + "format": "float" + } + }, + "description": "Metrics at the step number during the fine-tuning job." + }, + "fine_tuning_job_id": { + "type": "string", + "description": "The name of the fine-tuning job that this checkpoint was created from." + }, + "object": { + "type": "string", + "enum": [ + "fine_tuning.job.checkpoint" + ], + "description": "The object type, which is always \"fine_tuning.job.checkpoint\"." + } + }, + "description": "The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use." + }, + "OpenAI.FineTuningJobEvent": { + "type": "object", + "required": [ + "object", + "id", + "created_at", + "level", + "message" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "fine_tuning.job.event" + ], + "description": "The object type, which is always \"fine_tuning.job.event\"." + }, + "id": { + "type": "string", + "description": "The object identifier." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the fine-tuning job was created." + }, + "level": { + "type": "string", + "enum": [ + "info", + "warn", + "error" + ], + "description": "The log level of the event." + }, + "message": { + "type": "string", + "description": "The message of the event." + }, + "type": { + "type": "string", + "enum": [ + "message", + "metrics" + ], + "description": "The type of event." + }, + "data": { + "description": "The data associated with the event." + } + }, + "description": "Fine-tuning job event object" + }, + "OpenAI.FunctionObject": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "description": { + "type": "string", + "description": "A description of what the function does, used by the model to choose when and how to call the function." + }, + "name": { + "type": "string", + "description": "The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64." + }, + "parameters": { + "description": "The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.\n\nOmitting `parameters` defines a function with an empty parameter list." + }, + "strict": { + "type": "boolean", + "nullable": true, + "description": "Whether to enable strict schema adherence when generating the function call. If set to true, the model will follow the exact schema defined in the `parameters` field. Only a subset of JSON Schema is supported when `strict` is `true`. Learn more about Structured Outputs in the [function calling guide](docs/guides/function-calling).", + "default": false + } + } + }, + "OpenAI.FunctionTool": { + "type": "object", + "required": [ + "type", + "name", + "parameters", + "strict" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "The type of the function tool. Always `function`." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + }, + "description": { + "type": "string", + "nullable": true, + "description": "A description of the function. Used by the model to determine whether or not to call the function." + }, + "parameters": { + "nullable": true, + "description": "A JSON schema object describing the parameters of the function." + }, + "strict": { + "type": "boolean", + "nullable": true, + "description": "Whether to enforce strict parameter validation. Default `true`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "Defines a function in your own code the model can choose to call. Learn more about [function calling](https://platform.openai.com/docs/guides/function-calling)." + }, + "OpenAI.FunctionToolCallItemParam": { + "type": "object", + "required": [ + "type", + "call_id", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function_call" + ] + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model." + }, + "name": { + "type": "string", + "description": "The name of the function to run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of the arguments to pass to the function." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n" + }, + "OpenAI.FunctionToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "call_id", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ], + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model." + }, + "name": { + "type": "string", + "description": "The name of the function to run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of the arguments to pass to the function." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n" + }, + "OpenAI.FunctionToolCallOutputItemParam": { + "type": "object", + "required": [ + "type", + "call_id", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function_call_output" + ] + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model." + }, + "output": { + "type": "string", + "description": "A JSON string of the output of the function tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "The output of a function tool call.\n" + }, + "OpenAI.FunctionToolCallOutputItemResource": { + "type": "object", + "required": [ + "type", + "status", + "call_id", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function_call_output" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ], + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + }, + "call_id": { + "type": "string", + "description": "The unique ID of the function tool call generated by the model." + }, + "output": { + "type": "string", + "description": "A JSON string of the output of the function tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The output of a function tool call.\n" + }, + "OpenAI.Grader": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.GraderType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "label_model": "#/components/schemas/OpenAI.GraderLabelModel", + "text_similarity": "#/components/schemas/OpenAI.GraderTextSimilarity", + "python": "#/components/schemas/OpenAI.GraderPython", + "score_model": "#/components/schemas/OpenAI.GraderScoreModel", + "multi": "#/components/schemas/OpenAI.GraderMulti" + } + } + }, + "OpenAI.GraderLabelModel": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input", + "labels", + "passing_labels" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "label_model" + ], + "description": "The object type, which is always `label_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation. Must support structured outputs." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalItem" + } + }, + "labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels to assign to each item in the evaluation." + }, + "passing_labels": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The labels that indicate a passing result. Must be a subset of labels." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation." + }, + "OpenAI.GraderMulti": { + "type": "object", + "required": [ + "type", + "name", + "graders", + "calculate_output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "multi" + ], + "description": "The object type, which is always `multi`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "graders": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.GraderStringCheck" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderTextSimilarity" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderScoreModel" + } + ] + } + }, + "calculate_output": { + "type": "string", + "description": "A formula to calculate the output based on grader results." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A MultiGrader object combines the output of multiple graders to produce a single score." + }, + "OpenAI.GraderPython": { + "type": "object", + "required": [ + "type", + "name", + "source" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "python" + ], + "description": "The object type, which is always `python`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "source": { + "type": "string", + "description": "The source code of the python script." + }, + "image_tag": { + "type": "string", + "description": "The image tag to use for the python script." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A PythonGrader object that runs a python script on the input." + }, + "OpenAI.GraderScoreModel": { + "type": "object", + "required": [ + "type", + "name", + "model", + "input" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "score_model" + ], + "description": "The object type, which is always `score_model`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "model": { + "type": "string", + "description": "The model to use for the evaluation." + }, + "sampling_params": { + "description": "The sampling parameters for the model." + }, + "input": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.EvalItem" + }, + "description": "The input text. This may include template strings." + }, + "range": { + "type": "array", + "items": { + "type": "number", + "format": "float" + }, + "description": "The range of the score. Defaults to `[0, 1]`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A ScoreModelGrader object that uses a model to assign a score to the input." + }, + "OpenAI.GraderStringCheck": { + "type": "object", + "required": [ + "type", + "name", + "input", + "reference", + "operation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "string_check" + ], + "description": "The object type, which is always `string_check`." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "input": { + "type": "string", + "description": "The input text. This may include template strings." + }, + "reference": { + "type": "string", + "description": "The reference text. This may include template strings." + }, + "operation": { + "type": "string", + "enum": [ + "eq", + "ne", + "like", + "ilike" + ], + "description": "The string check operation to perform. One of `eq`, `ne`, `like`, or `ilike`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A StringCheckGrader object that performs a string comparison between input and reference using a specified operation." + }, + "OpenAI.GraderTextSimilarity": { + "type": "object", + "required": [ + "type", + "name", + "input", + "reference", + "evaluation_metric" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text_similarity" + ], + "description": "The type of grader." + }, + "name": { + "type": "string", + "description": "The name of the grader." + }, + "input": { + "type": "string", + "description": "The text being graded." + }, + "reference": { + "type": "string", + "description": "The text being graded against." + }, + "evaluation_metric": { + "type": "string", + "enum": [ + "fuzzy_match", + "bleu", + "gleu", + "meteor", + "rouge_1", + "rouge_2", + "rouge_3", + "rouge_4", + "rouge_5", + "rouge_l" + ], + "description": "The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`, `meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or `rouge_l`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Grader" + } + ], + "description": "A TextSimilarityGrader object which grades text based on similarity metrics." + }, + "OpenAI.GraderType": { + "type": "string", + "enum": [ + "string_check", + "text_similarity", + "score_model", + "label_model", + "python", + "multi" + ] + }, + "OpenAI.ImageGenTool": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_generation" + ], + "description": "The type of the image generation tool. Always `image_generation`." + }, + "model": { + "type": "string", + "enum": [ + "gpt-image-1" + ], + "description": "The image generation model to use. Default: `gpt-image-1`.", + "default": "gpt-image-1" + }, + "quality": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "auto" + ], + "description": "The quality of the generated image. One of `low`, `medium`, `high`,\nor `auto`. Default: `auto`.", + "default": "auto" + }, + "size": { + "type": "string", + "enum": [ + "1024x1024", + "1024x1536", + "1536x1024", + "auto" + ], + "description": "The size of the generated image. One of `1024x1024`, `1024x1536`,\n`1536x1024`, or `auto`. Default: `auto`.", + "default": "auto" + }, + "output_format": { + "type": "string", + "enum": [ + "png", + "webp", + "jpeg" + ], + "description": "The output format of the generated image. One of `png`, `webp`, or\n`jpeg`. Default: `png`.", + "default": "png" + }, + "output_compression": { + "type": "integer", + "format": "int32", + "minimum": 0, + "maximum": 100, + "description": "Compression level for the output image. Default: 100.", + "default": 100 + }, + "moderation": { + "type": "string", + "enum": [ + "auto", + "low" + ], + "description": "Moderation level for the generated image. Default: `auto`.", + "default": "auto" + }, + "background": { + "type": "string", + "enum": [ + "transparent", + "opaque", + "auto" + ], + "description": "Background type for the generated image. One of `transparent`,\n`opaque`, or `auto`. Default: `auto`.", + "default": "auto" + }, + "input_image_mask": { + "type": "object", + "properties": { + "image_url": { + "type": "string", + "description": "Base64-encoded mask image." + }, + "file_id": { + "type": "string", + "description": "File ID for the mask image." + } + }, + "description": "Optional mask for inpainting. Contains `image_url`\n(string, optional) and `file_id` (string, optional)." + }, + "partial_images": { + "type": "integer", + "format": "int32", + "minimum": 0, + "maximum": 3, + "description": "Number of partial images to generate in streaming mode, from 0 (default value) to 3.", + "default": 0 + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "A tool that generates images using a model like `gpt-image-1`." + }, + "OpenAI.ImageGenToolCallItemParam": { + "type": "object", + "required": [ + "type", + "result" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_generation_call" + ] + }, + "result": { + "type": "string", + "nullable": true, + "description": "The generated image encoded in base64." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "An image generation request made by the model.\n" + }, + "OpenAI.ImageGenToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "result" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_generation_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "generating", + "failed" + ] + }, + "result": { + "type": "string", + "nullable": true, + "description": "The generated image encoded in base64." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "An image generation request made by the model.\n" + }, + "OpenAI.ImplicitUserMessage": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + } + ] + } + } + }, + "OpenAI.Includable": { + "type": "string", + "enum": [ + "code_interpreter_call.outputs", + "computer_call_output.output.image_url", + "file_search_call.results", + "message.input_image.image_url", + "message.output_text.logprobs", + "reasoning.encrypted_content" + ], + "description": "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `code_interpreter_call.outputs`: Includes the outputs of python code execution\n in code interpreter tool call items.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n- `file_search_call.results`: Include the search results of\n the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `message.output_text.logprobs`: Include logprobs with assistant messages.\n- `reasoning.encrypted_content`: Includes an encrypted version of reasoning\n tokens in reasoning item outputs. This enables reasoning items to be used in\n multi-turn conversations when using the Responses API statelessly (like\n when the `store` parameter is set to `false`, or when an organization is\n enrolled in the zero data retention program)." + }, + "OpenAI.ItemContent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ItemContentType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "input_audio": "#/components/schemas/OpenAI.ItemContentInputAudio", + "output_audio": "#/components/schemas/OpenAI.ItemContentOutputAudio", + "refusal": "#/components/schemas/OpenAI.ItemContentRefusal", + "input_text": "#/components/schemas/OpenAI.ItemContentInputText", + "input_image": "#/components/schemas/OpenAI.ItemContentInputImage", + "input_file": "#/components/schemas/OpenAI.ItemContentInputFile", + "output_text": "#/components/schemas/OpenAI.ItemContentOutputText" + } + } + }, + "OpenAI.ItemContentInputAudio": { + "type": "object", + "required": [ + "type", + "data", + "format" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_audio" + ], + "description": "The type of the input item. Always `input_audio`." + }, + "data": { + "type": "string", + "description": "Base64-encoded audio data." + }, + "format": { + "type": "string", + "enum": [ + "mp3", + "wav" + ], + "description": "The format of the audio data. Currently supported formats are `mp3` and\n`wav`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "An audio input to the model." + }, + "OpenAI.ItemContentInputFile": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_file" + ], + "description": "The type of the input item. Always `input_file`." + }, + "file_id": { + "type": "string", + "nullable": true, + "description": "The ID of the file to be sent to the model." + }, + "filename": { + "type": "string", + "description": "The name of the file to be sent to the model." + }, + "file_data": { + "type": "string", + "description": "The content of the file to be sent to the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "A file input to the model." + }, + "OpenAI.ItemContentInputImage": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_image" + ], + "description": "The type of the input item. Always `input_image`." + }, + "image_url": { + "type": "string", + "nullable": true, + "description": "The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image in a data URL." + }, + "file_id": { + "type": "string", + "nullable": true, + "description": "The ID of the file to be sent to the model." + }, + "detail": { + "type": "string", + "enum": [ + "low", + "high", + "auto" + ], + "description": "The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.", + "default": "auto" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "An image input to the model. Learn about [image inputs](/docs/guides/vision)." + }, + "OpenAI.ItemContentInputText": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "input_text" + ], + "description": "The type of the input item. Always `input_text`." + }, + "text": { + "type": "string", + "description": "The text input to the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "A text input to the model." + }, + "OpenAI.ItemContentOutputAudio": { + "type": "object", + "required": [ + "type", + "data", + "transcript" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "output_audio" + ], + "description": "The type of the output audio. Always `output_audio`." + }, + "data": { + "type": "string", + "description": "Base64-encoded audio data from the model." + }, + "transcript": { + "type": "string", + "description": "The transcript of the audio data from the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "An audio output from the model." + }, + "OpenAI.ItemContentOutputText": { + "type": "object", + "required": [ + "type", + "text", + "annotations" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "output_text" + ], + "description": "The type of the output text. Always `output_text`." + }, + "text": { + "type": "string", + "description": "The text output from the model." + }, + "annotations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Annotation" + }, + "description": "The annotations of the text output." + }, + "logprobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.LogProb" + } + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "A text output from the model." + }, + "OpenAI.ItemContentRefusal": { + "type": "object", + "required": [ + "type", + "refusal" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "refusal" + ], + "description": "The type of the refusal. Always `refusal`." + }, + "refusal": { + "type": "string", + "description": "The refusal explanationfrom the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "A refusal from the model." + }, + "OpenAI.ItemContentType": { + "type": "string", + "enum": [ + "input_text", + "input_audio", + "input_image", + "input_file", + "output_text", + "output_audio", + "refusal" + ], + "description": "Multi-modal input and output contents." + }, + "OpenAI.ItemParam": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ItemType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "file_search_call": "#/components/schemas/OpenAI.FileSearchToolCallItemParam", + "computer_call": "#/components/schemas/OpenAI.ComputerToolCallItemParam", + "computer_call_output": "#/components/schemas/OpenAI.ComputerToolCallOutputItemParam", + "web_search_call": "#/components/schemas/OpenAI.WebSearchToolCallItemParam", + "function_call": "#/components/schemas/OpenAI.FunctionToolCallItemParam", + "function_call_output": "#/components/schemas/OpenAI.FunctionToolCallOutputItemParam", + "reasoning": "#/components/schemas/OpenAI.ReasoningItemParam", + "item_reference": "#/components/schemas/OpenAI.ItemReferenceItemParam", + "image_generation_call": "#/components/schemas/OpenAI.ImageGenToolCallItemParam", + "code_interpreter_call": "#/components/schemas/OpenAI.CodeInterpreterToolCallItemParam", + "local_shell_call": "#/components/schemas/OpenAI.LocalShellToolCallItemParam", + "local_shell_call_output": "#/components/schemas/OpenAI.LocalShellToolCallOutputItemParam", + "mcp_list_tools": "#/components/schemas/OpenAI.MCPListToolsItemParam", + "mcp_approval_request": "#/components/schemas/OpenAI.MCPApprovalRequestItemParam", + "mcp_approval_response": "#/components/schemas/OpenAI.MCPApprovalResponseItemParam", + "mcp_call": "#/components/schemas/OpenAI.MCPCallItemParam", + "message": "#/components/schemas/OpenAI.ResponsesMessageItemParam" + } + }, + "description": "Content item used to generate a response." + }, + "OpenAI.ItemReferenceItemParam": { + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "item_reference" + ] + }, + "id": { + "type": "string", + "description": "The service-originated ID of the previously generated response item being referenced." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "An internal identifier for an item to reference." + }, + "OpenAI.ItemResource": { + "type": "object", + "required": [ + "type", + "id" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ItemType" + }, + "id": { + "type": "string" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "file_search_call": "#/components/schemas/OpenAI.FileSearchToolCallItemResource", + "computer_call": "#/components/schemas/OpenAI.ComputerToolCallItemResource", + "computer_call_output": "#/components/schemas/OpenAI.ComputerToolCallOutputItemResource", + "web_search_call": "#/components/schemas/OpenAI.WebSearchToolCallItemResource", + "function_call": "#/components/schemas/OpenAI.FunctionToolCallItemResource", + "function_call_output": "#/components/schemas/OpenAI.FunctionToolCallOutputItemResource", + "reasoning": "#/components/schemas/OpenAI.ReasoningItemResource", + "image_generation_call": "#/components/schemas/OpenAI.ImageGenToolCallItemResource", + "code_interpreter_call": "#/components/schemas/OpenAI.CodeInterpreterToolCallItemResource", + "local_shell_call": "#/components/schemas/OpenAI.LocalShellToolCallItemResource", + "local_shell_call_output": "#/components/schemas/OpenAI.LocalShellToolCallOutputItemResource", + "mcp_list_tools": "#/components/schemas/OpenAI.MCPListToolsItemResource", + "mcp_approval_request": "#/components/schemas/OpenAI.MCPApprovalRequestItemResource", + "mcp_approval_response": "#/components/schemas/OpenAI.MCPApprovalResponseItemResource", + "mcp_call": "#/components/schemas/OpenAI.MCPCallItemResource", + "message": "#/components/schemas/OpenAI.ResponsesMessageItemResource" + } + }, + "description": "Content item used to generate a response." + }, + "OpenAI.ItemType": { + "type": "string", + "enum": [ + "message", + "file_search_call", + "function_call", + "function_call_output", + "computer_call", + "computer_call_output", + "web_search_call", + "reasoning", + "item_reference", + "image_generation_call", + "code_interpreter_call", + "local_shell_call", + "local_shell_call_output", + "mcp_list_tools", + "mcp_approval_request", + "mcp_approval_response", + "mcp_call" + ] + }, + "OpenAI.ListFineTuningJobCheckpointsResponse": { + "type": "object", + "required": [ + "data", + "object", + "has_more" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.FineTuningJobCheckpoint" + } + }, + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "first_id": { + "type": "string", + "nullable": true + }, + "last_id": { + "type": "string", + "nullable": true + }, + "has_more": { + "type": "boolean" + } + } + }, + "OpenAI.ListFineTuningJobEventsResponse": { + "type": "object", + "required": [ + "data", + "object", + "has_more" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.FineTuningJobEvent" + } + }, + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "has_more": { + "type": "boolean" + } + } + }, + "OpenAI.ListModelsResponse": { + "type": "object", + "required": [ + "object", + "data" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Model" + } + } + } + }, + "OpenAI.ListPaginatedFineTuningJobsResponse": { + "type": "object", + "required": [ + "data", + "has_more", + "object" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.FineTuningJob" + } + }, + "has_more": { + "type": "boolean" + }, + "object": { + "type": "string", + "enum": [ + "list" + ] + } + } + }, + "OpenAI.ListVectorStoreFilesFilter": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "in_progress", + "completed", + "failed", + "cancelled" + ] + } + ] + }, + "OpenAI.ListVectorStoreFilesResponse": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean" + } + } + }, + "OpenAI.ListVectorStoresResponse": { + "type": "object", + "required": [ + "object", + "data", + "first_id", + "last_id", + "has_more" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.VectorStoreObject" + } + }, + "first_id": { + "type": "string" + }, + "last_id": { + "type": "string" + }, + "has_more": { + "type": "boolean" + } + } + }, + "OpenAI.LocalShellExecAction": { + "type": "object", + "required": [ + "type", + "command", + "env" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "exec" + ], + "description": "The type of the local shell action. Always `exec`." + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The command to run." + }, + "timeout_ms": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "Optional timeout in milliseconds for the command." + }, + "working_directory": { + "type": "string", + "nullable": true, + "description": "Optional working directory to run the command in." + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables to set for the command." + }, + "user": { + "type": "string", + "nullable": true, + "description": "Optional user to run the command as." + } + }, + "description": "Execute a shell command on the server." + }, + "OpenAI.LocalShellTool": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "local_shell" + ], + "description": "The type of the local shell tool. Always `local_shell`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "A tool that allows the model to execute shell commands in a local environment." + }, + "OpenAI.LocalShellToolCallItemParam": { + "type": "object", + "required": [ + "type", + "call_id", + "action" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "local_shell_call" + ] + }, + "call_id": { + "type": "string", + "description": "The unique ID of the local shell tool call generated by the model." + }, + "action": { + "$ref": "#/components/schemas/OpenAI.LocalShellExecAction" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A tool call to run a command on the local shell.\n" + }, + "OpenAI.LocalShellToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "call_id", + "action" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "local_shell_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + }, + "call_id": { + "type": "string", + "description": "The unique ID of the local shell tool call generated by the model." + }, + "action": { + "$ref": "#/components/schemas/OpenAI.LocalShellExecAction" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A tool call to run a command on the local shell.\n" + }, + "OpenAI.LocalShellToolCallOutputItemParam": { + "type": "object", + "required": [ + "type", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "local_shell_call_output" + ] + }, + "output": { + "type": "string", + "description": "A JSON string of the output of the local shell tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "The output of a local shell tool call.\n" + }, + "OpenAI.LocalShellToolCallOutputItemResource": { + "type": "object", + "required": [ + "type", + "status", + "output" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "local_shell_call_output" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ] + }, + "output": { + "type": "string", + "description": "A JSON string of the output of the local shell tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The output of a local shell tool call.\n" + }, + "OpenAI.Location": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.LocationType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "approximate": "#/components/schemas/OpenAI.ApproximateLocation" + } + } + }, + "OpenAI.LocationType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "approximate" + ] + } + ] + }, + "OpenAI.LogProb": { + "type": "object", + "required": [ + "token", + "logprob", + "bytes", + "top_logprobs" + ], + "properties": { + "token": { + "type": "string" + }, + "logprob": { + "type": "number", + "format": "float" + }, + "bytes": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "top_logprobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.TopLogProb" + } + } + }, + "description": "The log probability of a token." + }, + "OpenAI.MCPApprovalRequestItemParam": { + "type": "object", + "required": [ + "type", + "server_label", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_approval_request" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server making the request." + }, + "name": { + "type": "string", + "description": "The name of the tool to run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of arguments for the tool." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A request for human approval of a tool invocation.\n" + }, + "OpenAI.MCPApprovalRequestItemResource": { + "type": "object", + "required": [ + "type", + "server_label", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_approval_request" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server making the request." + }, + "name": { + "type": "string", + "description": "The name of the tool to run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of arguments for the tool." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A request for human approval of a tool invocation.\n" + }, + "OpenAI.MCPApprovalResponseItemParam": { + "type": "object", + "required": [ + "type", + "approval_request_id", + "approve" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_approval_response" + ] + }, + "approval_request_id": { + "type": "string", + "description": "The ID of the approval request being answered." + }, + "approve": { + "type": "boolean", + "description": "Whether the request was approved." + }, + "reason": { + "type": "string", + "nullable": true, + "description": "Optional reason for the decision." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A response to an MCP approval request.\n" + }, + "OpenAI.MCPApprovalResponseItemResource": { + "type": "object", + "required": [ + "type", + "approval_request_id", + "approve" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_approval_response" + ] + }, + "approval_request_id": { + "type": "string", + "description": "The ID of the approval request being answered." + }, + "approve": { + "type": "boolean", + "description": "Whether the request was approved." + }, + "reason": { + "type": "string", + "nullable": true, + "description": "Optional reason for the decision." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A response to an MCP approval request.\n" + }, + "OpenAI.MCPCallItemParam": { + "type": "object", + "required": [ + "type", + "server_label", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_call" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server running the tool." + }, + "name": { + "type": "string", + "description": "The name of the tool that was run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of the arguments passed to the tool." + }, + "output": { + "type": "string", + "nullable": true, + "description": "The output from the tool call." + }, + "error": { + "type": "string", + "nullable": true, + "description": "The error from the tool call, if any." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "An invocation of a tool on an MCP server.\n" + }, + "OpenAI.MCPCallItemResource": { + "type": "object", + "required": [ + "type", + "server_label", + "name", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_call" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server running the tool." + }, + "name": { + "type": "string", + "description": "The name of the tool that was run." + }, + "arguments": { + "type": "string", + "description": "A JSON string of the arguments passed to the tool." + }, + "output": { + "type": "string", + "nullable": true, + "description": "The output from the tool call." + }, + "error": { + "type": "string", + "nullable": true, + "description": "The error from the tool call, if any." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "An invocation of a tool on an MCP server.\n" + }, + "OpenAI.MCPListToolsItemParam": { + "type": "object", + "required": [ + "type", + "server_label", + "tools" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_list_tools" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server." + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.MCPListToolsTool" + }, + "description": "The tools available on the server." + }, + "error": { + "type": "string", + "nullable": true, + "description": "Error message if the server could not list tools." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A list of tools available on an MCP server.\n" + }, + "OpenAI.MCPListToolsItemResource": { + "type": "object", + "required": [ + "type", + "server_label", + "tools" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp_list_tools" + ] + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server." + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.MCPListToolsTool" + }, + "description": "The tools available on the server." + }, + "error": { + "type": "string", + "nullable": true, + "description": "Error message if the server could not list tools." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A list of tools available on an MCP server.\n" + }, + "OpenAI.MCPListToolsTool": { + "type": "object", + "required": [ + "name", + "input_schema" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the tool." + }, + "description": { + "type": "string", + "nullable": true, + "description": "The description of the tool." + }, + "input_schema": { + "description": "The JSON schema describing the tool's input." + }, + "annotations": { + "nullable": true, + "description": "Additional annotations about the tool." + } + }, + "description": "A tool available on an MCP server." + }, + "OpenAI.MCPTool": { + "type": "object", + "required": [ + "type", + "server_label", + "server_url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp" + ], + "description": "The type of the MCP tool. Always `mcp`." + }, + "server_label": { + "type": "string", + "description": "A label for this MCP server, used to identify it in tool calls." + }, + "server_url": { + "type": "string", + "description": "The URL for the MCP server." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Optional HTTP headers to send to the MCP server. Use for authentication\nor other purposes." + }, + "allowed_tools": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object", + "properties": { + "tool_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of allowed tool names." + } + } + } + ], + "nullable": true, + "description": "List of allowed tool names or a filter object." + }, + "require_approval": { + "anyOf": [ + { + "type": "object", + "properties": { + "always": { + "type": "object", + "properties": { + "tool_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of tools that require approval." + } + }, + "description": "A list of tools that always require approval." + }, + "never": { + "type": "object", + "properties": { + "tool_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of tools that do not require approval." + } + }, + "description": "A list of tools that never require approval." + } + } + }, + { + "type": "string", + "enum": [ + "always", + "never" + ] + } + ], + "nullable": true, + "description": "Specify which of the MCP server's tools require approval.", + "default": "always" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "Give the model access to additional tools via remote Model Context Protocol\n(MCP) servers. [Learn more about MCP](/docs/guides/tools-remote-mcp)." + }, + "OpenAI.MetadataPropertyForRequest": { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters." + }, + "OpenAI.Model": { + "type": "object", + "required": [ + "id", + "created", + "object", + "owned_by" + ], + "properties": { + "id": { + "type": "string", + "description": "The model identifier, which can be referenced in the API endpoints." + }, + "created": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) when the model was created." + }, + "object": { + "type": "string", + "enum": [ + "model" + ], + "description": "The object type, which is always \"model\"." + }, + "owned_by": { + "type": "string", + "description": "The organization that owns the model." + } + }, + "description": "Describes an OpenAI model offering that can be used with the API." + }, + "OpenAI.OtherChunkingStrategyResponseParam": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ], + "description": "Always `other`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyResponseParam" + } + ], + "description": "This is returned when the chunking strategy is unknown. Typically, this is because the file was indexed before the `chunking_strategy` concept was introduced in the API." + }, + "OpenAI.ParallelToolCalls": { + "type": "boolean", + "description": "Whether to enable [parallel function calling](/docs/guides/function-calling#configuring-parallel-function-calling) during tool use." + }, + "OpenAI.Prompt": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "description": "The unique identifier of the prompt template to use." + }, + "version": { + "type": "string", + "nullable": true, + "description": "Optional version of the prompt template." + }, + "variables": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsePromptVariables" + } + ], + "nullable": true + } + }, + "description": "Reference to a prompt template and its variables.\n[Learn more](/docs/guides/text?api-mode=responses#reusable-prompts)." + }, + "OpenAI.RankingOptions": { + "type": "object", + "properties": { + "ranker": { + "type": "string", + "enum": [ + "auto", + "default-2024-11-15" + ], + "description": "The ranker to use for the file search." + }, + "score_threshold": { + "type": "number", + "format": "float", + "description": "The score threshold for the file search, a number between 0 and 1. Numbers closer to 1 will attempt to return only the most relevant results, but may return fewer results." + } + } + }, + "OpenAI.Reasoning": { + "type": "object", + "properties": { + "effort": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningEffort" + } + ], + "nullable": true, + "default": "medium" + }, + "summary": { + "type": "string", + "enum": [ + "auto", + "concise", + "detailed" + ], + "nullable": true, + "description": "A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`." + }, + "generate_summary": { + "type": "string", + "enum": [ + "auto", + "concise", + "detailed" + ], + "nullable": true, + "description": "**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.", + "deprecated": true, + "default": null + } + }, + "description": "**o-series models only**\n\nConfiguration options for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning)." + }, + "OpenAI.ReasoningEffort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "description": "**o-series models only**\n\nConstrains effort on reasoning for\n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response." + }, + "OpenAI.ReasoningItemParam": { + "type": "object", + "required": [ + "type", + "summary" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "reasoning" + ] + }, + "encrypted_content": { + "type": "string", + "nullable": true, + "description": "The encrypted content of the reasoning item - populated when a response is\ngenerated with `reasoning.encrypted_content` in the `include` parameter." + }, + "summary": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPart" + }, + "description": "Reasoning text contents." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](/docs/guides/conversation-state).\n" + }, + "OpenAI.ReasoningItemResource": { + "type": "object", + "required": [ + "type", + "summary" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "reasoning" + ] + }, + "encrypted_content": { + "type": "string", + "nullable": true, + "description": "The encrypted content of the reasoning item - populated when a response is\ngenerated with `reasoning.encrypted_content` in the `include` parameter." + }, + "summary": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPart" + }, + "description": "Reasoning text contents." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A description of the chain of thought used by a reasoning model while generating\na response. Be sure to include these items in your `input` to the Responses API\nfor subsequent turns of a conversation if you are manually\n[managing context](/docs/guides/conversation-state).\n" + }, + "OpenAI.ReasoningItemSummaryPart": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPartType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "summary_text": "#/components/schemas/OpenAI.ReasoningItemSummaryTextPart" + } + } + }, + "OpenAI.ReasoningItemSummaryPartType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "summary_text" + ] + } + ] + }, + "OpenAI.ReasoningItemSummaryTextPart": { + "type": "object", + "required": [ + "type", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "summary_text" + ] + }, + "text": { + "type": "string" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPart" + } + ] + }, + "OpenAI.Response": { + "type": "object", + "required": [ + "metadata", + "temperature", + "top_p", + "user", + "id", + "object", + "created_at", + "error", + "incomplete_details", + "output", + "instructions", + "parallel_tool_calls" + ], + "properties": { + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + }, + "temperature": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 2, + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both." + }, + "top_p": { + "type": "number", + "format": "float", + "nullable": true, + "minimum": 0, + "maximum": 1, + "description": "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both." + }, + "user": { + "type": "string", + "nullable": true, + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids)." + }, + "top_logprobs": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability." + }, + "previous_response_id": { + "type": "string", + "nullable": true, + "description": "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about\n[conversation state](/docs/guides/conversation-state)." + }, + "reasoning": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Reasoning" + } + ], + "nullable": true + }, + "background": { + "type": "boolean", + "nullable": true, + "description": "Whether to run the model response in the background.\n[Learn more](/docs/guides/background).", + "default": false + }, + "max_output_tokens": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning)." + }, + "max_tool_calls": { + "type": "integer", + "format": "int32", + "nullable": true, + "description": "The maximum number of total calls to built-in tools that can be processed in a response. This maximum number applies across all built-in tool calls, not per individual tool. Any further attempts to call a tool by the model will be ignored." + }, + "text": { + "type": "object", + "properties": { + "format": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + }, + "description": "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)" + }, + "tools": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.Tool" + }, + "description": "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the\n model's capabilities, like [web search](/docs/guides/tools-web-search)\n or [file search](/docs/guides/tools-file-search). Learn more about\n [built-in tools](/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you,\n enabling the model to call your own code. Learn more about\n [function calling](/docs/guides/function-calling)." + }, + "tool_choice": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceOptions" + }, + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call." + }, + "prompt": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Prompt" + } + ], + "nullable": true + }, + "truncation": { + "type": "string", + "enum": [ + "auto", + "disabled" + ], + "nullable": true, + "description": "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds\n the model's context window size, the model will truncate the\n response to fit the context window by dropping input items in the\n middle of the conversation.\n- `disabled` (default): If a model response will exceed the context window\n size for a model, the request will fail with a 400 error.", + "default": "disabled" + }, + "id": { + "type": "string", + "description": "Unique identifier for this Response." + }, + "object": { + "type": "string", + "enum": [ + "response" + ], + "description": "The object type of this resource - always set to `response`." + }, + "status": { + "type": "string", + "enum": [ + "completed", + "failed", + "in_progress", + "cancelled", + "queued", + "incomplete" + ], + "description": "The status of the response generation. One of `completed`, `failed`,\n`in_progress`, `cancelled`, `queued`, or `incomplete`." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "Unix timestamp (in seconds) of when this Response was created." + }, + "error": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseError" + } + ], + "nullable": true + }, + "incomplete_details": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "max_output_tokens", + "content_filter" + ], + "description": "The reason why the response is incomplete." + } + }, + "nullable": true, + "description": "Details about why the response is incomplete." + }, + "output": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemResource" + }, + "description": "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent\n on the model's response.\n- Rather than accessing the first item in the `output` array and\n assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs." + }, + "instructions": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + } + ], + "nullable": true, + "description": "A system (or developer) message inserted into the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses." + }, + "output_text": { + "type": "string", + "nullable": true, + "description": "SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs." + }, + "usage": { + "$ref": "#/components/schemas/OpenAI.ResponseUsage" + }, + "parallel_tool_calls": { + "type": "boolean", + "description": "Whether to allow the model to run tool calls in parallel.", + "default": true + } + } + }, + "OpenAI.ResponseCodeInterpreterCallCodeDeltaEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.code_interpreter_call_code.delta" + ], + "description": "The type of the event. Always `response.code_interpreter_call_code.delta`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response for which the code is being streamed." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the code interpreter tool call item." + }, + "delta": { + "type": "string", + "description": "The partial code snippet being streamed by the code interpreter." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a partial code snippet is streamed by the code interpreter." + }, + "OpenAI.ResponseCodeInterpreterCallCodeDoneEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id", + "code" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.code_interpreter_call_code.done" + ], + "description": "The type of the event. Always `response.code_interpreter_call_code.done`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response for which the code is finalized." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the code interpreter tool call item." + }, + "code": { + "type": "string", + "description": "The final code snippet output by the code interpreter." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the code snippet is finalized by the code interpreter." + }, + "OpenAI.ResponseCodeInterpreterCallCompletedEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.code_interpreter_call.completed" + ], + "description": "The type of the event. Always `response.code_interpreter_call.completed`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response for which the code interpreter call is completed." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the code interpreter tool call item." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the code interpreter call is completed." + }, + "OpenAI.ResponseCodeInterpreterCallInProgressEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.code_interpreter_call.in_progress" + ], + "description": "The type of the event. Always `response.code_interpreter_call.in_progress`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response for which the code interpreter call is in progress." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the code interpreter tool call item." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a code interpreter call is in progress." + }, + "OpenAI.ResponseCodeInterpreterCallInterpretingEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.code_interpreter_call.interpreting" + ], + "description": "The type of the event. Always `response.code_interpreter_call.interpreting`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response for which the code interpreter is interpreting code." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the code interpreter tool call item." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the code interpreter is actively interpreting the code snippet." + }, + "OpenAI.ResponseCompletedEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.completed" + ], + "description": "The type of the event. Always `response.completed`." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "Properties of the completed response." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the model response is complete." + }, + "OpenAI.ResponseContentPartAddedEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "part" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.content_part.added" + ], + "description": "The type of the event. Always `response.content_part.added`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the content part was added to." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the content part was added to." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that was added." + }, + "part": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "The content part that was added." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a new content part is added." + }, + "OpenAI.ResponseContentPartDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "part" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.content_part.done" + ], + "description": "The type of the event. Always `response.content_part.done`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the content part was added to." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the content part was added to." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that is done." + }, + "part": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemContent" + } + ], + "description": "The content part that is done." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a content part is done." + }, + "OpenAI.ResponseCreatedEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.created" + ], + "description": "The type of the event. Always `response.created`." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "The response that was created." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "An event that is emitted when a response is created." + }, + "OpenAI.ResponseError": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "$ref": "#/components/schemas/OpenAI.ResponseErrorCode" + }, + "message": { + "type": "string", + "description": "A human-readable description of the error." + } + }, + "description": "An error object returned when the model fails to generate a Response." + }, + "OpenAI.ResponseErrorCode": { + "type": "string", + "enum": [ + "server_error", + "rate_limit_exceeded", + "invalid_prompt", + "vector_store_timeout", + "invalid_image", + "invalid_image_format", + "invalid_base64_image", + "invalid_image_url", + "image_too_large", + "image_too_small", + "image_parse_error", + "image_content_policy_violation", + "invalid_image_mode", + "image_file_too_large", + "unsupported_image_media_type", + "empty_image_file", + "failed_to_download_image", + "image_file_not_found" + ], + "description": "The error code for the response." + }, + "OpenAI.ResponseErrorEvent": { + "type": "object", + "required": [ + "type", + "code", + "message", + "param" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "error" + ], + "description": "The type of the event. Always `error`." + }, + "code": { + "type": "string", + "nullable": true, + "description": "The error code." + }, + "message": { + "type": "string", + "description": "The error message." + }, + "param": { + "type": "string", + "nullable": true, + "description": "The error parameter." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an error occurs." + }, + "OpenAI.ResponseFailedEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.failed" + ], + "description": "The type of the event. Always `response.failed`." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "The response that failed." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "An event that is emitted when a response fails." + }, + "OpenAI.ResponseFileSearchCallCompletedEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.file_search_call.completed" + ], + "description": "The type of the event. Always `response.file_search_call.completed`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the file search call is initiated." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a file search call is completed (results found)." + }, + "OpenAI.ResponseFileSearchCallInProgressEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.file_search_call.in_progress" + ], + "description": "The type of the event. Always `response.file_search_call.in_progress`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the file search call is initiated." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a file search call is initiated." + }, + "OpenAI.ResponseFileSearchCallSearchingEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.file_search_call.searching" + ], + "description": "The type of the event. Always `response.file_search_call.searching`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the file search call is searching." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the file search call is initiated." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a file search is currently searching." + }, + "OpenAI.ResponseFormat": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text", + "json_object", + "json_schema" + ] + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/OpenAI.ResponseFormatText", + "json_object": "#/components/schemas/OpenAI.ResponseFormatJsonObject", + "json_schema": "#/components/schemas/OpenAI.ResponseFormatJsonSchema" + } + } + }, + "OpenAI.ResponseFormatJsonObject": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_object" + ], + "description": "The type of response format being defined. Always `json_object`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseFormat" + } + ], + "description": "JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so." + }, + "OpenAI.ResponseFormatJsonSchema": { + "type": "object", + "required": [ + "type", + "json_schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_schema" + ], + "description": "The type of response format being defined. Always `json_schema`." + }, + "json_schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description of what the response format is for, used by the model to\ndetermine how to respond in the format." + }, + "name": { + "type": "string", + "description": "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64." + }, + "schema": { + "$ref": "#/components/schemas/OpenAI.ResponseFormatJsonSchemaSchema" + }, + "strict": { + "type": "boolean", + "nullable": true, + "description": "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).", + "default": false + } + }, + "required": [ + "name" + ], + "description": "Structured Outputs configuration options, including a JSON Schema." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseFormat" + } + ], + "description": "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs)." + }, + "OpenAI.ResponseFormatJsonSchemaSchema": { + "type": "object", + "additionalProperties": {}, + "description": "The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/)." + }, + "OpenAI.ResponseFormatText": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ], + "description": "The type of response format being defined. Always `text`." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseFormat" + } + ], + "description": "Default response format. Used to generate text responses." + }, + "OpenAI.ResponseFunctionCallArgumentsDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.function_call_arguments.delta" + ], + "description": "The type of the event. Always `response.function_call_arguments.delta`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the function-call arguments delta is added to." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the function-call arguments delta is added to." + }, + "delta": { + "type": "string", + "description": "The function-call arguments delta that is added." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is a partial function-call arguments delta." + }, + "OpenAI.ResponseFunctionCallArgumentsDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.function_call_arguments.done" + ] + }, + "item_id": { + "type": "string", + "description": "The ID of the item." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item." + }, + "arguments": { + "type": "string", + "description": "The function-call arguments." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when function-call arguments are finalized." + }, + "OpenAI.ResponseImageGenCallCompletedEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.image_generation_call.completed" + ], + "description": "The type of the event. Always 'response.image_generation_call.completed'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the image generation item being processed." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an image generation tool call has completed and the final image is available." + }, + "OpenAI.ResponseImageGenCallGeneratingEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.image_generation_call.generating" + ], + "description": "The type of the event. Always 'response.image_generation_call.generating'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the image generation item being processed." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an image generation tool call is actively generating an image (intermediate state)." + }, + "OpenAI.ResponseImageGenCallInProgressEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.image_generation_call.in_progress" + ], + "description": "The type of the event. Always 'response.image_generation_call.in_progress'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the image generation item being processed." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an image generation tool call is in progress." + }, + "OpenAI.ResponseImageGenCallPartialImageEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id", + "partial_image_index", + "partial_image_b64" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.image_generation_call.partial_image" + ], + "description": "The type of the event. Always 'response.image_generation_call.partial_image'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the image generation item being processed." + }, + "partial_image_index": { + "type": "integer", + "format": "int32", + "description": "0-based index for the partial image (backend is 1-based, but this is 0-based for the user)." + }, + "partial_image_b64": { + "type": "string", + "description": "Base64-encoded partial image data, suitable for rendering as an image." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a partial image is available during image generation streaming." + }, + "OpenAI.ResponseInProgressEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.in_progress" + ], + "description": "The type of the event. Always `response.in_progress`." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "The response that is in progress." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the response is in progress." + }, + "OpenAI.ResponseIncompleteEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.incomplete" + ], + "description": "The type of the event. Always `response.incomplete`." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "The response that was incomplete." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "An event that is emitted when a response finishes as incomplete." + }, + "OpenAI.ResponseItemList": { + "type": "object", + "required": [ + "object", + "data", + "has_more", + "first_id", + "last_id" + ], + "properties": { + "object": { + "type": "string", + "enum": [ + "list" + ], + "description": "The type of object returned, must be `list`." + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemResource" + }, + "description": "A list of items used to generate this response." + }, + "has_more": { + "type": "boolean", + "description": "Whether there are more items available." + }, + "first_id": { + "type": "string", + "description": "The ID of the first item in the list." + }, + "last_id": { + "type": "string", + "description": "The ID of the last item in the list." + } + }, + "description": "A list of Response items." + }, + "OpenAI.ResponseMCPCallArgumentsDeltaEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_call.arguments_delta" + ], + "description": "The type of the event. Always 'response.mcp_call.arguments_delta'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the MCP tool call item being processed." + }, + "delta": { + "description": "The partial update to the arguments for the MCP tool call." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is a delta (partial update) to the arguments of an MCP tool call." + }, + "OpenAI.ResponseMCPCallArgumentsDoneEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id", + "arguments" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_call.arguments_done" + ], + "description": "The type of the event. Always 'response.mcp_call.arguments_done'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the MCP tool call item being processed." + }, + "arguments": { + "description": "The finalized arguments for the MCP tool call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the arguments for an MCP tool call are finalized." + }, + "OpenAI.ResponseMCPCallCompletedEvent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_call.completed" + ], + "description": "The type of the event. Always 'response.mcp_call.completed'." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an MCP tool call has completed successfully." + }, + "OpenAI.ResponseMCPCallFailedEvent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_call.failed" + ], + "description": "The type of the event. Always 'response.mcp_call.failed'." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an MCP tool call has failed." + }, + "OpenAI.ResponseMCPCallInProgressEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_call.in_progress" + ], + "description": "The type of the event. Always 'response.mcp_call.in_progress'." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the MCP tool call item being processed." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an MCP tool call is in progress." + }, + "OpenAI.ResponseMCPListToolsCompletedEvent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_list_tools.completed" + ], + "description": "The type of the event. Always 'response.mcp_list_tools.completed'." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the list of available MCP tools has been successfully retrieved." + }, + "OpenAI.ResponseMCPListToolsFailedEvent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_list_tools.failed" + ], + "description": "The type of the event. Always 'response.mcp_list_tools.failed'." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the attempt to list available MCP tools has failed." + }, + "OpenAI.ResponseMCPListToolsInProgressEvent": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.mcp_list_tools.in_progress" + ], + "description": "The type of the event. Always 'response.mcp_list_tools.in_progress'." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the system is in the process of retrieving the list of available MCP tools." + }, + "OpenAI.ResponseOutputItemAddedEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.output_item.added" + ], + "description": "The type of the event. Always `response.output_item.added`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that was added." + }, + "item": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The output item that was added." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a new output item is added." + }, + "OpenAI.ResponseOutputItemDoneEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.output_item.done" + ], + "description": "The type of the event. Always `response.output_item.done`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that was marked done." + }, + "item": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "The output item that was marked done." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when an output item is marked done." + }, + "OpenAI.ResponsePromptVariables": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/OpenAI.ItemParam" + }, + "description": "Optional map of values to substitute in for variables in your\nprompt. The substitution values can either be strings, or other\nResponse input types like images or files.", + "x-oaiExpandable": true, + "x-oaiTypeLabel": "map" + }, + "OpenAI.ResponseQueuedEvent": { + "type": "object", + "required": [ + "type", + "response" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.queued" + ], + "description": "The type of the event. Always 'response.queued'." + }, + "response": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Response" + } + ], + "description": "The full response object that is queued." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a response is queued and waiting to be processed." + }, + "OpenAI.ResponseReasoningDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning.delta" + ], + "description": "The type of the event. Always 'response.reasoning.delta'." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the item for which reasoning is being updated." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the reasoning content part within the output item." + }, + "delta": { + "description": "The partial update to the reasoning content." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is a delta (partial update) to the reasoning content." + }, + "OpenAI.ResponseReasoningDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning.done" + ], + "description": "The type of the event. Always 'response.reasoning.done'." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the item for which reasoning is finalized." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the reasoning content part within the output item." + }, + "text": { + "type": "string", + "description": "The finalized reasoning text." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the reasoning content is finalized for an item." + }, + "OpenAI.ResponseReasoningSummaryDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary.delta" + ], + "description": "The type of the event. Always 'response.reasoning_summary.delta'." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the item for which the reasoning summary is being updated." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the output item." + }, + "delta": { + "description": "The partial update to the reasoning summary content." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is a delta (partial update) to the reasoning summary content." + }, + "OpenAI.ResponseReasoningSummaryDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary.done" + ], + "description": "The type of the event. Always 'response.reasoning_summary.done'." + }, + "item_id": { + "type": "string", + "description": "The unique identifier of the item for which the reasoning summary is finalized." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item in the response's output array." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the output item." + }, + "text": { + "type": "string", + "description": "The finalized reasoning summary text." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when the reasoning summary content is finalized for an item." + }, + "OpenAI.ResponseReasoningSummaryPartAddedEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "part" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary_part.added" + ], + "description": "The type of the event. Always `response.reasoning_summary_part.added`." + }, + "item_id": { + "type": "string", + "description": "The ID of the item this summary part is associated with." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item this summary part is associated with." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the reasoning summary." + }, + "part": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPart" + } + ], + "description": "The summary part that was added." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a new reasoning summary part is added." + }, + "OpenAI.ResponseReasoningSummaryPartDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "part" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary_part.done" + ], + "description": "The type of the event. Always `response.reasoning_summary_part.done`." + }, + "item_id": { + "type": "string", + "description": "The ID of the item this summary part is associated with." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item this summary part is associated with." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the reasoning summary." + }, + "part": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ReasoningItemSummaryPart" + } + ], + "description": "The completed summary part." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a reasoning summary part is completed." + }, + "OpenAI.ResponseReasoningSummaryTextDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary_text.delta" + ], + "description": "The type of the event. Always `response.reasoning_summary_text.delta`." + }, + "item_id": { + "type": "string", + "description": "The ID of the item this summary text delta is associated with." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item this summary text delta is associated with." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the reasoning summary." + }, + "delta": { + "type": "string", + "description": "The text delta that was added to the summary." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a delta is added to a reasoning summary text." + }, + "OpenAI.ResponseReasoningSummaryTextDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "summary_index", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.reasoning_summary_text.done" + ], + "description": "The type of the event. Always `response.reasoning_summary_text.done`." + }, + "item_id": { + "type": "string", + "description": "The ID of the item this summary text is associated with." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item this summary text is associated with." + }, + "summary_index": { + "type": "integer", + "format": "int32", + "description": "The index of the summary part within the reasoning summary." + }, + "text": { + "type": "string", + "description": "The full text of the completed reasoning summary." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when a reasoning summary text is completed." + }, + "OpenAI.ResponseRefusalDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.refusal.delta" + ], + "description": "The type of the event. Always `response.refusal.delta`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the refusal text is added to." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the refusal text is added to." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that the refusal text is added to." + }, + "delta": { + "type": "string", + "description": "The refusal text that is added." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is a partial refusal text." + }, + "OpenAI.ResponseRefusalDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "refusal" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.refusal.done" + ], + "description": "The type of the event. Always `response.refusal.done`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the refusal text is finalized." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the refusal text is finalized." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that the refusal text is finalized." + }, + "refusal": { + "type": "string", + "description": "The refusal text that is finalized." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when refusal text is finalized." + }, + "OpenAI.ResponseStreamEvent": { + "type": "object", + "required": [ + "type", + "sequence_number" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEventType" + }, + "sequence_number": { + "type": "integer", + "format": "int32", + "description": "The sequence number for this event." + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "response.completed": "#/components/schemas/OpenAI.ResponseCompletedEvent", + "response.content_part.added": "#/components/schemas/OpenAI.ResponseContentPartAddedEvent", + "response.content_part.done": "#/components/schemas/OpenAI.ResponseContentPartDoneEvent", + "response.created": "#/components/schemas/OpenAI.ResponseCreatedEvent", + "error": "#/components/schemas/OpenAI.ResponseErrorEvent", + "response.file_search_call.completed": "#/components/schemas/OpenAI.ResponseFileSearchCallCompletedEvent", + "response.file_search_call.in_progress": "#/components/schemas/OpenAI.ResponseFileSearchCallInProgressEvent", + "response.file_search_call.searching": "#/components/schemas/OpenAI.ResponseFileSearchCallSearchingEvent", + "response.function_call_arguments.delta": "#/components/schemas/OpenAI.ResponseFunctionCallArgumentsDeltaEvent", + "response.function_call_arguments.done": "#/components/schemas/OpenAI.ResponseFunctionCallArgumentsDoneEvent", + "response.in_progress": "#/components/schemas/OpenAI.ResponseInProgressEvent", + "response.failed": "#/components/schemas/OpenAI.ResponseFailedEvent", + "response.incomplete": "#/components/schemas/OpenAI.ResponseIncompleteEvent", + "response.output_item.added": "#/components/schemas/OpenAI.ResponseOutputItemAddedEvent", + "response.output_item.done": "#/components/schemas/OpenAI.ResponseOutputItemDoneEvent", + "response.refusal.delta": "#/components/schemas/OpenAI.ResponseRefusalDeltaEvent", + "response.refusal.done": "#/components/schemas/OpenAI.ResponseRefusalDoneEvent", + "response.output_text.delta": "#/components/schemas/OpenAI.ResponseTextDeltaEvent", + "response.output_text.done": "#/components/schemas/OpenAI.ResponseTextDoneEvent", + "response.reasoning_summary_part.added": "#/components/schemas/OpenAI.ResponseReasoningSummaryPartAddedEvent", + "response.reasoning_summary_part.done": "#/components/schemas/OpenAI.ResponseReasoningSummaryPartDoneEvent", + "response.reasoning_summary_text.delta": "#/components/schemas/OpenAI.ResponseReasoningSummaryTextDeltaEvent", + "response.reasoning_summary_text.done": "#/components/schemas/OpenAI.ResponseReasoningSummaryTextDoneEvent", + "response.web_search_call.completed": "#/components/schemas/OpenAI.ResponseWebSearchCallCompletedEvent", + "response.web_search_call.in_progress": "#/components/schemas/OpenAI.ResponseWebSearchCallInProgressEvent", + "response.web_search_call.searching": "#/components/schemas/OpenAI.ResponseWebSearchCallSearchingEvent", + "response.image_generation_call.completed": "#/components/schemas/OpenAI.ResponseImageGenCallCompletedEvent", + "response.image_generation_call.generating": "#/components/schemas/OpenAI.ResponseImageGenCallGeneratingEvent", + "response.image_generation_call.in_progress": "#/components/schemas/OpenAI.ResponseImageGenCallInProgressEvent", + "response.image_generation_call.partial_image": "#/components/schemas/OpenAI.ResponseImageGenCallPartialImageEvent", + "response.mcp_call.arguments_delta": "#/components/schemas/OpenAI.ResponseMCPCallArgumentsDeltaEvent", + "response.mcp_call.arguments_done": "#/components/schemas/OpenAI.ResponseMCPCallArgumentsDoneEvent", + "response.mcp_call.completed": "#/components/schemas/OpenAI.ResponseMCPCallCompletedEvent", + "response.mcp_call.failed": "#/components/schemas/OpenAI.ResponseMCPCallFailedEvent", + "response.mcp_call.in_progress": "#/components/schemas/OpenAI.ResponseMCPCallInProgressEvent", + "response.mcp_list_tools.completed": "#/components/schemas/OpenAI.ResponseMCPListToolsCompletedEvent", + "response.mcp_list_tools.failed": "#/components/schemas/OpenAI.ResponseMCPListToolsFailedEvent", + "response.mcp_list_tools.in_progress": "#/components/schemas/OpenAI.ResponseMCPListToolsInProgressEvent", + "response.queued": "#/components/schemas/OpenAI.ResponseQueuedEvent", + "response.reasoning.delta": "#/components/schemas/OpenAI.ResponseReasoningDeltaEvent", + "response.reasoning.done": "#/components/schemas/OpenAI.ResponseReasoningDoneEvent", + "response.reasoning_summary.delta": "#/components/schemas/OpenAI.ResponseReasoningSummaryDeltaEvent", + "response.reasoning_summary.done": "#/components/schemas/OpenAI.ResponseReasoningSummaryDoneEvent", + "response.code_interpreter_call_code.delta": "#/components/schemas/OpenAI.ResponseCodeInterpreterCallCodeDeltaEvent", + "response.code_interpreter_call_code.done": "#/components/schemas/OpenAI.ResponseCodeInterpreterCallCodeDoneEvent", + "response.code_interpreter_call.completed": "#/components/schemas/OpenAI.ResponseCodeInterpreterCallCompletedEvent", + "response.code_interpreter_call.in_progress": "#/components/schemas/OpenAI.ResponseCodeInterpreterCallInProgressEvent", + "response.code_interpreter_call.interpreting": "#/components/schemas/OpenAI.ResponseCodeInterpreterCallInterpretingEvent" + } + } + }, + "OpenAI.ResponseStreamEventType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "response.audio.delta", + "response.audio.done", + "response.audio_transcript.delta", + "response.audio_transcript.done", + "response.code_interpreter_call_code.delta", + "response.code_interpreter_call_code.done", + "response.code_interpreter_call.completed", + "response.code_interpreter_call.in_progress", + "response.code_interpreter_call.interpreting", + "response.completed", + "response.content_part.added", + "response.content_part.done", + "response.created", + "error", + "response.file_search_call.completed", + "response.file_search_call.in_progress", + "response.file_search_call.searching", + "response.function_call_arguments.delta", + "response.function_call_arguments.done", + "response.in_progress", + "response.failed", + "response.incomplete", + "response.output_item.added", + "response.output_item.done", + "response.refusal.delta", + "response.refusal.done", + "response.output_text.annotation.added", + "response.output_text.delta", + "response.output_text.done", + "response.reasoning_summary_part.added", + "response.reasoning_summary_part.done", + "response.reasoning_summary_text.delta", + "response.reasoning_summary_text.done", + "response.web_search_call.completed", + "response.web_search_call.in_progress", + "response.web_search_call.searching", + "response.image_generation_call.completed", + "response.image_generation_call.generating", + "response.image_generation_call.in_progress", + "response.image_generation_call.partial_image", + "response.mcp_call.arguments_delta", + "response.mcp_call.arguments_done", + "response.mcp_call.completed", + "response.mcp_call.failed", + "response.mcp_call.in_progress", + "response.mcp_list_tools.completed", + "response.mcp_list_tools.failed", + "response.mcp_list_tools.in_progress", + "response.queued", + "response.reasoning.delta", + "response.reasoning.done", + "response.reasoning_summary.delta", + "response.reasoning_summary.done" + ] + } + ] + }, + "OpenAI.ResponseTextDeltaEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "delta", + "obfuscation" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.output_text.delta" + ], + "description": "The type of the event. Always `response.output_text.delta`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the text delta was added to." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the text delta was added to." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that the text delta was added to." + }, + "delta": { + "type": "string", + "description": "The text delta that was added." + }, + "obfuscation": { + "type": "string", + "description": "A field of random characters introduced by stream obfuscation. Stream obfuscation is a mechanism that mitigates certain side-channel attacks." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when there is an additional text delta." + }, + "OpenAI.ResponseTextDoneEvent": { + "type": "object", + "required": [ + "type", + "item_id", + "output_index", + "content_index", + "text" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.output_text.done" + ], + "description": "The type of the event. Always `response.output_text.done`." + }, + "item_id": { + "type": "string", + "description": "The ID of the output item that the text content is finalized." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the text content is finalized." + }, + "content_index": { + "type": "integer", + "format": "int32", + "description": "The index of the content part that the text content is finalized." + }, + "text": { + "type": "string", + "description": "The text content that is finalized." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Emitted when text content is finalized." + }, + "OpenAI.ResponseTextFormatConfiguration": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfigurationType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/OpenAI.ResponseTextFormatConfigurationText", + "json_object": "#/components/schemas/OpenAI.ResponseTextFormatConfigurationJsonObject", + "json_schema": "#/components/schemas/OpenAI.ResponseTextFormatConfigurationJsonSchema" + } + } + }, + "OpenAI.ResponseTextFormatConfigurationJsonObject": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_object" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + ] + }, + "OpenAI.ResponseTextFormatConfigurationJsonSchema": { + "type": "object", + "required": [ + "type", + "name", + "schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_schema" + ], + "description": "The type of response format being defined. Always `json_schema`." + }, + "description": { + "type": "string", + "description": "A description of what the response format is for, used by the model to\ndetermine how to respond in the format." + }, + "name": { + "type": "string", + "description": "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64." + }, + "schema": { + "$ref": "#/components/schemas/OpenAI.ResponseFormatJsonSchemaSchema" + }, + "strict": { + "type": "boolean", + "nullable": true, + "description": "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).", + "default": false + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + ], + "description": "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs)." + }, + "OpenAI.ResponseTextFormatConfigurationText": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseTextFormatConfiguration" + } + ] + }, + "OpenAI.ResponseTextFormatConfigurationType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "text", + "json_schema", + "json_object" + ] + } + ], + "description": "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it." + }, + "OpenAI.ResponseUsage": { + "type": "object", + "required": [ + "input_tokens", + "input_tokens_details", + "output_tokens", + "output_tokens_details", + "total_tokens" + ], + "properties": { + "input_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of input tokens." + }, + "input_tokens_details": { + "type": "object", + "properties": { + "cached_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens that were retrieved from the cache.\n[More on prompt caching](/docs/guides/prompt-caching)." + } + }, + "required": [ + "cached_tokens" + ], + "description": "A detailed breakdown of the input tokens." + }, + "output_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of output tokens." + }, + "output_tokens_details": { + "type": "object", + "properties": { + "reasoning_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of reasoning tokens." + } + }, + "required": [ + "reasoning_tokens" + ], + "description": "A detailed breakdown of the output tokens." + }, + "total_tokens": { + "type": "integer", + "format": "int32", + "description": "The total number of tokens used." + } + }, + "description": "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used." + }, + "OpenAI.ResponseWebSearchCallCompletedEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.web_search_call.completed" + ], + "description": "The type of the event. Always `response.web_search_call.completed`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the web search call is associated with." + }, + "item_id": { + "type": "string", + "description": "Unique ID for the output item associated with the web search call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.ResponseWebSearchCallInProgressEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.web_search_call.in_progress" + ], + "description": "The type of the event. Always `response.web_search_call.in_progress`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the web search call is associated with." + }, + "item_id": { + "type": "string", + "description": "Unique ID for the output item associated with the web search call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.ResponseWebSearchCallSearchingEvent": { + "type": "object", + "required": [ + "type", + "output_index", + "item_id" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "response.web_search_call.searching" + ], + "description": "The type of the event. Always `response.web_search_call.searching`." + }, + "output_index": { + "type": "integer", + "format": "int32", + "description": "The index of the output item that the web search call is associated with." + }, + "item_id": { + "type": "string", + "description": "Unique ID for the output item associated with the web search call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponseStreamEvent" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.ResponsesAssistantMessageItemParam": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the message, which is always `assistant`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemParam" + } + ], + "description": "A message parameter item with the `assistant` role." + }, + "OpenAI.ResponsesAssistantMessageItemResource": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "assistant" + ], + "description": "The role of the message, which is always `assistant`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemResource" + } + ], + "description": "A message resource item with the `assistant` role." + }, + "OpenAI.ResponsesDeveloperMessageItemParam": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "developer" + ], + "description": "The role of the message, which is always `developer`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemParam" + } + ], + "description": "A message parameter item with the `developer` role." + }, + "OpenAI.ResponsesDeveloperMessageItemResource": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "developer" + ], + "description": "The role of the message, which is always `developer`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemResource" + } + ], + "description": "A message resource item with the `developer` role." + }, + "OpenAI.ResponsesMessageItemParam": { + "type": "object", + "required": [ + "type", + "role" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "message" + ], + "description": "The type of the responses item, which is always 'message'." + }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageRole" + } + ], + "description": "The role associated with the message." + } + }, + "discriminator": { + "propertyName": "role", + "mapping": { + "user": "#/components/schemas/OpenAI.ResponsesUserMessageItemParam", + "system": "#/components/schemas/OpenAI.ResponsesSystemMessageItemParam", + "developer": "#/components/schemas/OpenAI.ResponsesDeveloperMessageItemParam", + "assistant": "#/components/schemas/OpenAI.ResponsesAssistantMessageItemParam" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "A response message item, representing a role and content, as provided as client request parameters." + }, + "OpenAI.ResponsesMessageItemResource": { + "type": "object", + "required": [ + "type", + "status", + "role" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "message" + ], + "description": "The type of the responses item, which is always 'message'." + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "incomplete" + ], + "description": "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + }, + "role": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageRole" + } + ], + "description": "The role associated with the message." + } + }, + "discriminator": { + "propertyName": "role", + "mapping": { + "user": "#/components/schemas/OpenAI.ResponsesUserMessageItemResource", + "system": "#/components/schemas/OpenAI.ResponsesSystemMessageItemResource", + "developer": "#/components/schemas/OpenAI.ResponsesDeveloperMessageItemResource", + "assistant": "#/components/schemas/OpenAI.ResponsesAssistantMessageItemResource" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "A response message resource item, representing a role and content, as provided on service responses." + }, + "OpenAI.ResponsesMessageRole": { + "type": "string", + "enum": [ + "system", + "developer", + "user", + "assistant" + ], + "description": "The collection of valid roles for responses message items." + }, + "OpenAI.ResponsesSystemMessageItemParam": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "system" + ], + "description": "The role of the message, which is always `system`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemParam" + } + ], + "description": "A message parameter item with the `system` role." + }, + "OpenAI.ResponsesSystemMessageItemResource": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "system" + ], + "description": "The role of the message, which is always `system`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemResource" + } + ], + "description": "A message resource item with the `system` role." + }, + "OpenAI.ResponsesUserMessageItemParam": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "user" + ], + "description": "The role of the message, which is always `user`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemParam" + } + ], + "description": "A message parameter item with the `user` role." + }, + "OpenAI.ResponsesUserMessageItemResource": { + "type": "object", + "required": [ + "role", + "content" + ], + "properties": { + "role": { + "type": "string", + "enum": [ + "user" + ], + "description": "The role of the message, which is always `user`." + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OpenAI.ItemContent" + }, + "description": "The content associated with the message." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ResponsesMessageItemResource" + } + ], + "description": "A message resource item with the `user` role." + }, + "OpenAI.RunGraderRequest": { + "type": "object", + "required": [ + "grader", + "model_sample" + ], + "properties": { + "grader": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.GraderStringCheck" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderTextSimilarity" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderPython" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderScoreModel" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderMulti" + } + ], + "description": "The grader used for the fine-tuning job." + }, + "item": { + "description": "The dataset item provided to the grader. This will be used to populate\nthe `item` namespace. See [the guide](/docs/guides/graders) for more details." + }, + "model_sample": { + "type": "string", + "description": "The model sample to be evaluated. This value will be used to populate\nthe `sample` namespace. See [the guide](/docs/guides/graders) for more details.\nThe `output_json` variable will be populated if the model sample is a\nvalid JSON string." + } + } + }, + "OpenAI.RunGraderResponse": { + "type": "object", + "required": [ + "reward", + "metadata", + "sub_rewards", + "model_grader_token_usage_per_model" + ], + "properties": { + "reward": { + "type": "number", + "format": "float" + }, + "metadata": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + }, + "errors": { + "type": "object", + "properties": { + "formula_parse_error": { + "type": "boolean" + }, + "sample_parse_error": { + "type": "boolean" + }, + "truncated_observation_error": { + "type": "boolean" + }, + "unresponsive_reward_error": { + "type": "boolean" + }, + "invalid_variable_error": { + "type": "boolean" + }, + "other_error": { + "type": "boolean" + }, + "python_grader_server_error": { + "type": "boolean" + }, + "python_grader_server_error_type": { + "type": "string", + "nullable": true + }, + "python_grader_runtime_error": { + "type": "boolean" + }, + "python_grader_runtime_error_details": { + "type": "string", + "nullable": true + }, + "model_grader_server_error": { + "type": "boolean" + }, + "model_grader_refusal_error": { + "type": "boolean" + }, + "model_grader_parse_error": { + "type": "boolean" + }, + "model_grader_server_error_details": { + "type": "string", + "nullable": true + } + }, + "required": [ + "formula_parse_error", + "sample_parse_error", + "truncated_observation_error", + "unresponsive_reward_error", + "invalid_variable_error", + "other_error", + "python_grader_server_error", + "python_grader_server_error_type", + "python_grader_runtime_error", + "python_grader_runtime_error_details", + "model_grader_server_error", + "model_grader_refusal_error", + "model_grader_parse_error", + "model_grader_server_error_details" + ] + }, + "execution_time": { + "type": "number", + "format": "float" + }, + "scores": {}, + "token_usage": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "sampled_model_name": { + "type": "string", + "nullable": true + } + }, + "required": [ + "name", + "type", + "errors", + "execution_time", + "scores", + "token_usage", + "sampled_model_name" + ] + }, + "sub_rewards": {}, + "model_grader_token_usage_per_model": {} + } + }, + "OpenAI.StaticChunkingStrategy": { + "type": "object", + "required": [ + "max_chunk_size_tokens", + "chunk_overlap_tokens" + ], + "properties": { + "max_chunk_size_tokens": { + "type": "integer", + "format": "int32", + "minimum": 100, + "maximum": 4096, + "description": "The maximum number of tokens in each chunk. The default value is `800`. The minimum value is `100` and the maximum value is `4096`." + }, + "chunk_overlap_tokens": { + "type": "integer", + "format": "int32", + "description": "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`." + } + } + }, + "OpenAI.StaticChunkingStrategyRequestParam": { + "type": "object", + "required": [ + "type", + "static" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "static" + ], + "description": "Always `static`." + }, + "static": { + "$ref": "#/components/schemas/OpenAI.StaticChunkingStrategy" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyRequestParam" + } + ], + "description": "Customize your own chunking strategy by setting chunk size and chunk overlap." + }, + "OpenAI.StaticChunkingStrategyResponseParam": { + "type": "object", + "required": [ + "type", + "static" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "static" + ], + "description": "Always `static`." + }, + "static": { + "$ref": "#/components/schemas/OpenAI.StaticChunkingStrategy" + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyResponseParam" + } + ] + }, + "OpenAI.StopConfiguration": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "Not supported with latest reasoning models `o3` and `o4-mini`.\n\nUp to 4 sequences where the API will stop generating further tokens. The\nreturned text will not contain the stop sequence." + }, + "OpenAI.Tool": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ToolType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "function": "#/components/schemas/OpenAI.FunctionTool", + "file_search": "#/components/schemas/OpenAI.FileSearchTool", + "computer_use_preview": "#/components/schemas/OpenAI.ComputerUsePreviewTool", + "web_search_preview": "#/components/schemas/OpenAI.WebSearchPreviewTool", + "code_interpreter": "#/components/schemas/OpenAI.CodeInterpreterTool", + "image_generation": "#/components/schemas/OpenAI.ImageGenTool", + "local_shell": "#/components/schemas/OpenAI.LocalShellTool", + "mcp": "#/components/schemas/OpenAI.MCPTool" + } + } + }, + "OpenAI.ToolChoiceObject": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObjectType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "file_search": "#/components/schemas/OpenAI.ToolChoiceObjectFileSearch", + "computer_use_preview": "#/components/schemas/OpenAI.ToolChoiceObjectComputer", + "web_search_preview": "#/components/schemas/OpenAI.ToolChoiceObjectWebSearch", + "image_generation": "#/components/schemas/OpenAI.ToolChoiceObjectImageGen", + "code_interpreter": "#/components/schemas/OpenAI.ToolChoiceObjectCodeInterpreter", + "function": "#/components/schemas/OpenAI.ToolChoiceObjectFunction", + "mcp": "#/components/schemas/OpenAI.ToolChoiceObjectMCP" + } + } + }, + "OpenAI.ToolChoiceObjectCodeInterpreter": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "code_interpreter" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ] + }, + "OpenAI.ToolChoiceObjectComputer": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "computer_use_preview" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ] + }, + "OpenAI.ToolChoiceObjectFileSearch": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "file_search" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ] + }, + "OpenAI.ToolChoiceObjectFunction": { + "type": "object", + "required": [ + "type", + "name" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "function" + ], + "description": "For function calling, the type is always `function`." + }, + "name": { + "type": "string", + "description": "The name of the function to call." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "Use this option to force the model to call a specific function." + }, + "OpenAI.ToolChoiceObjectImageGen": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image_generation" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ] + }, + "OpenAI.ToolChoiceObjectMCP": { + "type": "object", + "required": [ + "type", + "server_label" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "mcp" + ], + "description": "For MCP tools, the type is always `mcp`." + }, + "server_label": { + "type": "string", + "description": "The label of the MCP server to use." + }, + "name": { + "type": "string", + "nullable": true, + "description": "The name of the tool to call on the server." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "Use this option to force the model to call a specific tool on a remote MCP server." + }, + "OpenAI.ToolChoiceObjectType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "file_search", + "function", + "computer_use_preview", + "web_search_preview", + "image_generation", + "code_interpreter", + "mcp" + ] + } + ], + "description": "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools)." + }, + "OpenAI.ToolChoiceObjectWebSearch": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "web_search_preview" + ] + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ToolChoiceObject" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.ToolChoiceOptions": { + "type": "string", + "enum": [ + "none", + "auto", + "required" + ], + "description": "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools." + }, + "OpenAI.ToolType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "file_search", + "function", + "computer_use_preview", + "web_search_preview", + "mcp", + "code_interpreter", + "image_generation", + "local_shell" + ] + } + ], + "description": "A tool that can be used to generate a response." + }, + "OpenAI.TopLogProb": { + "type": "object", + "required": [ + "token", + "logprob", + "bytes" + ], + "properties": { + "token": { + "type": "string" + }, + "logprob": { + "type": "number", + "format": "float" + }, + "bytes": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + }, + "description": "The top log probability of a token." + }, + "OpenAI.UpdateVectorStoreFileAttributesRequest": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + } + ], + "nullable": true + } + } + }, + "OpenAI.UpdateVectorStoreRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "description": "The name of the vector store." + }, + "expires_after": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VectorStoreExpirationAfter" + } + ], + "nullable": true + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + } + }, + "OpenAI.ValidateGraderRequest": { + "type": "object", + "required": [ + "grader" + ], + "properties": { + "grader": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.GraderStringCheck" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderTextSimilarity" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderPython" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderScoreModel" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderMulti" + } + ], + "description": "The grader used for the fine-tuning job." + } + } + }, + "OpenAI.ValidateGraderResponse": { + "type": "object", + "properties": { + "grader": { + "anyOf": [ + { + "$ref": "#/components/schemas/OpenAI.GraderStringCheck" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderTextSimilarity" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderPython" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderScoreModel" + }, + { + "$ref": "#/components/schemas/OpenAI.GraderMulti" + } + ], + "description": "The grader used for the fine-tuning job." + } + } + }, + "OpenAI.VectorStoreExpirationAfter": { + "type": "object", + "required": [ + "anchor", + "days" + ], + "properties": { + "anchor": { + "type": "string", + "enum": [ + "last_active_at" + ], + "description": "Anchor timestamp after which the expiration policy applies. Supported anchors: `last_active_at`." + }, + "days": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 365, + "description": "The number of days after the anchor time that the vector store will expire." + } + }, + "description": "The expiration policy for a vector store." + }, + "OpenAI.VectorStoreFileAttributes": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "type": "integer", + "format": "int32" + }, + { + "type": "number", + "format": "float" + } + ] + }, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. Keys are strings\nwith a maximum length of 64 characters. Values are strings with a maximum\nlength of 512 characters, booleans, or numbers.", + "x-oaiTypeLabel": "map" + }, + "OpenAI.VectorStoreFileBatchObject": { + "type": "object", + "required": [ + "id", + "object", + "created_at", + "vector_store_id", + "status", + "file_counts" + ], + "properties": { + "id": { + "type": "string", + "description": "The identifier, which can be referenced in API endpoints." + }, + "object": { + "type": "string", + "enum": [ + "vector_store.files_batch" + ], + "description": "The object type, which is always `vector_store.file_batch`." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the vector store files batch was created." + }, + "vector_store_id": { + "type": "string", + "description": "The ID of the [vector store](/docs/api-reference/vector-stores/object) that the [File](/docs/api-reference/files) is attached to." + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "cancelled", + "failed" + ], + "description": "The status of the vector store files batch, which can be either `in_progress`, `completed`, `cancelled` or `failed`." + }, + "file_counts": { + "type": "object", + "properties": { + "in_progress": { + "type": "integer", + "format": "int32", + "description": "The number of files that are currently being processed." + }, + "completed": { + "type": "integer", + "format": "int32", + "description": "The number of files that have been processed." + }, + "failed": { + "type": "integer", + "format": "int32", + "description": "The number of files that have failed to process." + }, + "cancelled": { + "type": "integer", + "format": "int32", + "description": "The number of files that where cancelled." + }, + "total": { + "type": "integer", + "format": "int32", + "description": "The total number of files." + } + }, + "required": [ + "in_progress", + "completed", + "failed", + "cancelled", + "total" + ] + } + }, + "description": "A batch of files attached to a vector store." + }, + "OpenAI.VectorStoreFileObject": { + "type": "object", + "required": [ + "id", + "object", + "usage_bytes", + "created_at", + "vector_store_id", + "status", + "last_error" + ], + "properties": { + "id": { + "type": "string", + "description": "The identifier, which can be referenced in API endpoints." + }, + "object": { + "type": "string", + "enum": [ + "vector_store.file" + ], + "description": "The object type, which is always `vector_store.file`." + }, + "usage_bytes": { + "type": "integer", + "format": "int32", + "description": "The total vector store usage in bytes. Note that this may be different from the original file size." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the vector store file was created." + }, + "vector_store_id": { + "type": "string", + "description": "The ID of the [vector store](/docs/api-reference/vector-stores/object) that the [File](/docs/api-reference/files) is attached to." + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "completed", + "cancelled", + "failed" + ], + "description": "The status of the vector store file, which can be either `in_progress`, `completed`, `cancelled`, or `failed`. The status `completed` indicates that the vector store file is ready for use." + }, + "last_error": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "server_error", + "unsupported_file", + "invalid_file" + ], + "description": "One of `server_error` or `rate_limit_exceeded`." + }, + "message": { + "type": "string", + "description": "A human-readable description of the error." + } + }, + "required": [ + "code", + "message" + ], + "nullable": true, + "description": "The last error associated with this vector store file. Will be `null` if there are no errors." + }, + "chunking_strategy": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ChunkingStrategyResponseParam" + } + ], + "description": "The strategy used to chunk the file." + }, + "attributes": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.VectorStoreFileAttributes" + } + ], + "nullable": true + } + }, + "description": "A list of files attached to a vector store." + }, + "OpenAI.VectorStoreObject": { + "type": "object", + "required": [ + "id", + "object", + "created_at", + "name", + "usage_bytes", + "file_counts", + "status", + "last_active_at", + "metadata" + ], + "properties": { + "id": { + "type": "string", + "description": "The identifier, which can be referenced in API endpoints." + }, + "object": { + "type": "string", + "enum": [ + "vector_store" + ], + "description": "The object type, which is always `vector_store`." + }, + "created_at": { + "type": "integer", + "format": "unixtime", + "description": "The Unix timestamp (in seconds) for when the vector store was created." + }, + "name": { + "type": "string", + "description": "The name of the vector store." + }, + "usage_bytes": { + "type": "integer", + "format": "int32", + "description": "The total number of bytes used by the files in the vector store." + }, + "file_counts": { + "type": "object", + "properties": { + "in_progress": { + "type": "integer", + "format": "int32", + "description": "The number of files that are currently being processed." + }, + "completed": { + "type": "integer", + "format": "int32", + "description": "The number of files that have been successfully processed." + }, + "failed": { + "type": "integer", + "format": "int32", + "description": "The number of files that have failed to process." + }, + "cancelled": { + "type": "integer", + "format": "int32", + "description": "The number of files that were cancelled." + }, + "total": { + "type": "integer", + "format": "int32", + "description": "The total number of files." + } + }, + "required": [ + "in_progress", + "completed", + "failed", + "cancelled", + "total" + ] + }, + "status": { + "type": "string", + "enum": [ + "expired", + "in_progress", + "completed" + ], + "description": "The status of the vector store, which can be either `expired`, `in_progress`, or `completed`. A status of `completed` indicates that the vector store is ready for use." + }, + "expires_after": { + "$ref": "#/components/schemas/OpenAI.VectorStoreExpirationAfter" + }, + "expires_at": { + "type": "integer", + "format": "unixtime", + "nullable": true, + "description": "The Unix timestamp (in seconds) for when the vector store will expire." + }, + "last_active_at": { + "type": "integer", + "format": "unixtime", + "nullable": true, + "description": "The Unix timestamp (in seconds) for when the vector store was last active." + }, + "metadata": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "nullable": true, + "description": "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.", + "x-oaiTypeLabel": "map" + } + }, + "description": "A vector store is a collection of processed files can be used by the `file_search` tool." + }, + "OpenAI.VoiceIdsShared": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "alloy", + "ash", + "ballad", + "coral", + "echo", + "fable", + "onyx", + "nova", + "sage", + "shimmer", + "verse" + ] + } + ] + }, + "OpenAI.WebSearchAction": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/OpenAI.WebSearchActionType" + } + }, + "discriminator": { + "propertyName": "type", + "mapping": { + "find": "#/components/schemas/OpenAI.WebSearchActionFind", + "open_page": "#/components/schemas/OpenAI.WebSearchActionOpenPage", + "search": "#/components/schemas/OpenAI.WebSearchActionSearch" + } + } + }, + "OpenAI.WebSearchActionFind": { + "type": "object", + "required": [ + "type", + "url", + "pattern" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "find" + ], + "description": "The action type." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The URL of the page searched for the pattern." + }, + "pattern": { + "type": "string", + "description": "The pattern or text to search for within the page." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.WebSearchAction" + } + ], + "description": "Action type \"find\": Searches for a pattern within a loaded page." + }, + "OpenAI.WebSearchActionOpenPage": { + "type": "object", + "required": [ + "type", + "url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "open_page" + ], + "description": "The action type." + }, + "url": { + "type": "string", + "format": "uri", + "description": "The URL opened by the model." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.WebSearchAction" + } + ], + "description": "Action type \"open_page\" - Opens a specific URL from search results." + }, + "OpenAI.WebSearchActionSearch": { + "type": "object", + "required": [ + "type", + "query" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "search" + ], + "description": "The action type." + }, + "query": { + "type": "string", + "description": "The search query." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.WebSearchAction" + } + ], + "description": "Action type \"search\" - Performs a web search query." + }, + "OpenAI.WebSearchActionType": { + "type": "string", + "enum": [ + "search", + "open_page", + "find" + ] + }, + "OpenAI.WebSearchPreviewTool": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "web_search_preview" + ], + "description": "The type of the web search tool. One of `web_search_preview` or `web_search_preview_2025_03_11`." + }, + "user_location": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Location" + } + ], + "nullable": true, + "description": "The user's location." + }, + "search_context_size": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ], + "description": "High level guidance for the amount of context window space to use for the search. One of `low`, `medium`, or `high`. `medium` is the default." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.Tool" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.WebSearchToolCallItemParam": { + "type": "object", + "required": [ + "type", + "action" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "web_search_call" + ] + }, + "action": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.WebSearchAction" + } + ], + "description": "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemParam" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "OpenAI.WebSearchToolCallItemResource": { + "type": "object", + "required": [ + "type", + "status", + "action" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "web_search_call" + ] + }, + "status": { + "type": "string", + "enum": [ + "in_progress", + "searching", + "completed", + "failed" + ], + "description": "The status of the web search tool call." + }, + "action": { + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.WebSearchAction" + } + ], + "description": "An object describing the specific action taken in this web search call.\nIncludes details on how the model used the web (search, open_page, find)." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/OpenAI.ItemResource" + } + ], + "description": "Note: web_search is not yet available via Azure OpenAI." + }, + "PineconeChatDataSource": { + "type": "object", + "required": [ + "type", + "parameters" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "pinecone" + ], + "description": "The discriminated type identifier, which is always 'pinecone'." + }, + "parameters": { + "type": "object", + "properties": { + "top_n_documents": { + "type": "integer", + "format": "int32", + "description": "The configured number of documents to feature in the query." + }, + "in_scope": { + "type": "boolean", + "description": "Whether queries should be restricted to use of the indexed data." + }, + "strictness": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 5, + "description": "The configured strictness of the search relevance filtering.\nHigher strictness will increase precision but lower recall of the answer." + }, + "max_search_queries": { + "type": "integer", + "format": "int32", + "description": "The maximum number of rewritten queries that should be sent to the search provider for a single user message.\nBy default, the system will make an automatic determination." + }, + "allow_partial_result": { + "type": "boolean", + "description": "If set to true, the system will allow partial search results to be used and the request will fail if all\npartial queries fail. If not specified or specified as false, the request will fail if any search query fails.", + "default": false + }, + "include_contexts": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "citations", + "intent", + "all_retrieved_documents" + ] + }, + "maxItems": 3, + "description": "The output context properties to include on the response.\nBy default, citations and intent will be requested.", + "default": [ + "citations", + "intent" + ] + }, + "environment": { + "type": "string", + "description": "The environment name to use with Pinecone." + }, + "index_name": { + "type": "string", + "description": "The name of the Pinecone database index to use." + }, + "authentication": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceApiKeyAuthenticationOptions" + } + ], + "description": "The authentication mechanism to use with Pinecone.\nSupported authentication mechanisms for Pinecone include: API key." + }, + "embedding_dependency": { + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSourceVectorizationSource" + } + ], + "description": "The vectorization source to use as an embedding dependency for the Pinecone data source.\nSupported vectorization sources for Pinecone include: deployment name." + }, + "fields_mapping": { + "type": "object", + "properties": { + "content_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "title_field": { + "type": "string" + }, + "url_field": { + "type": "string" + }, + "filepath_field": { + "type": "string" + }, + "content_fields_separator": { + "type": "string" + } + }, + "required": [ + "content_fields" + ], + "description": "Field mappings to apply to data used by the Pinecone data source.\nNote that content field mappings are required for Pinecone." + } + }, + "required": [ + "environment", + "index_name", + "authentication", + "embedding_dependency", + "fields_mapping" + ], + "description": "The parameter information to control the use of the Pinecone data source." + } + }, + "allOf": [ + { + "$ref": "#/components/schemas/AzureChatDataSource" + } + ] + }, + "ResponseFormatJSONSchemaRequest": { + "type": "object", + "required": [ + "type", + "json_schema" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "json_schema" + ], + "description": "Type of response format" + }, + "json_schema": { + "type": "object", + "additionalProperties": {}, + "description": "JSON Schema for the response format" + } + } + }, + "ResponseModalities": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "text", + "audio" + ] + }, + "description": "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to\n[generate audio](/docs/guides/audio). To request that this model generate\nboth text and audio responses, you can use:\n\n`[\"text\", \"audio\"]`" + } + }, + "securitySchemes": { + "ApiKeyAuth": { + "type": "apiKey", + "in": "header", + "name": "api-key" + }, + "ApiKeyAuth_": { + "type": "apiKey", + "in": "header", + "name": "authorization" + }, + "OAuth2Auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + "scopes": { + "https://cognitiveservices.azure.com/.default": "" + } + } + } + } + } + }, + "servers": [ + { + "url": "https://{endpoint}.cognitiveservices.azure.com/openai/v1/", + "description": "Microsoft Foundry Models APIs", + "variables": { + "endpoint": { + "default": "", + "description": "A supported Microsoft Foundry Models APIs endpoint, including protocol and hostname.\nFor example:\nhttps://westus.api.cognitive.microsoft.com)." + } + } + } + ] +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/PassThrough.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/PassThrough.json new file mode 100644 index 000000000..99ed91673 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/PassThrough.json @@ -0,0 +1,105 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Pass-through API", + "version": "1.0" + }, + "servers": [{ + "url": "" + }], + "paths": { + "/*": { + "head": { + "summary": "HEAD", + "operationId": "68710975baba4a299d36deb0", + "responses": { + "200": { + "description": "" + } + } + }, + "patch": { + "summary": "PATCH", + "operationId": "68710975652cd979619f5a83", + "responses": { + "200": { + "description": "" + } + } + }, + "put": { + "summary": "PUT", + "operationId": "687109754b68aaea197b531c", + "responses": { + "200": { + "description": "" + } + } + }, + "delete": { + "summary": "DELETE", + "operationId": "68710975f9f8bdc276a491b6", + "responses": { + "200": { + "description": "" + } + } + }, + "get": { + "summary": "GET", + "operationId": "687109754f85de645e224a4a", + "responses": { + "200": { + "description": "" + } + } + }, + "options": { + "summary": "OPTIONS", + "operationId": "68710975b57233df8cb7124c", + "responses": { + "200": { + "description": "" + } + } + }, + "post": { + "summary": "POST", + "operationId": "687109756160cf7e2bfc0f6d", + "responses": { + "200": { + "description": "" + } + } + }, + "trace": { + "summary": "TRACE", + "operationId": "68710975245aeaa3eb6b126d", + "responses": { + "200": { + "description": "" + } + } + } + } + }, + "components": { + "securitySchemes": { + "apiKeyHeader": { + "type": "apiKey", + "name": "api-key", + "in": "header" + }, + "apiKeyQuery": { + "type": "apiKey", + "name": "subscription-key", + "in": "query" + } + } + }, + "security": [{ + "apiKeyHeader": [] + }, { + "apiKeyQuery": [] + }] +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-API.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-API.openapi.yaml new file mode 100644 index 000000000..b47d4f359 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-API.openapi.yaml @@ -0,0 +1,21443 @@ +openapi: 3.0.1 +info: + title: Universal-LLM-API + description: "The OpenAI REST API. Please see\nhttps://platform.openai.com/docs/api-reference for more details." + termsOfService: https://openai.com/policies/terms-of-use + contact: + name: OpenAI Support + url: https://help.openai.com/ + license: + name: MIT + url: https://github.com/openai/openai-openapi/blob/master/LICENSE + version: '1.0' +servers: + - url: https://apim-7c3dvrbie5sui.azure-api.net +paths: + /assistants: + get: + tags: + - Assistants + summary: Returns a list of assistants. + description: Returns a list of assistants. + operationId: listAssistants + parameters: + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListAssistantsResponse' + example: + object: list + data: + - id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + first_id: asst_abc123 + last_id: asst_abc456 + has_more: false + post: + tags: + - Assistants + summary: Create an assistant with a model and instructions. + description: Create an assistant with a model and instructions. + operationId: createAssistant + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateAssistantRequest' + example: + model: gpt-4o + name: string + description: string + instructions: string + reasoning_effort: medium + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AssistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + '/assistants/{assistant_id}': + get: + tags: + - Assistants + summary: Retrieves an assistant. + description: Retrieves an assistant. + operationId: getAssistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to retrieve. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AssistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + post: + tags: + - Assistants + summary: Modifies an assistant. + description: Modifies an assistant. + operationId: modifyAssistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to modify. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModifyAssistantRequest' + example: + model: { } + reasoning_effort: medium + name: string + description: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/AssistantObject' + example: + id: string + object: assistant + created_at: 0 + name: string + description: string + model: string + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + response_format: { } + delete: + tags: + - Assistants + summary: Delete an assistant. + description: Delete an assistant. + operationId: deleteAssistant + parameters: + - name: assistant_id + in: path + description: The ID of the assistant to delete. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteAssistantResponse' + example: + id: string + deleted: true + object: assistant.deleted + /audio/speech: + post: + tags: + - Audio + summary: Generates audio from the input text. + description: Generates audio from the input text. + operationId: createSpeech + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateSpeechRequest' + example: + model: { } + input: string + instructions: string + voice: ash + response_format: mp3 + speed: 1 + responses: + '200': + description: OK + headers: + Transfer-Encoding: + description: chunked + schema: + type: string + content: + application/octet-stream: + schema: + type: string + format: binary + examples: + default: + value: + /audio/transcriptions: + post: + tags: + - Audio + summary: Transcribes audio into the input language. + description: Transcribes audio into the input language. + operationId: createTranscription + requestBody: + content: + multipart/form-data: + schema: + required: + - file + - model + properties: + file: + type: string + description: "The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.\n" + format: binary + model: + type: string + description: "ID of the model to use. The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source Whisper V2 model).\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment. The [prompt](/docs/guides/speech-to-text#prompting) should match the audio language.\n" + response_format: + $ref: '#/components/schemas/AudioResponseFormat' + temperature: + type: number + description: "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + default: 0 + 'include[]': + type: array + description: "Additional information to include in the transcription response.\n`logprobs` will return the log probabilities of the tokens in the\nresponse to understand the model's confidence in the transcription.\n`logprobs` only works with response_format set to `json` and only with\nthe models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`.\n" + 'timestamp_granularities[]': + type: array + description: "The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.\n" + default: "[\n \"segment\"\n]" + stream: + type: boolean + description: "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). \nSee the [Streaming section of the Speech-to-Text guide](/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)\nfor more information.\n\nNote: Streaming is not supported for the `whisper-1` model and will be ignored.\n" + default: false + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/CreateTranscriptionResponseJson' + - $ref: '#/components/schemas/CreateTranscriptionResponseVerboseJson' + example: + text: string + logprobs: + - token: string + logprob: 0 + bytes: + - 0 + text/event-stream: + schema: + $ref: '#/components/schemas/CreateTranscriptionResponseStreamEvent' + examples: + default: + value: + /audio/translations: + post: + tags: + - Audio + summary: Translates audio into English. + description: Translates audio into English. + operationId: createTranslation + requestBody: + content: + multipart/form-data: + schema: + required: + - file + - model + properties: + file: + type: string + description: "The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.\n" + format: binary + model: + type: string + description: "ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is currently available.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment. The [prompt](/docs/guides/speech-to-text#prompting) should be in English.\n" + response_format: + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: "The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`.\n" + default: json + temperature: + type: number + description: "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + default: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/CreateTranslationResponseJson' + - $ref: '#/components/schemas/CreateTranslationResponseVerboseJson' + example: + text: string + /batches: + post: + tags: + - Batch + summary: Creates and executes a batch from an uploaded file of requests + description: Creates and executes a batch from an uploaded file of requests + operationId: createBatch + requestBody: + content: + application/json: + schema: + required: + - input_file_id + - endpoint + - completion_window + type: object + properties: + input_file_id: + type: string + description: "The ID of an uploaded file that contains requests for the new batch.\n\nSee [upload file](/docs/api-reference/files/create) for how to upload a file.\n\nYour input file must be formatted as a [JSONL file](/docs/api-reference/batch/request-input), and must be uploaded with the purpose `batch`. The file can contain up to 50,000 requests, and can be up to 200 MB in size.\n" + endpoint: + enum: + - /v1/responses + - /v1/chat/completions + - /v1/embeddings + - /v1/completions + type: string + description: "The endpoint to be used for all requests in the batch. Currently\n`/v1/responses`, `/v1/chat/completions`, `/v1/embeddings`,\nand `/v1/completions` are supported. Note that\n`/v1/embeddings` batches are also restricted to a maximum of\n50,000 embedding inputs across all requests in the batch." + completion_window: + enum: + - 24h + type: string + description: "The time frame within which the batch should be processed.\nCurrently only `24h` is supported." + metadata: + $ref: '#/components/schemas/Metadata' + example: + input_file_id: string + endpoint: /v1/responses + completion_window: 24h + metadata: { } + responses: + '200': + description: Batch created successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Batch' + example: + id: string + object: batch + endpoint: string + errors: + object: string + data: + - code: string + message: string + param: string + line: 0 + input_file_id: string + completion_window: string + status: validating + output_file_id: string + error_file_id: string + created_at: 0 + in_progress_at: 0 + expires_at: 0 + finalizing_at: 0 + completed_at: 0 + failed_at: 0 + expired_at: 0 + cancelling_at: 0 + cancelled_at: 0 + request_counts: + total: 0 + completed: 0 + failed: 0 + metadata: { } + get: + tags: + - Batch + summary: List your organization's batches. + description: List your organization's batches. + operationId: listBatches + parameters: + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + responses: + '200': + description: Batch listed successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/ListBatchesResponse' + example: + data: + - id: string + object: batch + endpoint: string + errors: + object: string + data: + - code: string + message: string + param: string + line: 0 + input_file_id: string + completion_window: string + status: validating + output_file_id: string + error_file_id: string + created_at: 0 + in_progress_at: 0 + expires_at: 0 + finalizing_at: 0 + completed_at: 0 + failed_at: 0 + expired_at: 0 + cancelling_at: 0 + cancelled_at: 0 + request_counts: + total: 0 + completed: 0 + failed: 0 + metadata: { } + first_id: batch_abc123 + last_id: batch_abc456 + has_more: true + object: list + '/batches/{batch_id}': + get: + tags: + - Batch + summary: Retrieves a batch. + description: Retrieves a batch. + operationId: retrieveBatch + parameters: + - name: batch_id + in: path + description: The ID of the batch to retrieve. + required: true + schema: + type: string + responses: + '200': + description: Batch retrieved successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/Batch' + example: + id: string + object: batch + endpoint: string + errors: + object: string + data: + - code: string + message: string + param: string + line: 0 + input_file_id: string + completion_window: string + status: validating + output_file_id: string + error_file_id: string + created_at: 0 + in_progress_at: 0 + expires_at: 0 + finalizing_at: 0 + completed_at: 0 + failed_at: 0 + expired_at: 0 + cancelling_at: 0 + cancelled_at: 0 + request_counts: + total: 0 + completed: 0 + failed: 0 + metadata: { } + '/batches/{batch_id}/cancel': + post: + tags: + - Batch + summary: Cancels an in-progress batch. + description: "Cancels an in-progress batch. The batch will be in status `cancelling`\nfor up to 10 minutes, before changing to `cancelled`, where it will have\npartial results (if any) available in the output file." + operationId: cancelBatch + parameters: + - name: batch_id + in: path + description: The ID of the batch to cancel. + required: true + schema: + type: string + responses: + '200': + description: Batch is cancelling. Returns the cancelling batch's details. + content: + application/json: + schema: + $ref: '#/components/schemas/Batch' + example: + id: string + object: batch + endpoint: string + errors: + object: string + data: + - code: string + message: string + param: string + line: 0 + input_file_id: string + completion_window: string + status: validating + output_file_id: string + error_file_id: string + created_at: 0 + in_progress_at: 0 + expires_at: 0 + finalizing_at: 0 + completed_at: 0 + failed_at: 0 + expired_at: 0 + cancelling_at: 0 + cancelled_at: 0 + request_counts: + total: 0 + completed: 0 + failed: 0 + metadata: { } + /chat/completions: + get: + tags: + - Chat + summary: List stored Chat Completions. + description: "List stored Chat Completions. Only Chat Completions that have been stored\nwith the `store` parameter set to `true` will be returned.\n" + operationId: listChatCompletions + parameters: + - name: model + in: query + description: The model used to generate the Chat Completions. + schema: + type: string + - name: metadata + in: query + description: "A list of metadata keys to filter the Chat Completions by. Example:\n\n`metadata[key1]=value1&metadata[key2]=value2`\n" + schema: + $ref: '#/components/schemas/Metadata' + - name: after + in: query + description: "Identifier for the last chat completion from the previous\npagination request." + schema: + type: string + - name: limit + in: query + description: Number of Chat Completions to retrieve. + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order for Chat Completions by timestamp. Use `asc` for\nascending order or `desc` for descending order. Defaults to `asc`." + schema: + enum: + - asc + - desc + type: string + default: asc + responses: + '200': + description: A list of Chat Completions + content: + application/json: + schema: + $ref: '#/components/schemas/ChatCompletionList' + example: + object: list + data: + - id: string + choices: + - finish_reason: stop + index: 0 + message: + content: string + refusal: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + annotations: + - type: url_citation + url_citation: + end_index: 0 + start_index: 0 + url: string + title: string + role: assistant + function_call: + arguments: string + name: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + refusal: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + created: 0 + model: string + service_tier: auto + system_fingerprint: string + object: chat.completion + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + first_id: string + last_id: string + has_more: true + post: + tags: + - Chat + summary: Creates a model response for the given chat conversation. + description: "Creates a model response for the given chat conversation. Learn more in the\n[text generation](/docs/guides/text-generation), [vision](/docs/guides/vision),\nand [audio](/docs/guides/audio) guides.\n\nParameter support can differ depending on the model used to generate the\nresponse, particularly for newer reasoning models. Parameters that are only\nsupported for reasoning models are noted below. For the current state of \nunsupported parameters in reasoning models, \n[refer to the reasoning guide](/docs/guides/reasoning).\n" + operationId: createChatCompletion + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionRequest' + example: + model: gpt-4o + messages: + - role: system + content: You are a helpful assistant + - role: user + content: How are you? + max_tokens: 50 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionResponse' + example: + id: string + choices: + - finish_reason: stop + index: 0 + message: + content: string + refusal: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + annotations: + - type: url_citation + url_citation: + end_index: 0 + start_index: 0 + url: string + title: string + role: assistant + function_call: + arguments: string + name: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + refusal: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + created: 0 + model: string + service_tier: auto + system_fingerprint: string + object: chat.completion + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + text/event-stream: + schema: + $ref: '#/components/schemas/CreateChatCompletionStreamResponse' + examples: + default: + value: + '/chat/completions/{completion_id}': + get: + tags: + - Chat + summary: Get a stored chat completion. + description: "Get a stored chat completion. Only Chat Completions that have been created\nwith the `store` parameter set to `true` will be returned.\n" + operationId: getChatCompletion + parameters: + - name: completion_id + in: path + description: The ID of the chat completion to retrieve. + required: true + schema: + type: string + responses: + '200': + description: A chat completion + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionResponse' + example: + id: string + choices: + - finish_reason: stop + index: 0 + message: + content: string + refusal: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + annotations: + - type: url_citation + url_citation: + end_index: 0 + start_index: 0 + url: string + title: string + role: assistant + function_call: + arguments: string + name: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + refusal: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + created: 0 + model: string + service_tier: auto + system_fingerprint: string + object: chat.completion + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + post: + tags: + - Chat + summary: Modify a stored chat completion. + description: "Modify a stored chat completion. Only Chat Completions that have been\ncreated with the `store` parameter set to `true` can be modified. Currently,\nthe only supported modification is to update the `metadata` field.\n" + operationId: updateChatCompletion + parameters: + - name: completion_id + in: path + description: The ID of the chat completion to update. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + required: + - metadata + type: object + properties: + metadata: + $ref: '#/components/schemas/Metadata' + example: + metadata: { } + responses: + '200': + description: A chat completion + content: + application/json: + schema: + $ref: '#/components/schemas/CreateChatCompletionResponse' + example: + id: string + choices: + - finish_reason: stop + index: 0 + message: + content: string + refusal: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + annotations: + - type: url_citation + url_citation: + end_index: 0 + start_index: 0 + url: string + title: string + role: assistant + function_call: + arguments: string + name: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + logprobs: + content: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + refusal: + - token: string + logprob: 0 + bytes: + - 0 + top_logprobs: + - token: string + logprob: 0 + bytes: + - 0 + created: 0 + model: string + service_tier: auto + system_fingerprint: string + object: chat.completion + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + delete: + tags: + - Chat + summary: Delete a stored chat completion. + description: "Delete a stored chat completion. Only Chat Completions that have been\ncreated with the `store` parameter set to `true` can be deleted.\n" + operationId: deleteChatCompletion + parameters: + - name: completion_id + in: path + description: The ID of the chat completion to delete. + required: true + schema: + type: string + responses: + '200': + description: The chat completion was deleted successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/ChatCompletionDeleted' + example: + object: chat.completion.deleted + id: string + deleted: true + '/chat/completions/{completion_id}/messages': + get: + tags: + - Chat + summary: Get the messages in a stored chat completion. + description: "Get the messages in a stored chat completion. Only Chat Completions that\nhave been created with the `store` parameter set to `true` will be\nreturned.\n" + operationId: getChatCompletionMessages + parameters: + - name: completion_id + in: path + description: The ID of the chat completion to retrieve messages from. + required: true + schema: + type: string + - name: after + in: query + description: Identifier for the last message from the previous pagination request. + schema: + type: string + - name: limit + in: query + description: Number of messages to retrieve. + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order for messages by timestamp. Use `asc` for ascending order\nor `desc` for descending order. Defaults to `asc`." + schema: + enum: + - asc + - desc + type: string + default: asc + responses: + '200': + description: A list of messages + content: + application/json: + schema: + $ref: '#/components/schemas/ChatCompletionMessageList' + example: + object: list + data: + - content: string + refusal: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + annotations: + - type: url_citation + url_citation: + end_index: 0 + start_index: 0 + url: string + title: string + role: assistant + function_call: + arguments: string + name: string + audio: + id: string + expires_at: 0 + data: string + transcript: string + id: string + first_id: string + last_id: string + has_more: true + /completions: + post: + tags: + - Completions + summary: Creates a completion for the provided prompt and parameters. + description: Creates a completion for the provided prompt and parameters. + operationId: createCompletion + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCompletionRequest' + example: + model: gpt-3.5-turbo-instruct + prompt: Say this is a test + max_tokens: 7 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateCompletionResponse' + example: + id: string + choices: + - finish_reason: stop + index: 0 + logprobs: + text_offset: + - 0 + token_logprobs: + - 0 + tokens: + - string + top_logprobs: + - { } + text: string + created: 0 + model: string + system_fingerprint: string + object: text_completion + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + accepted_prediction_tokens: 0 + audio_tokens: 0 + reasoning_tokens: 0 + rejected_prediction_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + /embeddings: + post: + tags: + - Embeddings + summary: Creates an embedding vector representing the input text. + description: Creates an embedding vector representing the input text. + operationId: createEmbedding + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEmbeddingRequest' + example: + model: text-embedding-ada-002 + input: How are you? + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEmbeddingResponse' + example: + data: + - index: 0 + embedding: + - 0 + object: embedding + model: string + object: list + usage: + prompt_tokens: 0 + total_tokens: 0 + /evals: + get: + tags: + - Evals + summary: List evaluations for a project. + description: "List evaluations for a project.\n" + operationId: listEvals + parameters: + - name: after + in: query + description: Identifier for the last eval from the previous pagination request. + schema: + type: string + - name: limit + in: query + description: Number of evals to retrieve. + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order for evals by timestamp. Use `asc` for ascending order or\n`desc` for descending order." + schema: + enum: + - asc + - desc + type: string + default: asc + - name: order_by + in: query + description: "Evals can be ordered by creation time or last updated time. Use\n`created_at` for creation time or `updated_at` for last updated time.\n" + schema: + enum: + - created_at + - updated_at + type: string + default: created_at + responses: + '200': + description: A list of evals + content: + application/json: + schema: + $ref: '#/components/schemas/EvalList' + example: + object: list + data: + - object: eval + id: string + name: Chatbot effectiveness Evaluation + data_source_config: + type: custom + schema: "{\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n}\n" + testing_criteria: eval + created_at: 0 + metadata: { } + first_id: string + last_id: string + has_more: true + post: + tags: + - Evals + summary: Create the structure of an evaluation that can be used to test a model's performance. + description: "Create the structure of an evaluation that can be used to test a model's performance.\nAn evaluation is a set of testing criteria and a datasource. After creating an evaluation, you can run it on different models and model parameters. We support several types of graders and datasources.\nFor more information, see the [Evals guide](/docs/guides/evals).\n" + operationId: createEval + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEvalRequest' + example: + name: string + metadata: { } + data_source_config: + type: custom + item_schema: "{\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\"type\": \"string\"},\n \"age\": {\"type\": \"integer\"}\n },\n \"required\": [\"name\", \"age\"]\n}\n" + include_sample_schema: false + testing_criteria: + - type: label_model + name: string + model: string + input: + - role: string + content: string + labels: + - string + passing_labels: + - string + responses: + '201': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Eval' + example: + object: eval + id: string + name: Chatbot effectiveness Evaluation + data_source_config: + type: custom + schema: "{\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n}\n" + testing_criteria: eval + created_at: 0 + metadata: { } + '/evals/{eval_id}': + get: + tags: + - Evals + summary: Get an evaluation by ID. + description: "Get an evaluation by ID.\n" + operationId: getEval + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to retrieve. + required: true + schema: + type: string + responses: + '200': + description: The evaluation + content: + application/json: + schema: + $ref: '#/components/schemas/Eval' + example: + object: eval + id: string + name: Chatbot effectiveness Evaluation + data_source_config: + type: custom + schema: "{\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n}\n" + testing_criteria: eval + created_at: 0 + metadata: { } + post: + tags: + - Evals + summary: Update certain properties of an evaluation. + description: "Update certain properties of an evaluation.\n" + operationId: updateEval + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to update. + required: true + schema: + type: string + requestBody: + description: Request to update an evaluation + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: Rename the evaluation. + metadata: + $ref: '#/components/schemas/Metadata' + example: + name: string + metadata: { } + responses: + '200': + description: The updated evaluation + content: + application/json: + schema: + $ref: '#/components/schemas/Eval' + example: + object: eval + id: string + name: Chatbot effectiveness Evaluation + data_source_config: + type: custom + schema: "{\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n}\n" + testing_criteria: eval + created_at: 0 + metadata: { } + delete: + tags: + - Evals + summary: Delete an evaluation. + description: "Delete an evaluation.\n" + operationId: deleteEval + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to delete. + required: true + schema: + type: string + responses: + '200': + description: Successfully deleted the evaluation. + content: + application/json: + schema: + required: + - object + - deleted + - eval_id + type: object + properties: + object: + type: string + example: eval.deleted + deleted: + type: boolean + example: true + eval_id: + type: string + example: eval_abc123 + example: + object: eval.deleted + deleted: true + eval_id: eval_abc123 + '404': + description: Evaluation not found. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: string + message: string + param: string + type: string + '/evals/{eval_id}/runs': + get: + tags: + - Evals + summary: Get a list of runs for an evaluation. + description: "Get a list of runs for an evaluation.\n" + operationId: getEvalRuns + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to retrieve runs for. + required: true + schema: + type: string + - name: after + in: query + description: Identifier for the last run from the previous pagination request. + schema: + type: string + - name: limit + in: query + description: Number of runs to retrieve. + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order for runs by timestamp. Use `asc` for ascending order or\n`desc` for descending order. Defaults to `asc`." + schema: + enum: + - asc + - desc + type: string + default: asc + - name: status + in: query + description: "Filter runs by status. One of `queued` | `in_progress` | `failed` |\n`completed` | `canceled`." + schema: + enum: + - queued + - in_progress + - completed + - canceled + - failed + type: string + responses: + '200': + description: A list of runs for the evaluation + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRunList' + example: + object: list + data: + - object: eval.run + id: string + eval_id: string + status: string + model: string + name: string + created_at: 0 + report_url: string + result_counts: + total: 0 + errored: 0 + failed: 0 + passed: 0 + per_model_usage: + - model_name: string + invocation_count: 0 + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + cached_tokens: 0 + per_testing_criteria_results: + - testing_criteria: string + passed: 0 + failed: 0 + data_source: + type: jsonl + source: + type: file_content + content: + - item: { } + sample: { } + metadata: { } + error: + code: string + message: string + first_id: string + last_id: string + has_more: true + post: + tags: + - Evals + summary: Create a new evaluation run. + description: "Create a new evaluation run. This is the endpoint that will kick off grading.\n" + operationId: createEvalRun + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to create a run for. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateEvalRunRequest' + example: + name: string + metadata: { } + data_source: + type: jsonl + source: + type: file_content + content: + - item: { } + sample: { } + responses: + '201': + description: Successfully created a run for the evaluation + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRun' + example: + object: eval.run + id: string + eval_id: string + status: string + model: string + name: string + created_at: 0 + report_url: string + result_counts: + total: 0 + errored: 0 + failed: 0 + passed: 0 + per_model_usage: + - model_name: string + invocation_count: 0 + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + cached_tokens: 0 + per_testing_criteria_results: + - testing_criteria: string + passed: 0 + failed: 0 + data_source: + type: jsonl + source: + type: file_content + content: + - item: { } + sample: { } + metadata: { } + error: + code: string + message: string + '400': + description: 'Bad request (for example, missing eval object)' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: string + message: string + param: string + type: string + '/evals/{eval_id}/runs/{run_id}': + get: + tags: + - Evals + summary: Get an evaluation run by ID. + description: "Get an evaluation run by ID.\n" + operationId: getEvalRun + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to retrieve runs for. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve. + required: true + schema: + type: string + responses: + '200': + description: The evaluation run + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRun' + example: + object: eval.run + id: string + eval_id: string + status: string + model: string + name: string + created_at: 0 + report_url: string + result_counts: + total: 0 + errored: 0 + failed: 0 + passed: 0 + per_model_usage: + - model_name: string + invocation_count: 0 + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + cached_tokens: 0 + per_testing_criteria_results: + - testing_criteria: string + passed: 0 + failed: 0 + data_source: + type: jsonl + source: + type: file_content + content: + - item: { } + sample: { } + metadata: { } + error: + code: string + message: string + post: + tags: + - Evals + summary: Cancel an ongoing evaluation run. + description: "Cancel an ongoing evaluation run.\n" + operationId: cancelEvalRun + parameters: + - name: eval_id + in: path + description: The ID of the evaluation whose run you want to cancel. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to cancel. + required: true + schema: + type: string + responses: + '200': + description: The canceled eval run object + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRun' + example: + object: eval.run + id: string + eval_id: string + status: string + model: string + name: string + created_at: 0 + report_url: string + result_counts: + total: 0 + errored: 0 + failed: 0 + passed: 0 + per_model_usage: + - model_name: string + invocation_count: 0 + prompt_tokens: 0 + completion_tokens: 0 + total_tokens: 0 + cached_tokens: 0 + per_testing_criteria_results: + - testing_criteria: string + passed: 0 + failed: 0 + data_source: + type: jsonl + source: + type: file_content + content: + - item: { } + sample: { } + metadata: { } + error: + code: string + message: string + delete: + tags: + - Evals + summary: Delete an eval run. + description: "Delete an eval run.\n" + operationId: deleteEvalRun + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to delete the run from. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to delete. + required: true + schema: + type: string + responses: + '200': + description: Successfully deleted the eval run + content: + application/json: + schema: + type: object + properties: + object: + type: string + example: eval.run.deleted + deleted: + type: boolean + example: true + run_id: + type: string + example: evalrun_677469f564d48190807532a852da3afb + example: + object: eval.run.deleted + deleted: true + run_id: evalrun_677469f564d48190807532a852da3afb + '404': + description: Run not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: string + message: string + param: string + type: string + '/evals/{eval_id}/runs/{run_id}/output_items': + get: + tags: + - Evals + summary: Get a list of output items for an evaluation run. + description: "Get a list of output items for an evaluation run.\n" + operationId: getEvalRunOutputItems + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to retrieve runs for. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve output items for. + required: true + schema: + type: string + - name: after + in: query + description: "Identifier for the last output item from the previous pagination\nrequest." + schema: + type: string + - name: limit + in: query + description: Number of output items to retrieve. + schema: + type: integer + default: 20 + - name: status + in: query + description: "Filter output items by status. Use `failed` to filter by failed output\nitems or `pass` to filter by passed output items.\n" + schema: + enum: + - fail + - pass + type: string + - name: order + in: query + description: "Sort order for output items by timestamp. Use `asc` for ascending\norder or `desc` for descending order. Defaults to `asc`." + schema: + enum: + - asc + - desc + type: string + default: asc + responses: + '200': + description: A list of output items for the evaluation run + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRunOutputItemList' + example: + object: list + data: + - object: eval.run.output_item + id: string + run_id: string + eval_id: string + created_at: 0 + status: string + datasource_item_id: 0 + datasource_item: { } + results: + - { } + sample: + input: + - role: string + content: string + output: + - role: string + content: string + finish_reason: string + model: string + usage: + total_tokens: 0 + completion_tokens: 0 + prompt_tokens: 0 + cached_tokens: 0 + error: + code: string + message: string + temperature: 0 + max_completion_tokens: 0 + top_p: 0 + seed: 0 + first_id: string + last_id: string + has_more: true + '/evals/{eval_id}/runs/{run_id}/output_items/{output_item_id}': + get: + tags: + - Evals + summary: Get an evaluation run output item by ID. + description: "Get an evaluation run output item by ID.\n" + operationId: getEvalRunOutputItem + parameters: + - name: eval_id + in: path + description: The ID of the evaluation to retrieve runs for. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve. + required: true + schema: + type: string + - name: output_item_id + in: path + description: The ID of the output item to retrieve. + required: true + schema: + type: string + responses: + '200': + description: The evaluation run output item + content: + application/json: + schema: + $ref: '#/components/schemas/EvalRunOutputItem' + example: + object: eval.run.output_item + id: string + run_id: string + eval_id: string + created_at: 0 + status: string + datasource_item_id: 0 + datasource_item: { } + results: + - { } + sample: + input: + - role: string + content: string + output: + - role: string + content: string + finish_reason: string + model: string + usage: + total_tokens: 0 + completion_tokens: 0 + prompt_tokens: 0 + cached_tokens: 0 + error: + code: string + message: string + temperature: 0 + max_completion_tokens: 0 + top_p: 0 + seed: 0 + /files: + get: + tags: + - Files + summary: Returns a list of files. + description: Returns a list of files. + operationId: listFiles + parameters: + - name: purpose + in: query + description: Only return files with the given purpose. + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 10,000, and the default is 10,000.\n" + schema: + type: integer + default: 10000 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFilesResponse' + example: + object: list + data: + - id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + first_id: file-abc123 + last_id: file-abc456 + has_more: false + post: + tags: + - Files + summary: Upload a file that can be used across various endpoints. + description: "Upload a file that can be used across various endpoints. Individual files can be up to 512 MB, and the size of all files uploaded by one organization can be up to 100 GB.\n\nThe Assistants API supports files up to 2 million tokens and of specific file types. See the [Assistants Tools guide](/docs/assistants/tools) for details.\n\nThe Fine-tuning API only supports `.jsonl` files. The input also has certain required formats for fine-tuning [chat](/docs/api-reference/fine-tuning/chat-input) or [completions](/docs/api-reference/fine-tuning/completions-input) models.\n\nThe Batch API only supports `.jsonl` files up to 200 MB in size. The input also has a specific required [format](/docs/api-reference/batch/request-input).\n\nPlease [contact us](https://help.openai.com/) if you need to increase these storage limits.\n" + operationId: createFile + requestBody: + content: + multipart/form-data: + schema: + required: + - file + - purpose + properties: + file: + type: string + description: "The File object (not file name) to be uploaded.\n" + format: binary + purpose: + enum: + - assistants + - batch + - fine-tune + - vision + - user_data + - evals + type: string + description: "The intended purpose of the uploaded file. One of: - `assistants`: Used in the Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used for eval data sets\n" + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/OpenAIFile' + example: + id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + '/files/{file_id}': + delete: + tags: + - Files + summary: Delete a file. + description: Delete a file. + operationId: deleteFile + parameters: + - name: file_id + in: path + description: The ID of the file to use for this request. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteFileResponse' + example: + id: string + object: file + deleted: true + get: + tags: + - Files + summary: Returns information about a specific file. + description: Returns information about a specific file. + operationId: retrieveFile + parameters: + - name: file_id + in: path + description: The ID of the file to use for this request. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/OpenAIFile' + example: + id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + '/files/{file_id}/content': + get: + tags: + - Files + summary: Returns the contents of the specified file. + description: Returns the contents of the specified file. + operationId: downloadFile + parameters: + - name: file_id + in: path + description: The ID of the file to use for this request. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + example: string + '/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions': + get: + tags: + - Fine-tuning + summary: List fine-tuning checkpoint permissions. + description: "**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).\n\nOrganization owners can use this endpoint to view all permissions for a fine-tuned model checkpoint.\n" + operationId: listFineTuningCheckpointPermissions + parameters: + - name: fine_tuned_model_checkpoint + in: path + description: "The ID of the fine-tuned model checkpoint to get permissions for.\n" + required: true + schema: + type: string + example: ft-AF1WoRqd3aJAHsqc9NY7iL8F + - name: project_id + in: query + description: The ID of the project to get permissions for. + schema: + type: string + - name: after + in: query + description: "Identifier for the last permission ID from the previous pagination\nrequest." + schema: + type: string + - name: limit + in: query + description: Number of permissions to retrieve. + schema: + type: integer + default: 10 + - name: order + in: query + description: The order in which to retrieve permissions. + schema: + enum: + - ascending + - descending + type: string + default: descending + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse' + example: + data: + - id: string + created_at: 0 + project_id: string + object: checkpoint.permission + object: list + first_id: string + last_id: string + has_more: true + post: + tags: + - Fine-tuning + summary: Create fine-tuning checkpoint permission. + description: "**NOTE:** Calling this endpoint requires an [admin API key](../admin-api-keys).\n\nThis enables organization owners to share fine-tuned models with other projects in their organization.\n" + operationId: createFineTuningCheckpointPermission + parameters: + - name: fine_tuned_model_checkpoint + in: path + description: "The ID of the fine-tuned model checkpoint to create a permission for.\n" + required: true + schema: + type: string + example: ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFineTuningCheckpointPermissionRequest' + example: + project_ids: + - string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFineTuningCheckpointPermissionResponse' + example: + data: + - id: string + created_at: 0 + project_id: string + object: checkpoint.permission + object: list + first_id: string + last_id: string + has_more: true + '/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}': + delete: + tags: + - Fine-tuning + summary: Delete fine-tuning checkpoint permission. + description: "**NOTE:** This endpoint requires an [admin API key](../admin-api-keys).\n\nOrganization owners can use this endpoint to delete a permission for a fine-tuned model checkpoint.\n" + operationId: deleteFineTuningCheckpointPermission + parameters: + - name: fine_tuned_model_checkpoint + in: path + description: "The ID of the fine-tuned model checkpoint to delete a permission for.\n" + required: true + schema: + type: string + example: ft:gpt-4o-mini-2024-07-18:org:weather:B7R9VjQd + - name: permission_id + in: path + description: "The ID of the fine-tuned model checkpoint permission to delete.\n" + required: true + schema: + type: string + example: cp_zc4Q7MP6XxulcVzj4MZdwsAB + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteFineTuningCheckpointPermissionResponse' + example: + id: string + object: checkpoint.permission + deleted: true + /fine_tuning/jobs: + post: + tags: + - Fine-tuning + summary: Creates a fine-tuning job which begins the process of creating a new model from a given dataset. + description: "Creates a fine-tuning job which begins the process of creating a new model from a given dataset.\n\nResponse includes details of the enqueued job including job status and the name of the fine-tuned models once complete.\n\n[Learn more about fine-tuning](/docs/guides/fine-tuning)\n" + operationId: createFineTuningJob + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateFineTuningJobRequest' + example: + model: gpt-4o-mini + training_file: file-abc123 + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + suffix: + validation_file: file-abc123 + integrations: + - type: { } + wandb: + project: my-wandb-project + name: string + entity: string + tags: + - custom-tag + seed: 42 + method: + type: supervised + supervised: + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + dpo: + hyperparameters: + beta: auto + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FineTuningJob' + example: + id: string + created_at: 0 + error: + code: string + message: string + param: string + fine_tuned_model: string + finished_at: 0 + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + model: string + object: fine_tuning.job + organization_id: string + result_files: + - file-abc123 + status: validating_files + trained_tokens: 0 + training_file: string + validation_file: string + integrations: + - type: wandb + wandb: + project: my-wandb-project + name: string + entity: string + tags: + - custom-tag + seed: 0 + estimated_finish: 0 + method: + type: supervised + supervised: + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + dpo: + hyperparameters: + beta: auto + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + metadata: { } + get: + tags: + - Fine-tuning + summary: List your organization's fine-tuning jobs + description: "List your organization's fine-tuning jobs\n" + operationId: listPaginatedFineTuningJobs + parameters: + - name: after + in: query + description: Identifier for the last job from the previous pagination request. + schema: + type: string + - name: limit + in: query + description: Number of fine-tuning jobs to retrieve. + schema: + type: integer + default: 20 + - name: metadata + in: query + description: "Optional metadata filter. To filter, use the syntax `metadata[k]=v`. Alternatively, set `metadata=null` to indicate no metadata.\n" + schema: + type: object + additionalProperties: + type: string + nullable: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListPaginatedFineTuningJobsResponse' + example: + data: + - id: string + created_at: 0 + error: + code: string + message: string + param: string + fine_tuned_model: string + finished_at: 0 + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + model: string + object: fine_tuning.job + organization_id: string + result_files: + - file-abc123 + status: validating_files + trained_tokens: 0 + training_file: string + validation_file: string + integrations: + - type: wandb + wandb: + project: my-wandb-project + name: string + entity: string + tags: + - custom-tag + seed: 0 + estimated_finish: 0 + method: + type: supervised + supervised: + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + dpo: + hyperparameters: + beta: auto + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + metadata: { } + has_more: true + object: list + '/fine_tuning/jobs/{fine_tuning_job_id}': + get: + tags: + - Fine-tuning + summary: Get info about a fine-tuning job. + description: "Get info about a fine-tuning job.\n\n[Learn more about fine-tuning](/docs/guides/fine-tuning)\n" + operationId: retrieveFineTuningJob + parameters: + - name: fine_tuning_job_id + in: path + description: "The ID of the fine-tuning job.\n" + required: true + schema: + type: string + example: ft-AF1WoRqd3aJAHsqc9NY7iL8F + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FineTuningJob' + example: + id: string + created_at: 0 + error: + code: string + message: string + param: string + fine_tuned_model: string + finished_at: 0 + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + model: string + object: fine_tuning.job + organization_id: string + result_files: + - file-abc123 + status: validating_files + trained_tokens: 0 + training_file: string + validation_file: string + integrations: + - type: wandb + wandb: + project: my-wandb-project + name: string + entity: string + tags: + - custom-tag + seed: 0 + estimated_finish: 0 + method: + type: supervised + supervised: + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + dpo: + hyperparameters: + beta: auto + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + metadata: { } + '/fine_tuning/jobs/{fine_tuning_job_id}/cancel': + post: + tags: + - Fine-tuning + summary: Immediately cancel a fine-tune job. + description: "Immediately cancel a fine-tune job.\n" + operationId: cancelFineTuningJob + parameters: + - name: fine_tuning_job_id + in: path + description: "The ID of the fine-tuning job to cancel.\n" + required: true + schema: + type: string + example: ft-AF1WoRqd3aJAHsqc9NY7iL8F + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/FineTuningJob' + example: + id: string + created_at: 0 + error: + code: string + message: string + param: string + fine_tuned_model: string + finished_at: 0 + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + model: string + object: fine_tuning.job + organization_id: string + result_files: + - file-abc123 + status: validating_files + trained_tokens: 0 + training_file: string + validation_file: string + integrations: + - type: wandb + wandb: + project: my-wandb-project + name: string + entity: string + tags: + - custom-tag + seed: 0 + estimated_finish: 0 + method: + type: supervised + supervised: + hyperparameters: + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + dpo: + hyperparameters: + beta: auto + batch_size: auto + learning_rate_multiplier: auto + n_epochs: auto + metadata: { } + '/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints': + get: + tags: + - Fine-tuning + summary: List checkpoints for a fine-tuning job. + description: "List checkpoints for a fine-tuning job.\n" + operationId: listFineTuningJobCheckpoints + parameters: + - name: fine_tuning_job_id + in: path + description: "The ID of the fine-tuning job to get checkpoints for.\n" + required: true + schema: + type: string + example: ft-AF1WoRqd3aJAHsqc9NY7iL8F + - name: after + in: query + description: "Identifier for the last checkpoint ID from the previous pagination\nrequest." + schema: + type: string + - name: limit + in: query + description: Number of checkpoints to retrieve. + schema: + type: integer + default: 10 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFineTuningJobCheckpointsResponse' + example: + data: + - id: string + created_at: 0 + fine_tuned_model_checkpoint: string + step_number: 0 + metrics: + step: 0 + train_loss: 0 + train_mean_token_accuracy: 0 + valid_loss: 0 + valid_mean_token_accuracy: 0 + full_valid_loss: 0 + full_valid_mean_token_accuracy: 0 + fine_tuning_job_id: string + object: fine_tuning.job.checkpoint + object: list + first_id: string + last_id: string + has_more: true + '/fine_tuning/jobs/{fine_tuning_job_id}/events': + get: + tags: + - Fine-tuning + summary: Get status updates for a fine-tuning job. + description: "Get status updates for a fine-tuning job.\n" + operationId: listFineTuningEvents + parameters: + - name: fine_tuning_job_id + in: path + description: "The ID of the fine-tuning job to get events for.\n" + required: true + schema: + type: string + example: ft-AF1WoRqd3aJAHsqc9NY7iL8F + - name: after + in: query + description: Identifier for the last event from the previous pagination request. + schema: + type: string + - name: limit + in: query + description: Number of events to retrieve. + schema: + type: integer + default: 20 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFineTuningJobEventsResponse' + example: + data: + - object: fine_tuning.job.event + id: string + created_at: 0 + level: info + message: string + type: message + data: { } + object: list + has_more: true + /images/edits: + post: + tags: + - Images + summary: "Creates an edited or extended image given one or more source images and\na prompt." + description: "Creates an edited or extended image given one or more source images and\na prompt. This endpoint only supports `gpt-image-1` and `dall-e-2`." + operationId: createImageEdit + requestBody: + content: + multipart/form-data: + schema: + required: + - image + - prompt + properties: + image: + type: string + description: "The image(s) to edit. Must be a supported image file or an array of images.\n\nFor `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less\nthan 25MB. You can provide up to 16 images.\n\nFor `dall-e-2`, you can only provide one image, and it should be a square\n`png` file less than 4MB.\n" + prompt: + type: string + description: "A text description of the desired image(s). The maximum length is\n1000 characters for `dall-e-2`, and 32000 characters for\n`gpt-image-1`." + mask: + type: string + description: "An additional image whose fully transparent areas (e.g. where alpha\nis zero) indicate where `image` should be edited. If there are\nmultiple images provided, the mask will be applied on the first\nimage. Must be a valid PNG file, less than 4MB, and have the same\ndimensions as `image`." + format: binary + model: + type: string + description: "The model to use for image generation. Only `dall-e-2` and\n`gpt-image-1` are supported. Defaults to `dall-e-2` unless a\nparameter specific to `gpt-image-1` is used." + default: '' + n: + type: integer + description: The number of images to generate. Must be between 1 and 10. + default: 1 + size: + enum: + - 256x256 + - 512x512 + - 1024x1024 + - 1536x1024 + - 1024x1536 + - auto + type: string + description: "The size of the generated images. Must be one of `1024x1024`,\n`1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default\nvalue) for `gpt-image-1`, and one of `256x256`, `512x512`, or\n`1024x1024` for `dall-e-2`." + default: 1024x1024 + response_format: + enum: + - url + - b64_json + type: string + description: "The format in which the generated images are returned. Must be one\nof `url` or `b64_json`. URLs are only valid for 60 minutes after the\nimage has been generated. This parameter is only supported for\n`dall-e-2`, as `gpt-image-1` will always return base64-encoded\nimages." + default: url + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + quality: + enum: + - standard + - low + - medium + - high + - auto + type: string + description: "The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`.\n" + default: auto + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ImagesResponse' + example: + created: 0 + data: + - b64_json: string + url: string + revised_prompt: string + usage: + total_tokens: 0 + input_tokens: 0 + output_tokens: 0 + input_tokens_details: + text_tokens: 0 + image_tokens: 0 + /images/generations: + post: + tags: + - Images + summary: Creates an image given a prompt. + description: "Creates an image given a prompt. [Learn more](/docs/guides/images).\n" + operationId: createImage + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateImageRequest' + example: + prompt: A cute baby sea otter + model: dall-e-2 + n: 1 + quality: auto + response_format: url + output_format: png + output_compression: 100 + size: auto + moderation: auto + background: auto + style: vivid + user: user-1234 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ImagesResponse' + example: + created: 0 + data: + - b64_json: string + url: string + revised_prompt: string + usage: + total_tokens: 0 + input_tokens: 0 + output_tokens: 0 + input_tokens_details: + text_tokens: 0 + image_tokens: 0 + /images/variations: + post: + tags: + - Images + summary: Creates a variation of a given image. + description: "Creates a variation of a given image. This endpoint only supports\n`dall-e-2`." + operationId: createImageVariation + requestBody: + content: + multipart/form-data: + schema: + required: + - image + properties: + image: + type: string + description: "The image to use as the basis for the variation(s). Must be a valid\nPNG file, less than 4MB, and square." + format: binary + model: + type: string + description: "The model to use for image generation. Only `dall-e-2` is supported\nat this time." + default: '' + n: + type: integer + description: The number of images to generate. Must be between 1 and 10. + default: 1 + response_format: + enum: + - url + - b64_json + type: string + description: "The format in which the generated images are returned. Must be one\nof `url` or `b64_json`. URLs are only valid for 60 minutes after the\nimage has been generated." + default: url + size: + enum: + - 256x256 + - 512x512 + - 1024x1024 + type: string + description: "The size of the generated images. Must be one of `256x256`,\n`512x512`, or `1024x1024`." + default: 1024x1024 + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ImagesResponse' + example: + created: 0 + data: + - b64_json: string + url: string + revised_prompt: string + usage: + total_tokens: 0 + input_tokens: 0 + output_tokens: 0 + input_tokens_details: + text_tokens: 0 + image_tokens: 0 + /models: + get: + tags: + - Models + summary: "Lists the currently available models, and provides basic information\nabout each one such as the owner and availability." + description: "Lists the currently available models, and provides basic information\nabout each one such as the owner and availability." + operationId: listModels + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListModelsResponse' + example: + object: list + data: + - id: string + created: 0 + object: model + owned_by: string + '/models/{model}': + get: + tags: + - Models + summary: "Retrieves a model instance, providing basic information about the model\nsuch as the owner and permissioning." + description: "Retrieves a model instance, providing basic information about the model\nsuch as the owner and permissioning." + operationId: retrieveModel + parameters: + - name: model + in: path + description: The ID of the model to use for this request + required: true + schema: + type: string + example: gpt-4o-mini + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Model' + example: + id: string + created: 0 + object: model + owned_by: string + delete: + tags: + - Models + summary: Delete a fine-tuned model. + description: "Delete a fine-tuned model. You must have the Owner role in your\norganization to delete a model." + operationId: deleteModel + parameters: + - name: model + in: path + description: The model to delete + required: true + schema: + type: string + example: ft:gpt-4o-mini:acemeco:suffix:abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteModelResponse' + example: + id: string + deleted: true + object: string + /moderations: + post: + tags: + - Moderations + summary: Classifies if text and/or image inputs are potentially harmful. + description: "Classifies if text and/or image inputs are potentially harmful. Learn\nmore in the [moderation guide](/docs/guides/moderation).\n" + operationId: createModeration + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateModerationRequest' + example: + input: { } + model: omni-moderation-latest + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/CreateModerationResponse' + example: + id: string + model: string + results: + - flagged: true + categories: + hate: true + hate/threatening: true + harassment: true + harassment/threatening: true + illicit: true + illicit/violent: true + self-harm: true + self-harm/intent: true + self-harm/instructions: true + sexual: true + sexual/minors: true + violence: true + violence/graphic: true + category_scores: + hate: 0 + hate/threatening: 0 + harassment: 0 + harassment/threatening: 0 + illicit: 0 + illicit/violent: 0 + self-harm: 0 + self-harm/intent: 0 + self-harm/instructions: 0 + sexual: 0 + sexual/minors: 0 + violence: 0 + violence/graphic: 0 + category_applied_input_types: + hate: + - text + hate/threatening: + - text + harassment: + - text + harassment/threatening: + - text + illicit: + - text + illicit/violent: + - text + self-harm: + - text + self-harm/intent: + - text + self-harm/instructions: + - text + sexual: + - text + sexual/minors: + - text + violence: + - text + violence/graphic: + - text + /realtime/sessions: + post: + tags: + - Realtime + summary: "Create an ephemeral API token for use in client-side applications with the\nRealtime API." + description: "Create an ephemeral API token for use in client-side applications with the\nRealtime API. Can be configured with the same session parameters as the\n`session.update` client event.\n\nIt responds with a session object, plus a `client_secret` key which contains\na usable ephemeral API token that can be used to authenticate browser clients\nfor the Realtime API.\n" + operationId: create-realtime-session + requestBody: + description: Create an ephemeral API key with the given session configuration. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeSessionCreateRequest' + example: + modalities: { } + model: gpt-4o-realtime-preview + instructions: string + voice: ash + input_audio_format: pcm16 + output_audio_format: pcm16 + input_audio_transcription: + model: string + language: string + prompt: string + turn_detection: + type: server_vad + eagerness: auto + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + create_response: true + interrupt_response: true + input_audio_noise_reduction: + tools: + - type: function + name: string + description: string + parameters: { } + tool_choice: auto + temperature: 0.8 + max_response_output_tokens: { } + responses: + '200': + description: Session created successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeSessionCreateResponse' + example: + client_secret: + value: string + expires_at: 0 + modalities: { } + instructions: string + voice: ash + input_audio_format: string + output_audio_format: string + input_audio_transcription: + model: string + turn_detection: + type: string + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + tools: + - type: function + name: string + description: string + parameters: { } + tool_choice: string + temperature: 0 + max_response_output_tokens: { } + /realtime/transcription_sessions: + post: + tags: + - Realtime + summary: "Create an ephemeral API token for use in client-side applications with the\nRealtime API specifically for realtime transcriptions." + description: "Create an ephemeral API token for use in client-side applications with the\nRealtime API specifically for realtime transcriptions.\nCan be configured with the same session parameters as the `transcription_session.update` client event.\n\nIt responds with a session object, plus a `client_secret` key which contains\na usable ephemeral API token that can be used to authenticate browser clients\nfor the Realtime API.\n" + operationId: create-realtime-transcription-session + requestBody: + description: Create an ephemeral API key with the given session configuration. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest' + example: + modalities: { } + input_audio_format: pcm16 + input_audio_transcription: + model: gpt-4o-transcribe + language: string + prompt: string + turn_detection: + type: server_vad + eagerness: auto + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + create_response: true + interrupt_response: true + input_audio_noise_reduction: + include: + - string + responses: + '200': + description: Session created successfully. + content: + application/json: + schema: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse' + example: + client_secret: + value: string + expires_at: 0 + modalities: { } + input_audio_format: string + input_audio_transcription: + model: gpt-4o-transcribe + language: string + prompt: string + turn_detection: + type: string + threshold: 0 + prefix_padding_ms: 0 + silence_duration_ms: 0 + /responses: + post: + tags: + - Responses + summary: Creates a model response. + description: "Creates a model response. Provide [text](/docs/guides/text) or\n[image](/docs/guides/images) inputs to generate [text](/docs/guides/text)\nor [JSON](/docs/guides/structured-outputs) outputs. Have the model call\nyour own [custom code](/docs/guides/function-calling) or use built-in\n[tools](/docs/guides/tools) like [web search](/docs/guides/tools-web-search)\nor [file search](/docs/guides/tools-file-search) to use your own data\nas input for the model's response.\n" + operationId: createResponse + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateResponse' + example: + model: gpt-4o + input: How are you? + stream: false + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + example: + metadata: { } + temperature: 1 + top_p: 1 + user: user-1234 + service_tier: auto + previous_response_id: string + model: gpt-4o + reasoning: + effort: medium + description: auto + generate_summary: auto + max_output_tokens: 0 + instructions: string + text: + format: + type: text + tools: + - type: file_search + vector_store_ids: + - string + max_num_results: 0 + ranking_options: + ranker: auto + score_threshold: 0 + filters: + type: eq + key: string + value: { } + tool_choice: { } + truncation: disabled + id: string + object: response + status: completed + created_at: 0 + error: + code: server_error + message: string + incomplete_details: + reason: max_output_tokens + output: + - id: string + type: message + role: assistant + content: + - type: output_text + text: string + annotations: + - type: file_citation + file_id: string + index: 0 + status: in_progress + output_text: string + usage: + input_tokens: 0 + input_tokens_details: + cached_tokens: 0 + output_tokens: 0 + output_tokens_details: + reasoning_tokens: 0 + total_tokens: 0 + parallel_tool_calls: true + text/event-stream: + schema: + $ref: '#/components/schemas/ResponseStreamEvent' + examples: + default: + value: + '/responses/{response_id}': + get: + tags: + - Responses + summary: Retrieves a model response with the given ID. + description: "Retrieves a model response with the given ID.\n" + operationId: getResponse + parameters: + - name: response_id + in: path + description: The ID of the response to retrieve. + required: true + schema: + type: string + example: resp_677efb5139a88190b512bc3fef8e535d + - name: include + in: query + description: "Additional fields to include in the response. See the `include`\nparameter for Response creation above for more information.\n" + schema: + type: array + items: + $ref: '#/components/schemas/Includable' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Response' + example: + metadata: { } + temperature: 1 + top_p: 1 + user: user-1234 + service_tier: auto + previous_response_id: string + model: gpt-4o + reasoning: + effort: medium + description: auto + generate_summary: auto + max_output_tokens: 0 + instructions: string + text: + format: + type: text + tools: + - type: file_search + vector_store_ids: + - string + max_num_results: 0 + ranking_options: + ranker: auto + score_threshold: 0 + filters: + type: eq + key: string + value: { } + tool_choice: { } + truncation: disabled + id: string + object: response + status: completed + created_at: 0 + error: + code: server_error + message: string + incomplete_details: + reason: max_output_tokens + output: + - id: string + type: message + role: assistant + content: + - type: output_text + text: string + annotations: + - type: file_citation + file_id: string + index: 0 + status: in_progress + output_text: string + usage: + input_tokens: 0 + input_tokens_details: + cached_tokens: 0 + output_tokens: 0 + output_tokens_details: + reasoning_tokens: 0 + total_tokens: 0 + parallel_tool_calls: true + delete: + tags: + - Responses + summary: Deletes a model response with the given ID. + description: "Deletes a model response with the given ID.\n" + operationId: deleteResponse + parameters: + - name: response_id + in: path + description: The ID of the response to delete. + required: true + schema: + type: string + example: resp_677efb5139a88190b512bc3fef8e535d + responses: + '200': + description: OK + '404': + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: string + message: string + param: string + type: string + '/responses/{response_id}/input_items': + get: + tags: + - Responses + summary: Returns a list of input items for a given response. + description: Returns a list of input items for a given response. + operationId: listInputItems + parameters: + - name: response_id + in: path + description: The ID of the response to retrieve input items for. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between\n1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "The order to return the input items in. Default is `asc`.\n- `asc`: Return the input items in ascending order.\n- `desc`: Return the input items in descending order.\n" + schema: + enum: + - asc + - desc + type: string + - name: after + in: query + description: "An item ID to list items after, used in pagination.\n" + schema: + type: string + - name: before + in: query + description: "An item ID to list items before, used in pagination.\n" + schema: + type: string + - name: include + in: query + description: "Additional fields to include in the response. See the `include`\nparameter for Response creation above for more information.\n" + schema: + type: array + items: + $ref: '#/components/schemas/Includable' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ResponseItemList' + example: + object: list + data: + - type: message + role: user + status: in_progress + content: + - type: input_text + text: string + id: string + has_more: true + first_id: string + last_id: string + /threads: + post: + tags: + - Assistants + summary: Create a thread. + description: Create a thread. + operationId: createThread + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateThreadRequest' + example: + messages: + - role: user + content: { } + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ThreadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + /threads/runs: + post: + tags: + - Assistants + summary: Create a thread and run it in one request. + description: Create a thread and run it in one request. + operationId: createThreadAndRun + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateThreadAndRunRequest' + example: + assistant_id: string + thread: + messages: + - role: user + content: { } + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + vector_stores: + - file_ids: + - string + chunking_strategy: + type: auto + metadata: { } + metadata: { } + model: gpt-4o + instructions: string + tools: + - type: code_interpreter + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + temperature: 1 + top_p: 1 + stream: true + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}': + get: + tags: + - Assistants + summary: Retrieves a thread. + description: Retrieves a thread. + operationId: getThread + parameters: + - name: thread_id + in: path + description: The ID of the thread to retrieve. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ThreadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + post: + tags: + - Assistants + summary: Modifies a thread. + description: Modifies a thread. + operationId: modifyThread + parameters: + - name: thread_id + in: path + description: The ID of the thread to modify. Only the `metadata` can be modified. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModifyThreadRequest' + example: + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ThreadObject' + example: + id: string + object: thread + created_at: 0 + tool_resources: + code_interpreter: + file_ids: + - string + file_search: + vector_store_ids: + - string + metadata: { } + delete: + tags: + - Assistants + summary: Delete a thread. + description: Delete a thread. + operationId: deleteThread + parameters: + - name: thread_id + in: path + description: The ID of the thread to delete. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteThreadResponse' + example: + id: string + deleted: true + object: thread.deleted + '/threads/{thread_id}/messages': + get: + tags: + - Assistants + summary: Returns a list of messages for a given thread. + description: Returns a list of messages for a given thread. + operationId: listMessages + parameters: + - name: thread_id + in: path + description: "The ID of the [thread](/docs/api-reference/threads) the messages\nbelong to." + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: run_id + in: query + description: "Filter messages by the run ID that generated them.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListMessagesResponse' + example: + object: list + data: + - id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + detail: auto + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + first_id: msg_abc123 + last_id: msg_abc123 + has_more: false + post: + tags: + - Assistants + summary: Create a message. + description: Create a message. + operationId: createMessage + parameters: + - name: thread_id + in: path + description: "The ID of the [thread](/docs/api-reference/threads) to create a\nmessage for." + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateMessageRequest' + example: + role: user + content: { } + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/MessageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + detail: auto + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + '/threads/{thread_id}/messages/{message_id}': + get: + tags: + - Assistants + summary: Retrieve a message. + description: Retrieve a message. + operationId: getMessage + parameters: + - name: thread_id + in: path + description: "The ID of the [thread](/docs/api-reference/threads) to which this\nmessage belongs." + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to retrieve. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/MessageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + detail: auto + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + post: + tags: + - Assistants + summary: Modifies a message. + description: Modifies a message. + operationId: modifyMessage + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this message belongs. + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to modify. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModifyMessageRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/MessageObject' + example: + id: string + object: thread.message + created_at: 0 + thread_id: string + status: in_progress + incomplete_details: + reason: content_filter + completed_at: 0 + incomplete_at: 0 + role: user + content: + - type: image_file + image_file: + file_id: string + detail: auto + assistant_id: string + run_id: string + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + delete: + tags: + - Assistants + summary: Deletes a message. + description: Deletes a message. + operationId: deleteMessage + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this message belongs. + required: true + schema: + type: string + - name: message_id + in: path + description: The ID of the message to delete. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteMessageResponse' + example: + id: string + deleted: true + object: thread.message.deleted + '/threads/{thread_id}/runs': + get: + tags: + - Assistants + summary: Returns a list of runs belonging to a thread. + description: Returns a list of runs belonging to a thread. + operationId: listRuns + parameters: + - name: thread_id + in: path + description: The ID of the thread the run belongs to. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListRunsResponse' + example: + object: list + data: + - id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + first_id: run_abc123 + last_id: run_abc456 + has_more: false + post: + tags: + - Assistants + summary: Create a run. + description: Create a run. + operationId: createRun + parameters: + - name: thread_id + in: path + description: The ID of the thread to run. + required: true + schema: + type: string + - name: 'include[]' + in: query + description: "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n\nSee the [file search tool documentation](/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.\n" + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateRunRequest' + example: + assistant_id: string + model: gpt-4o + reasoning_effort: medium + instructions: string + additional_instructions: string + additional_messages: + - role: user + content: { } + attachments: + - file_id: string + tools: + - type: code_interpreter + metadata: { } + tools: + - type: code_interpreter + metadata: { } + temperature: 1 + top_p: 1 + stream: true + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}': + get: + tags: + - Assistants + summary: Retrieves a run. + description: Retrieves a run. + operationId: getRun + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to retrieve. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + post: + tags: + - Assistants + summary: Modifies a run. + description: Modifies a run. + operationId: modifyRun + parameters: + - name: thread_id + in: path + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to modify. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModifyRunRequest' + example: + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}/cancel': + post: + tags: + - Assistants + summary: Cancels a run that is `in_progress`. + description: Cancels a run that is `in_progress`. + operationId: cancelRun + parameters: + - name: thread_id + in: path + description: The ID of the thread to which this run belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to cancel. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + '/threads/{thread_id}/runs/{run_id}/steps': + get: + tags: + - Assistants + summary: Returns a list of run steps belonging to a run. + description: Returns a list of run steps belonging to a run. + operationId: listRunSteps + parameters: + - name: thread_id + in: path + description: The ID of the thread the run and run steps belong to. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run the run steps belong to. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: 'include[]' + in: query + description: "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n\nSee the [file search tool documentation](/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.\n" + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListRunStepsResponse' + example: + object: list + data: + - id: string + object: thread.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + first_id: step_abc123 + last_id: step_abc456 + has_more: false + '/threads/{thread_id}/runs/{run_id}/steps/{step_id}': + get: + tags: + - Assistants + summary: Retrieves a run step. + description: Retrieves a run step. + operationId: getRunStep + parameters: + - name: thread_id + in: path + description: The ID of the thread to which the run and run step belongs. + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run to which the run step belongs. + required: true + schema: + type: string + - name: step_id + in: path + description: The ID of the run step to retrieve. + required: true + schema: + type: string + - name: 'include[]' + in: query + description: "A list of additional fields to include in the response. Currently the only supported value is `step_details.tool_calls[*].file_search.results[*].content` to fetch the file search result content.\n\nSee the [file search tool documentation](/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.\n" + schema: + type: array + items: + enum: + - 'step_details.tool_calls[*].file_search.results[*].content' + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunStepObject' + example: + id: string + object: thread.run.step + created_at: 0 + assistant_id: string + thread_id: string + run_id: string + type: message_creation + status: in_progress + step_details: + type: message_creation + message_creation: + message_id: string + last_error: + code: server_error + message: string + expired_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + '/threads/{thread_id}/runs/{run_id}/submit_tool_outputs': + post: + tags: + - Assistants + summary: 'When a run has the `status: "requires_action"` and `required_action.' + description: "When a run has the `status: \"requires_action\"` and `required_action.type` is `submit_tool_outputs`, this endpoint can be used to submit the outputs from the tool calls once they're all completed. All outputs must be submitted in a single request.\n" + operationId: submitToolOuputsToRun + parameters: + - name: thread_id + in: path + description: "The ID of the [thread](/docs/api-reference/threads) to which this\nrun belongs." + required: true + schema: + type: string + - name: run_id + in: path + description: The ID of the run that requires the tool output submission. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitToolOutputsRunRequest' + example: + tool_outputs: + - tool_call_id: string + output: string + stream: true + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/RunObject' + example: + id: string + object: thread.run + created_at: 0 + thread_id: string + assistant_id: string + status: queued + required_action: + type: submit_tool_outputs + submit_tool_outputs: + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + last_error: + code: server_error + message: string + expires_at: 0 + started_at: 0 + cancelled_at: 0 + failed_at: 0 + completed_at: 0 + incomplete_details: + reason: max_completion_tokens + model: string + instructions: string + tools: + - type: code_interpreter + metadata: { } + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + temperature: 0 + top_p: 0 + max_prompt_tokens: 0 + max_completion_tokens: 0 + truncation_strategy: + type: auto + last_messages: 0 + tool_choice: { } + parallel_tool_calls: true + response_format: { } + /uploads: + post: + tags: + - Uploads + summary: Creates an intermediate Upload object that you can add Parts to. + description: "Creates an intermediate [Upload](/docs/api-reference/uploads/object) object\nthat you can add [Parts](/docs/api-reference/uploads/part-object) to.\nCurrently, an Upload can accept at most 8 GB in total and expires after an\nhour after you create it.\n\nOnce you complete the Upload, we will create a\n[File](/docs/api-reference/files/object) object that contains all the parts\nyou uploaded. This File is usable in the rest of our platform as a regular\nFile object.\n\nFor certain `purpose` values, the correct `mime_type` must be specified.\nPlease refer to documentation for the\n[supported MIME types for your use case](/docs/assistants/tools/file-search#supported-files).\n\nFor guidance on the proper filename extensions for each purpose, please\nfollow the documentation on [creating a\nFile](/docs/api-reference/files/create).\n" + operationId: createUpload + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUploadRequest' + example: + filename: string + purpose: assistants + bytes: 0 + mime_type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Upload' + example: + id: string + created_at: 0 + filename: string + bytes: 0 + purpose: string + status: pending + expires_at: 0 + object: upload + file: + id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + '/uploads/{upload_id}/cancel': + post: + tags: + - Uploads + summary: Cancels the Upload. + description: "Cancels the Upload. No Parts may be added after an Upload is cancelled.\n" + operationId: cancelUpload + parameters: + - name: upload_id + in: path + description: "The ID of the Upload.\n" + required: true + schema: + type: string + example: upload_abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Upload' + example: + id: string + created_at: 0 + filename: string + bytes: 0 + purpose: string + status: pending + expires_at: 0 + object: upload + file: + id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + '/uploads/{upload_id}/complete': + post: + tags: + - Uploads + summary: 'Completes the [Upload](/docs/api-reference/uploads/object).' + description: "Completes the [Upload](/docs/api-reference/uploads/object).\n\nWithin the returned Upload object, there is a nested [File](/docs/api-reference/files/object) object that is ready to use in the rest of the platform.\n\nYou can specify the order of the Parts by passing in an ordered list of the Part IDs.\n\nThe number of bytes uploaded upon completion must match the number of bytes initially specified when creating the Upload object. No Parts may be added after an Upload is completed.\n" + operationId: completeUpload + parameters: + - name: upload_id + in: path + description: "The ID of the Upload.\n" + required: true + schema: + type: string + example: upload_abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CompleteUploadRequest' + example: + part_ids: + - string + md5: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Upload' + example: + id: string + created_at: 0 + filename: string + bytes: 0 + purpose: string + status: pending + expires_at: 0 + object: upload + file: + id: string + bytes: 0 + created_at: 0 + expires_at: 0 + filename: string + object: file + purpose: assistants + status: uploaded + status_details: string + '/uploads/{upload_id}/parts': + post: + tags: + - Uploads + summary: Adds a Part to an Upload object. + description: "Adds a [Part](/docs/api-reference/uploads/part-object) to an [Upload](/docs/api-reference/uploads/object) object. A Part represents a chunk of bytes from the file you are trying to upload.\n\nEach Part can be at most 64 MB, and you can add Parts until you hit the Upload maximum of 8 GB.\n\nIt is possible to add multiple Parts in parallel. You can decide the intended order of the Parts when you [complete the Upload](/docs/api-reference/uploads/complete).\n" + operationId: addUploadPart + parameters: + - name: upload_id + in: path + description: "The ID of the Upload.\n" + required: true + schema: + type: string + example: upload_abc123 + requestBody: + content: + multipart/form-data: + schema: + required: + - data + properties: + data: + type: string + description: "The chunk of bytes for this Part.\n" + format: binary + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/UploadPart' + example: + id: string + created_at: 0 + upload_id: string + object: upload.part + /vector_stores: + get: + tags: + - Vector stores + summary: Returns a list of vector stores. + description: Returns a list of vector stores. + operationId: listVectorStores + parameters: + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListVectorStoresResponse' + example: + object: list + data: + - id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + first_id: vs_abc123 + last_id: vs_abc456 + has_more: false + post: + tags: + - Vector stores + summary: Create a vector store. + description: Create a vector store. + operationId: createVectorStore + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateVectorStoreRequest' + example: + file_ids: + - string + name: string + expires_after: + anchor: last_active_at + days: 0 + chunking_strategy: + type: auto + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + '/vector_stores/{vector_store_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store. + description: Retrieves a vector store. + operationId: getVectorStore + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to retrieve. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + post: + tags: + - Vector stores + summary: Modifies a vector store. + description: Modifies a vector store. + operationId: modifyVectorStore + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to modify. + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateVectorStoreRequest' + example: + name: string + expires_after: + anchor: last_active_at + days: 0 + metadata: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreObject' + example: + id: string + object: vector_store + created_at: 0 + name: string + usage_bytes: 0 + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + status: expired + expires_after: + anchor: last_active_at + days: 0 + expires_at: 0 + last_active_at: 0 + metadata: { } + delete: + tags: + - Vector stores + summary: Delete a vector store. + description: Delete a vector store. + operationId: deleteVectorStore + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to delete. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteVectorStoreResponse' + example: + id: string + deleted: true + object: vector_store.deleted + '/vector_stores/{vector_store_id}/file_batches': + post: + tags: + - Vector stores + summary: Create a vector store file batch. + description: Create a vector store file batch. + operationId: createVectorStoreFileBatch + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File Batch.\n" + required: true + schema: + type: string + example: vs_abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateVectorStoreFileBatchRequest' + example: + file_ids: + - string + chunking_strategy: + type: auto + attributes: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store file batch. + description: Retrieves a vector store file batch. + operationId: getVectorStoreFileBatch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: batch_id + in: path + description: The ID of the file batch being retrieved. + required: true + schema: + type: string + example: vsfb_abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel': + post: + tags: + - Vector stores + summary: Cancel a vector store file batch. + description: "Cancel a vector store file batch. This attempts to cancel the\nprocessing of files in this batch as soon as possible." + operationId: cancelVectorStoreFileBatch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file batch belongs to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch to cancel. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileBatchObject' + example: + id: string + object: vector_store.files_batch + created_at: 0 + vector_store_id: string + status: in_progress + file_counts: + in_progress: 0 + completed: 0 + failed: 0 + cancelled: 0 + total: 0 + '/vector_stores/{vector_store_id}/file_batches/{batch_id}/files': + get: + tags: + - Vector stores + summary: Returns a list of vector store files in a batch. + description: Returns a list of vector store files in a batch. + operationId: listFilesInVectorStoreBatch + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: batch_id + in: path + description: The ID of the file batch that the files belong to. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: "Filter by file status. One of `in_progress`, `completed`, `failed`,\n`cancelled`." + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: static + static: + max_chunk_size_tokens: 0 + chunk_overlap_tokens: 0 + attributes: { } + first_id: file-abc123 + last_id: file-abc456 + has_more: false + '/vector_stores/{vector_store_id}/files': + get: + tags: + - Vector stores + summary: Returns a list of vector store files. + description: Returns a list of vector store files. + operationId: listVectorStoreFiles + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the files belong to. + required: true + schema: + type: string + - name: limit + in: query + description: "A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 20.\n" + schema: + type: integer + default: 20 + - name: order + in: query + description: "Sort order by the `created_at` timestamp of the objects. `asc` for ascending order and `desc` for descending order.\n" + schema: + enum: + - asc + - desc + type: string + default: desc + - name: after + in: query + description: "A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.\n" + schema: + type: string + - name: before + in: query + description: "A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.\n" + schema: + type: string + - name: filter + in: query + description: "Filter by file status. One of `in_progress`, `completed`, `failed`,\n`cancelled`." + schema: + enum: + - in_progress + - completed + - failed + - cancelled + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListVectorStoreFilesResponse' + example: + object: list + data: + - id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: static + static: + max_chunk_size_tokens: 0 + chunk_overlap_tokens: 0 + attributes: { } + first_id: file-abc123 + last_id: file-abc456 + has_more: false + post: + tags: + - Vector stores + summary: Create a vector store file by attaching a File to a vector store. + description: "Create a vector store file by attaching a\n[File](/docs/api-reference/files) to a [vector\nstore](/docs/api-reference/vector-stores/object)." + operationId: createVectorStoreFile + parameters: + - name: vector_store_id + in: path + description: "The ID of the vector store for which to create a File.\n" + required: true + schema: + type: string + example: vs_abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateVectorStoreFileRequest' + example: + file_id: string + chunking_strategy: + type: auto + attributes: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: static + static: + max_chunk_size_tokens: 0 + chunk_overlap_tokens: 0 + attributes: { } + '/vector_stores/{vector_store_id}/files/{file_id}': + get: + tags: + - Vector stores + summary: Retrieves a vector store file. + description: Retrieves a vector store file. + operationId: getVectorStoreFile + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file being retrieved. + required: true + schema: + type: string + example: file-abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: static + static: + max_chunk_size_tokens: 0 + chunk_overlap_tokens: 0 + attributes: { } + delete: + tags: + - Vector stores + summary: Delete a vector store file. + description: "Delete a vector store file. This will remove the file from the vector\nstore but the file itself will not be deleted. To delete the file, use\nthe [delete file](/docs/api-reference/files/delete) endpoint." + operationId: deleteVectorStoreFile + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store that the file belongs to. + required: true + schema: + type: string + - name: file_id + in: path + description: The ID of the file to delete. + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/DeleteVectorStoreFileResponse' + example: + id: string + deleted: true + object: vector_store.file.deleted + post: + tags: + - Vector stores + summary: Update attributes on a vector store file. + description: Update attributes on a vector store file. + operationId: updateVectorStoreFileAttributes + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store the file belongs to. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file to update attributes. + required: true + schema: + type: string + example: file-abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateVectorStoreFileAttributesRequest' + example: + attributes: { } + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileObject' + example: + id: string + object: vector_store.file + usage_bytes: 0 + created_at: 0 + vector_store_id: string + status: in_progress + last_error: + code: server_error + message: string + chunking_strategy: + type: static + static: + max_chunk_size_tokens: 0 + chunk_overlap_tokens: 0 + attributes: { } + '/vector_stores/{vector_store_id}/files/{file_id}/content': + get: + tags: + - Vector stores + summary: Retrieve the parsed contents of a vector store file. + description: Retrieve the parsed contents of a vector store file. + operationId: retrieveVectorStoreFileContent + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store. + required: true + schema: + type: string + example: vs_abc123 + - name: file_id + in: path + description: The ID of the file within the vector store. + required: true + schema: + type: string + example: file-abc123 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreFileContentResponse' + example: + object: vector_store.file_content.page + data: + - type: string + text: string + has_more: true + next_page: string + '/vector_stores/{vector_store_id}/search': + post: + tags: + - Vector stores + summary: "Search a vector store for relevant chunks based on a query and file\nattributes filter." + description: "Search a vector store for relevant chunks based on a query and file\nattributes filter." + operationId: searchVectorStore + parameters: + - name: vector_store_id + in: path + description: The ID of the vector store to search. + required: true + schema: + type: string + example: vs_abc123 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreSearchRequest' + example: + query: { } + rewrite_query: false + max_num_results: 10 + filters: + type: eq + key: string + value: { } + ranking_options: + ranker: auto + score_threshold: 0 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VectorStoreSearchResultsPage' + example: + object: vector_store.search_results.page + search_query: + - string + data: + - file_id: string + filename: string + score: 0 + attributes: { } + content: + - type: text + text: string + has_more: true + next_page: string + /deployments: + get: + summary: Deployments + description: AI Foundry compatibility deployments endpoint to get Azure OpenAI deployments. + operationId: deployments + responses: + '200': + description: '' +components: + schemas: + AddUploadPartRequest: + required: + - data + type: object + properties: + data: + type: string + description: "The chunk of bytes for this Part.\n" + format: binary + additionalProperties: false + AdminApiKey: + required: + - object + - redacted_value + - name + - created_at + - last_used_at + - id + - owner + type: object + properties: + object: + type: string + description: 'The object type, which is always `organization.admin_api_key`' + example: organization.admin_api_key + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + example: key_abc + name: + type: string + description: The name of the API key + example: Administration Key + redacted_value: + type: string + description: The redacted value of the API key + example: sk-admin...def + value: + type: string + description: The value of the API key. Only shown on create. + example: sk-admin-1234abcd + created_at: + type: integer + description: The Unix timestamp (in seconds) of when the API key was created + format: int64 + example: 1711471533 + last_used_at: + type: integer + description: The Unix timestamp (in seconds) of when the API key was last used + format: int64 + nullable: true + example: 1711471534 + owner: + type: object + properties: + type: + type: string + description: Always `user` + example: user + object: + type: string + description: 'The object type, which is always organization.user' + example: organization.user + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + example: sa_456 + name: + type: string + description: The name of the user + example: My Service Account + created_at: + type: integer + description: The Unix timestamp (in seconds) of when the user was created + format: int64 + example: 1711471533 + role: + type: string + description: Always `owner` + example: owner + description: Represents an individual Admin API key in an org. + x-oaiMeta: + name: The admin API key object + example: "{\n \"object\": \"organization.admin_api_key\",\n \"id\": \"key_abc\",\n \"name\": \"Main Admin Key\",\n \"redacted_value\": \"sk-admin...xyz\",\n \"created_at\": 1711471533,\n \"last_used_at\": 1711471534,\n \"owner\": {\n \"type\": \"user\",\n \"object\": \"organization.user\",\n \"id\": \"user_123\",\n \"name\": \"John Doe\",\n \"created_at\": 1711471533,\n \"role\": \"owner\"\n }\n}\n" + ApiKeyList: + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/AdminApiKey' + has_more: + type: boolean + example: false + first_id: + type: string + example: key_abc + last_id: + type: string + example: key_xyz + AssistantObject: + title: Assistant + required: + - id + - object + - created_at + - name + - description + - model + - instructions + - tools + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - assistant + type: string + description: 'The object type, which is always `assistant`.' + x-stainless-const: true + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the assistant was created. + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + model: + type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models) for descriptions of them.\n" + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter`` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the [vector store](/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + description: Represents an `assistant` that can call the model and use tools. + x-oaiMeta: + name: The assistant object + beta: true + example: "{ \"id\": \"asst_abc123\",\n \"object\": \"assistant\",\n \"created_at\": 1698984975,\n \"name\": \"Math Tutor\",\n \"description\": null,\n \"model\": \"gpt-4o\",\n \"instructions\": \"You are a personal math tutor. When asked a question, write and run Python code to answer the question.\",\n \"tools\": [\n {\n \"type\": \"code_interpreter\"\n }\n ],\n \"metadata\": {},\n \"top_p\": 1.0,\n \"temperature\": 1.0,\n \"response_format\": \"auto\"\n}\n" + AssistantStreamEvent: + oneOf: + - $ref: '#/components/schemas/ThreadStreamEvent' + - $ref: '#/components/schemas/RunStreamEvent' + - $ref: '#/components/schemas/RunStepStreamEvent' + - $ref: '#/components/schemas/MessageStreamEvent' + - $ref: '#/components/schemas/ErrorEvent' + - $ref: '#/components/schemas/DoneEvent' + description: "Represents an event emitted when streaming a Run.\n\nEach event in a server-sent events stream has an `event` and `data` property:\n\n```\nevent: thread.created\ndata: {\"id\": \"thread_123\", \"object\": \"thread\", ...}\n```\n\nWe emit events whenever a new object is created, transitions to a new state, or is being\nstreamed in parts (deltas). For example, we emit `thread.run.created` when a new run\nis created, `thread.run.completed` when a run completes, and so on. When an Assistant chooses\nto create a message during a run, we emit a `thread.message.created event`, a\n`thread.message.in_progress` event, many `thread.message.delta` events, and finally a\n`thread.message.completed` event.\n\nWe may add additional events over time, so we recommend handling unknown events gracefully\nin your code. See the [Assistants API quickstart](/docs/assistants/overview) to learn how to\nintegrate the Assistants API with streaming.\n" + x-oaiMeta: + name: Assistant stream events + beta: true + AssistantSupportedModels: + enum: + - gpt-4.1 + - gpt-4.1-mini + - gpt-4.1-nano + - gpt-4.1-2025-04-14 + - gpt-4.1-mini-2025-04-14 + - gpt-4.1-nano-2025-04-14 + - o3-mini + - o3-mini-2025-01-31 + - o1 + - o1-2024-12-17 + - gpt-4o + - gpt-4o-2024-11-20 + - gpt-4o-2024-08-06 + - gpt-4o-2024-05-13 + - gpt-4o-mini + - gpt-4o-mini-2024-07-18 + - gpt-4.5-preview + - gpt-4.5-preview-2025-02-27 + - gpt-4-turbo + - gpt-4-turbo-2024-04-09 + - gpt-4-0125-preview + - gpt-4-turbo-preview + - gpt-4-1106-preview + - gpt-4-vision-preview + - gpt-4 + - gpt-4-0314 + - gpt-4-0613 + - gpt-4-32k + - gpt-4-32k-0314 + - gpt-4-32k-0613 + - gpt-3.5-turbo + - gpt-3.5-turbo-16k + - gpt-3.5-turbo-0613 + - gpt-3.5-turbo-1106 + - gpt-3.5-turbo-0125 + - gpt-3.5-turbo-16k-0613 + type: string + AssistantToolsCode: + title: Code interpreter tool + required: + - type + type: object + properties: + type: + enum: + - code_interpreter + type: string + description: 'The type of tool being defined: `code_interpreter`' + x-stainless-const: true + AssistantToolsFileSearch: + title: FileSearch tool + required: + - type + type: object + properties: + type: + enum: + - file_search + type: string + description: 'The type of tool being defined: `file_search`' + x-stainless-const: true + file_search: + type: object + properties: + max_num_results: + maximum: 50.0 + minimum: 1.0 + type: integer + description: "The maximum number of results the file search tool should output. The default is 20 for `gpt-4*` models and 5 for `gpt-3.5-turbo`. This number should be between 1 and 50 inclusive.\n\nNote that the file search tool may output fewer than `max_num_results` results. See the [file search tool documentation](/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.\n" + ranking_options: + $ref: '#/components/schemas/FileSearchRankingOptions' + description: Overrides for the file search tool. + AssistantToolsFileSearchTypeOnly: + title: FileSearch tool + required: + - type + type: object + properties: + type: + enum: + - file_search + type: string + description: 'The type of tool being defined: `file_search`' + x-stainless-const: true + AssistantToolsFunction: + title: Function tool + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of tool being defined: `function`' + x-stainless-const: true + function: + $ref: '#/components/schemas/FunctionObject' + AssistantsApiResponseFormatOption: + oneOf: + - enum: + - auto + type: string + description: "`auto` is the default value\n" + x-stainless-const: true + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + - $ref: '#/components/schemas/ResponseFormatJsonSchema' + description: "Specifies the format that the model must output. Compatible with [GPT-4o](/docs/models#gpt-4o), [GPT-4 Turbo](/docs/models#gpt-4-turbo-and-gpt-4), and all GPT-3.5 Turbo models since `gpt-3.5-turbo-1106`.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema. Learn more in the [Structured Outputs guide](/docs/guides/structured-outputs).\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.\n" + AssistantsApiToolChoiceOption: + oneOf: + - enum: + - none + - auto + - required + type: string + description: "`none` means the model will not call any tools and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools before responding to the user.\n" + - $ref: '#/components/schemas/AssistantsNamedToolChoice' + description: "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tools and instead generates a message.\n`auto` is the default value and means the model can pick between generating a message or calling one or more tools.\n`required` means the model must call one or more tools before responding to the user.\nSpecifying a particular tool like `{\"type\": \"file_search\"}` or `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n" + AssistantsNamedToolChoice: + required: + - type + type: object + properties: + type: + enum: + - function + - code_interpreter + - file_search + type: string + description: "The type of the tool. If type is `function`, the function name must\nbe set" + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: "Specifies a tool the model should use. Use to force the model to\ncall a specific tool." + AudioResponseFormat: + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: "The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`. For `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`, the only supported format is `json`.\n" + default: json + AuditLog: + required: + - id + - type + - effective_at + - actor + type: object + properties: + id: + type: string + description: The ID of this log. + type: + $ref: '#/components/schemas/AuditLogEventType' + effective_at: + type: integer + description: The Unix timestamp (in seconds) of the event. + project: + type: object + properties: + id: + type: string + description: The project ID. + name: + type: string + description: The project title. + description: "The project that the action was scoped to. Absent for actions not\nscoped to projects." + actor: + $ref: '#/components/schemas/AuditLogActor' + api_key.created: + type: object + properties: + id: + type: string + description: The tracking ID of the API key. + data: + type: object + properties: + scopes: + type: array + items: + type: string + description: "A list of scopes allowed for the API key, e.g.\n`[\"api.model.request\"]`" + description: The payload used to create the API key. + description: The details for events with this `type`. + api_key.updated: + type: object + properties: + id: + type: string + description: The tracking ID of the API key. + changes_requested: + type: object + properties: + scopes: + type: array + items: + type: string + description: "A list of scopes allowed for the API key, e.g.\n`[\"api.model.request\"]`" + description: The payload used to update the API key. + description: The details for events with this `type`. + api_key.deleted: + type: object + properties: + id: + type: string + description: The tracking ID of the API key. + description: The details for events with this `type`. + checkpoint_permission.created: + type: object + properties: + id: + type: string + description: The ID of the checkpoint permission. + data: + type: object + properties: + project_id: + type: string + description: "The ID of the project that the checkpoint permission was created\nfor." + fine_tuned_model_checkpoint: + type: string + description: The ID of the fine-tuned model checkpoint. + description: The payload used to create the checkpoint permission. + description: "The project and fine-tuned model checkpoint that the checkpoint\npermission was created for." + checkpoint_permission.deleted: + type: object + properties: + id: + type: string + description: The ID of the checkpoint permission. + description: The details for events with this `type`. + invite.sent: + type: object + properties: + id: + type: string + description: The ID of the invite. + data: + type: object + properties: + email: + type: string + description: The email invited to the organization. + role: + type: string + description: "The role the email was invited to be. Is either `owner` or\n`member`." + description: The payload used to create the invite. + description: The details for events with this `type`. + invite.accepted: + type: object + properties: + id: + type: string + description: The ID of the invite. + description: The details for events with this `type`. + invite.deleted: + type: object + properties: + id: + type: string + description: The ID of the invite. + description: The details for events with this `type`. + login.failed: + type: object + properties: + error_code: + type: string + description: The error code of the failure. + error_message: + type: string + description: The error message of the failure. + description: The details for events with this `type`. + logout.failed: + type: object + properties: + error_code: + type: string + description: The error code of the failure. + error_message: + type: string + description: The error message of the failure. + description: The details for events with this `type`. + organization.updated: + type: object + properties: + id: + type: string + description: The organization ID. + changes_requested: + type: object + properties: + title: + type: string + description: The organization title. + description: + type: string + description: The organization description. + name: + type: string + description: The organization name. + settings: + type: object + properties: + threads_ui_visibility: + type: string + description: "Visibility of the threads page which shows messages created with\nthe Assistants API and Playground. One of `ANY_ROLE`,\n`OWNERS`, or `NONE`." + usage_dashboard_visibility: + type: string + description: "Visibility of the usage dashboard which shows activity and costs\nfor your organization. One of `ANY_ROLE` or `OWNERS`." + description: The payload used to update the organization settings. + description: The details for events with this `type`. + project.created: + type: object + properties: + id: + type: string + description: The project ID. + data: + type: object + properties: + name: + type: string + description: The project name. + title: + type: string + description: The title of the project as seen on the dashboard. + description: The payload used to create the project. + description: The details for events with this `type`. + project.updated: + type: object + properties: + id: + type: string + description: The project ID. + changes_requested: + type: object + properties: + title: + type: string + description: The title of the project as seen on the dashboard. + description: The payload used to update the project. + description: The details for events with this `type`. + project.archived: + type: object + properties: + id: + type: string + description: The project ID. + description: The details for events with this `type`. + rate_limit.updated: + type: object + properties: + id: + type: string + description: The rate limit ID + changes_requested: + type: object + properties: + max_requests_per_1_minute: + type: integer + description: The maximum requests per minute. + max_tokens_per_1_minute: + type: integer + description: The maximum tokens per minute. + max_images_per_1_minute: + type: integer + description: The maximum images per minute. Only relevant for certain models. + max_audio_megabytes_per_1_minute: + type: integer + description: "The maximum audio megabytes per minute. Only relevant for certain\nmodels." + max_requests_per_1_day: + type: integer + description: The maximum requests per day. Only relevant for certain models. + batch_1_day_max_input_tokens: + type: integer + description: "The maximum batch input tokens per day. Only relevant for certain\nmodels." + description: The payload used to update the rate limits. + description: The details for events with this `type`. + rate_limit.deleted: + type: object + properties: + id: + type: string + description: The rate limit ID + description: The details for events with this `type`. + service_account.created: + type: object + properties: + id: + type: string + description: The service account ID. + data: + type: object + properties: + role: + type: string + description: The role of the service account. Is either `owner` or `member`. + description: The payload used to create the service account. + description: The details for events with this `type`. + service_account.updated: + type: object + properties: + id: + type: string + description: The service account ID. + changes_requested: + type: object + properties: + role: + type: string + description: The role of the service account. Is either `owner` or `member`. + description: The payload used to updated the service account. + description: The details for events with this `type`. + service_account.deleted: + type: object + properties: + id: + type: string + description: The service account ID. + description: The details for events with this `type`. + user.added: + type: object + properties: + id: + type: string + description: The user ID. + data: + type: object + properties: + role: + type: string + description: The role of the user. Is either `owner` or `member`. + description: The payload used to add the user to the project. + description: The details for events with this `type`. + user.updated: + type: object + properties: + id: + type: string + description: The project ID. + changes_requested: + type: object + properties: + role: + type: string + description: The role of the user. Is either `owner` or `member`. + description: The payload used to update the user. + description: The details for events with this `type`. + user.deleted: + type: object + properties: + id: + type: string + description: The user ID. + description: The details for events with this `type`. + certificate.created: + type: object + properties: + id: + type: string + description: The certificate ID. + name: + type: string + description: The name of the certificate. + description: The details for events with this `type`. + certificate.updated: + type: object + properties: + id: + type: string + description: The certificate ID. + name: + type: string + description: The name of the certificate. + description: The details for events with this `type`. + certificate.deleted: + type: object + properties: + id: + type: string + description: The certificate ID. + name: + type: string + description: The name of the certificate. + certificate: + type: string + description: The certificate content in PEM format. + description: The details for events with this `type`. + certificates.activated: + type: object + properties: + certificates: + type: array + items: + type: object + properties: + id: + type: string + description: The certificate ID. + name: + type: string + description: The name of the certificate. + description: The details for events with this `type`. + certificates.deactivated: + type: object + properties: + certificates: + type: array + items: + type: object + properties: + id: + type: string + description: The certificate ID. + name: + type: string + description: The name of the certificate. + description: The details for events with this `type`. + description: A log of a user action or configuration change within this organization. + x-oaiMeta: + name: The audit log object + example: "{ \"id\": \"req_xxx_20240101\",\n \"type\": \"api_key.created\",\n \"effective_at\": 1720804090,\n \"actor\": {\n \"type\": \"session\",\n \"session\": {\n \"user\": {\n \"id\": \"user-xxx\",\n \"email\": \"user@example.com\"\n },\n \"ip_address\": \"127.0.0.1\",\n \"user_agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\"\n }\n },\n \"api_key.created\": {\n \"id\": \"key_xxxx\",\n \"data\": {\n \"scopes\": [\"resource.operation\"]\n }\n }\n}\n" + AuditLogActor: + type: object + properties: + type: + enum: + - session + - api_key + type: string + description: The type of actor. Is either `session` or `api_key`. + session: + $ref: '#/components/schemas/AuditLogActorSession' + api_key: + $ref: '#/components/schemas/AuditLogActorApiKey' + description: The actor who performed the audit logged action. + AuditLogActorApiKey: + type: object + properties: + id: + type: string + description: The tracking id of the API key. + type: + enum: + - user + - service_account + type: string + description: The type of API key. Can be either `user` or `service_account`. + user: + $ref: '#/components/schemas/AuditLogActorUser' + service_account: + $ref: '#/components/schemas/AuditLogActorServiceAccount' + description: The API Key used to perform the audit logged action. + AuditLogActorServiceAccount: + type: object + properties: + id: + type: string + description: The service account id. + description: The service account that performed the audit logged action. + AuditLogActorSession: + type: object + properties: + user: + $ref: '#/components/schemas/AuditLogActorUser' + ip_address: + type: string + description: The IP address from which the action was performed. + description: The session in which the audit logged action was performed. + AuditLogActorUser: + type: object + properties: + id: + type: string + description: The user id. + email: + type: string + description: The user email. + description: The user who performed the audit logged action. + AuditLogEventType: + enum: + - api_key.created + - api_key.updated + - api_key.deleted + - checkpoint_permission.created + - checkpoint_permission.deleted + - invite.sent + - invite.accepted + - invite.deleted + - login.succeeded + - login.failed + - logout.succeeded + - logout.failed + - organization.updated + - project.created + - project.updated + - project.archived + - service_account.created + - service_account.updated + - service_account.deleted + - rate_limit.updated + - rate_limit.deleted + - user.added + - user.updated + - user.deleted + type: string + description: The event type. + AutoChunkingStrategyRequestParam: + title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + x-stainless-const: true + additionalProperties: false + description: "The default strategy. This strategy currently uses a\n`max_chunk_size_tokens` of `800` and `chunk_overlap_tokens` of `400`." + Batch: + required: + - id + - object + - endpoint + - input_file_id + - completion_window + - status + - created_at + type: object + properties: + id: + type: string + object: + enum: + - batch + type: string + description: 'The object type, which is always `batch`.' + x-stainless-const: true + endpoint: + type: string + description: The OpenAI API endpoint used by the batch. + errors: + type: object + properties: + object: + type: string + description: 'The object type, which is always `list`.' + data: + type: array + items: + type: object + properties: + code: + type: string + description: An error code identifying the error type. + message: + type: string + description: A human-readable message providing more details about the error. + param: + type: string + description: 'The name of the parameter that caused the error, if applicable.' + nullable: true + line: + type: integer + description: "The line number of the input file where the error occurred, if\napplicable." + nullable: true + input_file_id: + type: string + description: The ID of the input file for the batch. + completion_window: + type: string + description: The time frame within which the batch should be processed. + status: + enum: + - validating + - failed + - in_progress + - finalizing + - completed + - expired + - cancelling + - cancelled + type: string + description: The current status of the batch. + output_file_id: + type: string + description: "The ID of the file containing the outputs of successfully executed\nrequests." + error_file_id: + type: string + description: The ID of the file containing the outputs of requests with errors. + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch was created. + in_progress_at: + type: integer + description: "The Unix timestamp (in seconds) for when the batch started\nprocessing." + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch will expire. + finalizing_at: + type: integer + description: "The Unix timestamp (in seconds) for when the batch started\nfinalizing." + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch was completed. + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch failed. + expired_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch expired. + cancelling_at: + type: integer + description: "The Unix timestamp (in seconds) for when the batch started\ncancelling." + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the batch was cancelled. + request_counts: + required: + - total + - completed + - failed + type: object + properties: + total: + type: integer + description: Total number of requests in the batch. + completed: + type: integer + description: Number of requests that have been completed successfully. + failed: + type: integer + description: Number of requests that have failed. + description: The request counts for different statuses within the batch. + metadata: + $ref: '#/components/schemas/Metadata' + x-oaiMeta: + name: The batch object + example: "{\n \"id\": \"batch_abc123\",\n \"object\": \"batch\",\n \"endpoint\": \"/v1/completions\",\n \"errors\": null,\n \"input_file_id\": \"file-abc123\",\n \"completion_window\": \"24h\",\n \"status\": \"completed\",\n \"output_file_id\": \"file-cvaTdG\",\n \"error_file_id\": \"file-HOWS94\",\n \"created_at\": 1711471533,\n \"in_progress_at\": 1711471538,\n \"expires_at\": 1711557933,\n \"finalizing_at\": 1711493133,\n \"completed_at\": 1711493163,\n \"failed_at\": null,\n \"expired_at\": null,\n \"cancelling_at\": null,\n \"cancelled_at\": null,\n \"request_counts\": {\n \"total\": 100,\n \"completed\": 95,\n \"failed\": 5\n },\n \"metadata\": {\n \"customer_id\": \"user_123456789\",\n \"batch_description\": \"Nightly eval job\",\n }\n}\n" + BatchRequestInput: + type: object + properties: + custom_id: + type: string + description: "A developer-provided per-request id that will be used to match\noutputs to inputs. Must be unique for each request in a batch." + method: + enum: + - POST + type: string + description: "The HTTP method to be used for the request. Currently only `POST`\nis supported." + x-stainless-const: true + url: + type: string + description: "The OpenAI API relative URL to be used for the request. Currently\n`/v1/chat/completions`, `/v1/embeddings`, and `/v1/completions` are\nsupported." + description: The per-line object of the batch input file + x-oaiMeta: + name: The request input object + example: "{\"custom_id\": \"request-1\", \"method\": \"POST\", \"url\": \"/v1/chat/completions\", \"body\": {\"model\": \"gpt-4o-mini\", \"messages\": [{\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, {\"role\": \"user\", \"content\": \"What is 2+2?\"}]}}\n" + BatchRequestOutput: + type: object + properties: + id: + type: string + custom_id: + type: string + description: "A developer-provided per-request id that will be used to match\noutputs to inputs." + response: + type: object + properties: + status_code: + type: integer + description: The HTTP status code of the response + request_id: + type: string + description: "An unique identifier for the OpenAI API request. Please include\nthis request ID when contacting support." + body: + type: object + description: The JSON body of the response + x-oaiTypeLabel: map + nullable: true + error: + type: object + properties: + code: + type: string + description: A machine-readable error code. + message: + type: string + description: A human-readable error message. + description: "For requests that failed with a non-HTTP error, this will contain\nmore information on the cause of the failure." + nullable: true + description: The per-line object of the batch output and error files + x-oaiMeta: + name: The request output object + example: "{\"id\": \"batch_req_wnaDys\", \"custom_id\": \"request-2\", \"response\": {\"status_code\": 200, \"request_id\": \"req_c187b3\", \"body\": {\"id\": \"chatcmpl-9758Iw\", \"object\": \"chat.completion\", \"created\": 1711475054, \"model\": \"gpt-4o-mini\", \"choices\": [{\"index\": 0, \"message\": {\"role\": \"assistant\", \"content\": \"2 + 2 equals 4.\"}, \"finish_reason\": \"stop\"}], \"usage\": {\"prompt_tokens\": 24, \"completion_tokens\": 15, \"total_tokens\": 39}, \"system_fingerprint\": null}}, \"error\": null}\n" + Certificate: + required: + - object + - id + - name + - created_at + - certificate_details + type: object + properties: + object: + enum: + - certificate + - organization.certificate + - organization.project.certificate + type: string + description: "The object type.\n\n- If creating, updating, or getting a specific certificate, the object type is `certificate`.\n- If listing, activating, or deactivating certificates for the organization, the object type is `organization.certificate`.\n- If listing, activating, or deactivating certificates for a project, the object type is `organization.project.certificate`.\n" + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + name: + type: string + description: The name of the certificate. + created_at: + type: integer + description: The Unix timestamp (in seconds) of when the certificate was uploaded. + certificate_details: + type: object + properties: + valid_at: + type: integer + description: "The Unix timestamp (in seconds) of when the certificate becomes\nvalid." + expires_at: + type: integer + description: The Unix timestamp (in seconds) of when the certificate expires. + content: + type: string + description: The content of the certificate in PEM format. + active: + type: boolean + description: "Whether the certificate is currently active at the specified scope.\nNot returned when getting details for a specific certificate." + description: Represents an individual `certificate` uploaded to the organization. + x-oaiMeta: + name: The certificate object + example: "{ \"object\": \"certificate\",\n \"id\": \"cert_abc\",\n \"name\": \"My Certificate\",\n \"created_at\": 1234567,\n \"certificate_details\": {\n \"valid_at\": 1234567,\n \"expires_at\": 12345678,\n \"content\": \"-----BEGIN CERTIFICATE----- MIIGAjCCA...6znFlOW+ -----END CERTIFICATE-----\"\n }\n}\n" + ChatCompletionDeleted: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - chat.completion.deleted + type: string + description: The type of object being deleted. + x-stainless-const: true + id: + type: string + description: The ID of the chat completion that was deleted. + deleted: + type: boolean + description: Whether the chat completion was deleted. + ChatCompletionFunctionCallOption: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: "Specifying a particular function via `{\"name\": \"my_function\"}` forces the model to call that function.\n" + ChatCompletionFunctions: + required: + - name + type: object + properties: + description: + type: string + description: "A description of what the function does, used by the model to\nchoose when and how to call the function." + name: + type: string + description: "The name of the function to be called. Must be a-z, A-Z, 0-9, or\ncontain underscores and dashes, with a maximum length of 64." + parameters: + $ref: '#/components/schemas/FunctionParameters' + deprecated: true + ChatCompletionList: + title: ChatCompletionList + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + description: "The type of this object. It is always set to \"list\".\n" + default: list + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/CreateChatCompletionResponse' + description: "An array of chat completion objects.\n" + first_id: + type: string + description: The identifier of the first chat completion in the data array. + last_id: + type: string + description: The identifier of the last chat completion in the data array. + has_more: + type: boolean + description: Indicates whether there are more Chat Completions available. + description: "An object representing a list of Chat Completions.\n" + x-oaiMeta: + name: The chat completion list object + group: chat + example: "{ \"object\": \"list\",\n \"data\": [\n {\n \"object\": \"chat.completion\",\n \"id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2\",\n \"model\": \"gpt-4o-2024-08-06\",\n \"created\": 1738960610,\n \"request_id\": \"req_ded8ab984ec4bf840f37566c1011c417\",\n \"tool_choice\": null,\n \"usage\": {\n \"total_tokens\": 31,\n \"completion_tokens\": 18,\n \"prompt_tokens\": 13\n },\n \"seed\": 4944116822809979520,\n \"top_p\": 1.0,\n \"temperature\": 1.0,\n \"presence_penalty\": 0.0,\n \"frequency_penalty\": 0.0,\n \"system_fingerprint\": \"fp_50cad350e4\",\n \"input_user\": null,\n \"service_tier\": \"default\",\n \"tools\": null,\n \"metadata\": {},\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"content\": \"Mind of circuits hum, \\nLearning patterns in silenceΓÇö \\nFuture's quiet spark.\",\n \"role\": \"assistant\",\n \"tool_calls\": null,\n \"function_call\": null\n },\n \"finish_reason\": \"stop\",\n \"logprobs\": null\n }\n ],\n \"response_format\": null\n }\n ],\n \"first_id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2\",\n \"last_id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2\",\n \"has_more\": false\n}\n" + ChatCompletionMessageList: + title: ChatCompletionMessageList + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + description: "The type of this object. It is always set to \"list\".\n" + default: list + x-stainless-const: true + data: + type: array + items: + allOf: + - $ref: '#/components/schemas/ChatCompletionResponseMessage' + - required: + - id + type: object + properties: + id: + type: string + description: The identifier of the chat message. + description: "An array of chat completion message objects.\n" + first_id: + type: string + description: The identifier of the first chat message in the data array. + last_id: + type: string + description: The identifier of the last chat message in the data array. + has_more: + type: boolean + description: Indicates whether there are more chat messages available. + description: "An object representing a list of chat completion messages.\n" + x-oaiMeta: + name: The chat completion message list object + group: chat + example: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0\",\n \"role\": \"user\",\n \"content\": \"write a haiku about ai\",\n \"name\": null,\n \"content_parts\": null\n }\n ],\n \"first_id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0\",\n \"last_id\": \"chatcmpl-AyPNinnUqUDYo9SAdA52NobMflmj2-0\",\n \"has_more\": false\n}\n" + ChatCompletionMessageToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-stainless-const: true + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: "The arguments to call the function with, as generated by the model\nin JSON format. Note that the model does not always generate\nvalid JSON, and may hallucinate parameters not defined by your\nfunction schema. Validate the arguments in your code before\ncalling your function." + description: The function that the model called. + ChatCompletionMessageToolCallChunk: + required: + - index + type: object + properties: + index: + type: integer + id: + type: string + description: The ID of the tool call. + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-stainless-const: true + function: + type: object + properties: + name: + type: string + description: The name of the function to call. + arguments: + type: string + description: "The arguments to call the function with, as generated by the model\nin JSON format. Note that the model does not always generate\nvalid JSON, and may hallucinate parameters not defined by your\nfunction schema. Validate the arguments in your code before\ncalling your function." + ChatCompletionMessageToolCalls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionMessageToolCall' + description: 'The tool calls generated by the model, such as function calls.' + ChatCompletionModalities: + type: array + items: + enum: + - text + - audio + type: string + description: "Output types that you would like the model to generate for this request.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to [generate audio](/docs/guides/audio). To\nrequest that this model generate both text and audio responses, you can\nuse:\n\n`[\"text\", \"audio\"]`\n" + nullable: true + ChatCompletionNamedToolChoice: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-stainless-const: true + function: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to call. + description: "Specifies a tool the model should use. Use to force the model to\ncall a specific function." + ChatCompletionRequestAssistantMessage: + title: Assistant message + required: + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the assistant message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestAssistantMessageContentPart' + description: "An array of content parts with a defined type. Can be one or more\nof type `text`, or exactly one of type `refusal`." + description: "The contents of the assistant message. Required unless `tool_calls` or `function_call` is specified.\n" + nullable: true + refusal: + type: string + description: The refusal message by the assistant. + nullable: true + role: + enum: + - assistant + type: string + description: 'The role of the messages author, in this case `assistant`.' + x-stainless-const: true + name: + type: string + description: "An optional name for the participant. Provides the model\ninformation to differentiate between participants of the same role." + audio: + required: + - id + type: object + properties: + id: + type: string + description: "Unique identifier for a previous audio response from the model.\n" + description: "Data about a previous audio response from the model. \n[Learn more](/docs/guides/audio).\n" + nullable: true + tool_calls: + $ref: '#/components/schemas/ChatCompletionMessageToolCalls' + function_call: + required: + - arguments + - name + type: object + properties: + arguments: + type: string + description: "The arguments to call the function with, as generated by the model\nin JSON format. Note that the model does not always generate\nvalid JSON, and may hallucinate parameters not defined by your\nfunction schema. Validate the arguments in your code before\ncalling your function." + name: + type: string + description: The name of the function to call. + description: "Deprecated and replaced by `tool_calls`. The name and arguments of\na function that should be called, as generated by the model." + nullable: true + deprecated: true + description: "Messages sent by the model in response to user messages.\n" + ChatCompletionRequestAssistantMessageContentPart: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartRefusal' + ChatCompletionRequestDeveloperMessage: + title: Developer message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the developer message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + description: "An array of content parts with a defined type. For developer\nmessages, only type `text` is supported." + description: The contents of the developer message. + role: + enum: + - developer + type: string + description: 'The role of the messages author, in this case `developer`.' + x-stainless-const: true + name: + type: string + description: "An optional name for the participant. Provides the model\ninformation to differentiate between participants of the same role." + description: "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, `developer` messages\nreplace the previous `system` messages.\n" + ChatCompletionRequestFunctionMessage: + title: Function message + required: + - role + - content + - name + type: object + properties: + role: + enum: + - function + type: string + description: 'The role of the messages author, in this case `function`.' + x-stainless-const: true + content: + type: string + description: The contents of the function message. + nullable: true + name: + type: string + description: The name of the function to call. + deprecated: true + ChatCompletionRequestMessage: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestDeveloperMessage' + - $ref: '#/components/schemas/ChatCompletionRequestSystemMessage' + - $ref: '#/components/schemas/ChatCompletionRequestUserMessage' + - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage' + - $ref: '#/components/schemas/ChatCompletionRequestToolMessage' + - $ref: '#/components/schemas/ChatCompletionRequestFunctionMessage' + ChatCompletionRequestMessageContentPartAudio: + title: Audio content part + required: + - type + - input_audio + type: object + properties: + type: + enum: + - input_audio + type: string + description: The type of the content part. Always `input_audio`. + x-stainless-const: true + input_audio: + required: + - data + - format + type: object + properties: + data: + type: string + description: Base64 encoded audio data. + format: + enum: + - wav + - mp3 + type: string + description: "The format of the encoded audio data. Currently supports \"wav\" and \"mp3\".\n" + description: "Learn about [audio inputs](/docs/guides/audio).\n" + ChatCompletionRequestMessageContentPartFile: + title: File content part + required: + - type + - file + type: object + properties: + type: + enum: + - file + type: string + description: The type of the content part. Always `file`. + x-stainless-const: true + file: + type: object + properties: + filename: + type: string + description: "The name of the file, used when passing the file to the model as a\nstring.\n" + file_data: + type: string + description: "The base64 encoded file data, used when passing the file to the model\nas a string.\n" + file_id: + type: string + description: "The ID of an uploaded file to use as input.\n" + description: "Learn about [file inputs](/docs/guides/text) for text generation.\n" + ChatCompletionRequestMessageContentPartImage: + title: Image content part + required: + - type + - image_url + type: object + properties: + type: + enum: + - image_url + type: string + description: The type of the content part. + x-stainless-const: true + image_url: + required: + - url + type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + detail: + enum: + - auto + - low + - high + type: string + description: "Specifies the detail level of the image. Learn more in the [Vision\nguide](/docs/guides/vision#low-or-high-fidelity-image-understanding)." + default: auto + description: "Learn about [image inputs](/docs/guides/vision).\n" + ChatCompletionRequestMessageContentPartRefusal: + title: Refusal content part + required: + - type + - refusal + type: object + properties: + type: + enum: + - refusal + type: string + description: The type of the content part. + x-stainless-const: true + refusal: + type: string + description: The refusal message generated by the model. + ChatCompletionRequestMessageContentPartText: + title: Text content part + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: The type of the content part. + x-stainless-const: true + text: + type: string + description: The text content. + description: "Learn about [text inputs](/docs/guides/text-generation).\n" + ChatCompletionRequestSystemMessage: + title: System message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The contents of the system message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestSystemMessageContentPart' + description: "An array of content parts with a defined type. For system messages,\nonly type `text` is supported." + description: The contents of the system message. + role: + enum: + - system + type: string + description: 'The role of the messages author, in this case `system`.' + x-stainless-const: true + name: + type: string + description: "An optional name for the participant. Provides the model\ninformation to differentiate between participants of the same role." + description: "Developer-provided instructions that the model should follow, regardless of\nmessages sent by the user. With o1 models and newer, use `developer` messages\nfor this purpose instead.\n" + ChatCompletionRequestSystemMessageContentPart: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + ChatCompletionRequestToolMessage: + title: Tool message + required: + - role + - content + - tool_call_id + type: object + properties: + role: + enum: + - tool + type: string + description: 'The role of the messages author, in this case `tool`.' + x-stainless-const: true + content: + oneOf: + - title: Text content + type: string + description: The contents of the tool message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestToolMessageContentPart' + description: "An array of content parts with a defined type. For tool messages,\nonly type `text` is supported." + description: The contents of the tool message. + tool_call_id: + type: string + description: Tool call that this message is responding to. + ChatCompletionRequestToolMessageContentPart: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + ChatCompletionRequestUserMessage: + title: User message + required: + - content + - role + type: object + properties: + content: + oneOf: + - title: Text content + type: string + description: The text contents of the message. + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestUserMessageContentPart' + description: "An array of content parts with a defined type. Supported options\ndiffer based on the [model](/docs/models) being used to generate\nthe response. Can contain text, image, or audio inputs." + description: "The contents of the user message.\n" + role: + enum: + - user + type: string + description: 'The role of the messages author, in this case `user`.' + x-stainless-const: true + name: + type: string + description: "An optional name for the participant. Provides the model\ninformation to differentiate between participants of the same role." + description: "Messages sent by an end user, containing prompts or additional context\ninformation.\n" + ChatCompletionRequestUserMessageContentPart: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartImage' + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartAudio' + - $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartFile' + ChatCompletionResponseMessage: + required: + - role + - content + - refusal + type: object + properties: + content: + type: string + description: The contents of the message. + nullable: true + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + tool_calls: + $ref: '#/components/schemas/ChatCompletionMessageToolCalls' + annotations: + type: array + items: + required: + - type + - url_citation + type: object + properties: + type: + enum: + - url_citation + type: string + description: The type of the URL citation. Always `url_citation`. + x-stainless-const: true + url_citation: + required: + - end_index + - start_index + - url + - title + type: object + properties: + end_index: + type: integer + description: The index of the last character of the URL citation in the message. + start_index: + type: integer + description: "The index of the first character of the URL citation in the\nmessage." + url: + type: string + description: The URL of the web resource. + title: + type: string + description: The title of the web resource. + description: A URL citation when using web search. + description: "A URL citation when using web search.\n" + description: "Annotations for the message, when applicable, as when using the\n[web search tool](/docs/guides/tools-web-search?api-mode=chat).\n" + role: + enum: + - assistant + type: string + description: The role of the author of this message. + x-stainless-const: true + function_call: + required: + - name + - arguments + type: object + properties: + arguments: + type: string + description: "The arguments to call the function with, as generated by the model\nin JSON format. Note that the model does not always generate\nvalid JSON, and may hallucinate parameters not defined by your\nfunction schema. Validate the arguments in your code before\ncalling your function." + name: + type: string + description: The name of the function to call. + description: "Deprecated and replaced by `tool_calls`. The name and arguments of\na function that should be called, as generated by the model." + deprecated: true + audio: + required: + - id + - expires_at + - data + - transcript + type: object + properties: + id: + type: string + description: Unique identifier for this audio response. + expires_at: + type: integer + description: "The Unix timestamp (in seconds) for when this audio response will\nno longer be accessible on the server for use in multi-turn\nconversations.\n" + data: + type: string + description: "Base64 encoded audio bytes generated by the model, in the format\nspecified in the request.\n" + transcript: + type: string + description: Transcript of the audio generated by the model. + description: "If the audio output modality is requested, this object contains data\nabout the audio response from the model. [Learn more](/docs/guides/audio).\n" + nullable: true + description: A chat completion message generated by the model. + ChatCompletionRole: + enum: + - developer + - system + - user + - assistant + - tool + - function + type: string + description: The role of the author of a message + ChatCompletionStreamOptions: + type: object + properties: + include_usage: + type: boolean + description: "If set, an additional chunk will be streamed before the `data: [DONE]`\nmessage. The `usage` field on this chunk shows the token usage statistics\nfor the entire request, and the `choices` field will always be an empty\narray.\n\nAll other chunks will also include a `usage` field, but with a null\nvalue. **NOTE:** If the stream is interrupted, you may not receive the\nfinal usage chunk which contains the total token usage for the request.\n" + description: "Options for streaming response. Only set this when you set `stream: true`.\n" + default: + nullable: true + ChatCompletionStreamResponseDelta: + type: object + properties: + content: + type: string + description: The contents of the chunk message. + nullable: true + function_call: + type: object + properties: + arguments: + type: string + description: "The arguments to call the function with, as generated by the model\nin JSON format. Note that the model does not always generate\nvalid JSON, and may hallucinate parameters not defined by your\nfunction schema. Validate the arguments in your code before\ncalling your function." + name: + type: string + description: The name of the function to call. + description: "Deprecated and replaced by `tool_calls`. The name and arguments of\na function that should be called, as generated by the model." + deprecated: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionMessageToolCallChunk' + role: + enum: + - developer + - system + - user + - assistant + - tool + type: string + description: The role of the author of this message. + refusal: + type: string + description: The refusal message generated by the model. + nullable: true + description: A chat completion delta generated by streamed model responses. + ChatCompletionTokenLogprob: + required: + - token + - logprob + - bytes + - top_logprobs + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: "The log probability of this token, if it is within the top 20 most\nlikely tokens. Otherwise, the value `-9999.0` is used to signify\nthat the token is very unlikely." + bytes: + type: array + items: + type: integer + description: "A list of integers representing the UTF-8 bytes representation of\nthe token. Useful in instances where characters are represented by\nmultiple tokens and their byte representations must be combined to\ngenerate the correct text representation. Can be `null` if there is\nno bytes representation for the token." + nullable: true + top_logprobs: + type: array + items: + required: + - token + - logprob + - bytes + type: object + properties: + token: + type: string + description: The token. + logprob: + type: number + description: "The log probability of this token, if it is within the top 20 most\nlikely tokens. Otherwise, the value `-9999.0` is used to\nsignify that the token is very unlikely." + bytes: + type: array + items: + type: integer + description: "A list of integers representing the UTF-8 bytes representation of\nthe token. Useful in instances where characters are\nrepresented by multiple tokens and their byte representations\nmust be combined to generate the correct text representation.\nCan be `null` if there is no bytes representation for the\ntoken." + nullable: true + description: "List of the most likely tokens and their log probability, at this\ntoken position. In rare cases, there may be fewer than the number of\nrequested `top_logprobs` returned." + ChatCompletionTool: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + x-stainless-const: true + function: + $ref: '#/components/schemas/FunctionObject' + ChatCompletionToolChoiceOption: + oneOf: + - enum: + - none + - auto + - required + type: string + description: "`none` means the model will not call any tool and instead generates a message. `auto` means the model can pick between generating a message or calling one or more tools. `required` means the model must call one or more tools.\n" + - $ref: '#/components/schemas/ChatCompletionNamedToolChoice' + description: "Controls which (if any) tool is called by the model.\n`none` means the model will not call any tool and instead generates a message.\n`auto` means the model can pick between generating a message or calling one or more tools.\n`required` means the model must call one or more tools.\nSpecifying a particular tool via `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}` forces the model to call that tool.\n\n`none` is the default when no tools are present. `auto` is the default if tools are present.\n" + ChunkingStrategyRequestParam: + type: object + oneOf: + - $ref: '#/components/schemas/AutoChunkingStrategyRequestParam' + - $ref: '#/components/schemas/StaticChunkingStrategyRequestParam' + description: "The chunking strategy used to chunk the file(s). If not set, will\nuse the `auto` strategy." + Click: + title: Click + required: + - type + - button + - x + - y + type: object + properties: + type: + enum: + - click + type: string + description: "Specifies the event type. For a click action, this property is \nalways set to `click`.\n" + default: click + x-stainless-const: true + button: + enum: + - left + - right + - wheel + - back + - forward + type: string + description: "Indicates which mouse button was pressed during the click. One of `left`, `right`, `wheel`, `back`, or `forward`.\n" + x: + type: integer + description: "The x-coordinate where the click occurred.\n" + y: + type: integer + description: "The y-coordinate where the click occurred.\n" + description: "A click action.\n" + CodeInterpreterFileOutput: + title: Code interpreter file output + required: + - type + - files + type: object + properties: + type: + enum: + - files + type: string + description: "The type of the code interpreter file output. Always `files`.\n" + x-stainless-const: true + files: + type: array + items: + required: + - mime_type + - file_id + type: object + properties: + mime_type: + type: string + description: "The MIME type of the file.\n" + file_id: + type: string + description: "The ID of the file.\n" + description: "The output of a code interpreter tool call that is a file.\n" + CodeInterpreterTextOutput: + title: Code interpreter text output + required: + - type + - logs + type: object + properties: + type: + enum: + - logs + type: string + description: "The type of the code interpreter text output. Always `logs`.\n" + x-stainless-const: true + logs: + type: string + description: "The logs of the code interpreter tool call.\n" + description: "The output of a code interpreter tool call that is text.\n" + CodeInterpreterToolCall: + title: Code interpreter tool call + required: + - id + - type + - code + - status + - results + type: object + properties: + id: + type: string + description: "The unique ID of the code interpreter tool call.\n" + type: + enum: + - code_interpreter_call + type: string + description: "The type of the code interpreter tool call. Always `code_interpreter_call`.\n" + x-stainless-const: true + code: + type: string + description: "The code to run.\n" + status: + enum: + - in_progress + - interpreting + - completed + type: string + description: "The status of the code interpreter tool call.\n" + results: + type: array + items: + $ref: '#/components/schemas/CodeInterpreterToolOutput' + description: "The results of the code interpreter tool call.\n" + description: "A tool call to run code.\n" + CodeInterpreterToolOutput: + oneOf: + - $ref: '#/components/schemas/CodeInterpreterTextOutput' + - $ref: '#/components/schemas/CodeInterpreterFileOutput' + ComparisonFilter: + title: Comparison Filter + required: + - type + - key + - value + type: object + properties: + type: + enum: + - eq + - ne + - gt + - gte + - lt + - lte + type: string + description: "Specifies the comparison operator: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`.\n- `eq`: equals\n- `ne`: not equal\n- `gt`: greater than\n- `gte`: greater than or equal\n- `lt`: less than\n- `lte`: less than or equal\n" + default: eq + key: + type: string + description: The key to compare against the value. + value: + oneOf: + - type: string + - type: number + - type: boolean + description: "The value to compare against the attribute key; supports string,\nnumber, or boolean types." + additionalProperties: false + description: "A filter used to compare a specified attribute key to a given value using a defined comparison operation.\n" + x-oaiMeta: + name: ComparisonFilter + CompleteUploadRequest: + required: + - part_ids + type: object + properties: + part_ids: + type: array + items: + type: string + description: "The ordered list of Part IDs.\n" + md5: + type: string + description: "The optional md5 checksum for the file contents to verify if the bytes uploaded matches what you expect.\n" + additionalProperties: false + CompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of tokens in the generated completion. + default: 0 + prompt_tokens: + type: integer + description: Number of tokens in the prompt. + default: 0 + total_tokens: + type: integer + description: Total number of tokens used in the request (prompt + completion). + default: 0 + completion_tokens_details: + type: object + properties: + accepted_prediction_tokens: + type: integer + description: "When using Predicted Outputs, the number of tokens in the\nprediction that appeared in the completion.\n" + default: 0 + audio_tokens: + type: integer + description: Audio input tokens generated by the model. + default: 0 + reasoning_tokens: + type: integer + description: Tokens generated by the model for reasoning. + default: 0 + rejected_prediction_tokens: + type: integer + description: "When using Predicted Outputs, the number of tokens in the\nprediction that did not appear in the completion. However, like\nreasoning tokens, these tokens are still counted in the total\ncompletion tokens for purposes of billing, output, and context window\nlimits.\n" + default: 0 + description: Breakdown of tokens used in a completion. + prompt_tokens_details: + type: object + properties: + audio_tokens: + type: integer + description: Audio input tokens present in the prompt. + default: 0 + cached_tokens: + type: integer + description: Cached tokens present in the prompt. + default: 0 + description: Breakdown of tokens used in the prompt. + description: Usage statistics for the completion request. + CompoundFilter: + title: Compound Filter + required: + - type + - filters + type: object + properties: + type: + enum: + - and + - or + type: string + description: 'Type of operation: `and` or `or`.' + filters: + type: array + items: + oneOf: + - $ref: '#/components/schemas/ComparisonFilter' + description: "Array of filters to combine. Items can be `ComparisonFilter` or\n`CompoundFilter`." + additionalProperties: false + description: Combine multiple filters using `and` or `or`. + x-oaiMeta: + name: CompoundFilter + ComputerAction: + oneOf: + - $ref: '#/components/schemas/Click' + - $ref: '#/components/schemas/DoubleClick' + - $ref: '#/components/schemas/Drag' + - $ref: '#/components/schemas/KeyPress' + - $ref: '#/components/schemas/Move' + - $ref: '#/components/schemas/Screenshot' + - $ref: '#/components/schemas/Scroll' + - $ref: '#/components/schemas/Type' + - $ref: '#/components/schemas/Wait' + ComputerScreenshotImage: + required: + - type + type: object + properties: + type: + enum: + - computer_screenshot + type: string + description: "Specifies the event type. For a computer screenshot, this property is\nalways set to `computer_screenshot`.\n" + default: computer_screenshot + x-stainless-const: true + image_url: + type: string + description: The URL of the screenshot image. + file_id: + type: string + description: The identifier of an uploaded file that contains the screenshot. + description: "A computer screenshot image used with the computer use tool.\n" + ComputerToolCall: + title: Computer tool call + required: + - type + - id + - action + - call_id + - pending_safety_checks + - status + type: object + properties: + type: + enum: + - computer_call + type: string + description: The type of the computer call. Always `computer_call`. + default: computer_call + id: + type: string + description: The unique ID of the computer call. + call_id: + type: string + description: "An identifier used when responding to the tool call with output.\n" + action: + $ref: '#/components/schemas/ComputerAction' + pending_safety_checks: + type: array + items: + $ref: '#/components/schemas/ComputerToolCallSafetyCheck' + description: "The pending safety checks for the computer call.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A tool call to a computer use tool. See the\n[computer use guide](/docs/guides/tools-computer-use) for more information.\n" + ComputerToolCallOutput: + title: Computer tool call output + required: + - type + - call_id + - output + type: object + properties: + type: + enum: + - computer_call_output + type: string + description: "The type of the computer tool call output. Always `computer_call_output`.\n" + default: computer_call_output + x-stainless-const: true + id: + type: string + description: "The ID of the computer tool call output.\n" + call_id: + type: string + description: "The ID of the computer tool call that produced the output.\n" + acknowledged_safety_checks: + type: array + items: + $ref: '#/components/schemas/ComputerToolCallSafetyCheck' + description: "The safety checks reported by the API that have been acknowledged by the\ndeveloper.\n" + output: + $ref: '#/components/schemas/ComputerScreenshotImage' + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + description: "The output of a computer tool call.\n" + ComputerToolCallOutputResource: + allOf: + - $ref: '#/components/schemas/ComputerToolCallOutput' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the computer call tool output.\n" + ComputerToolCallSafetyCheck: + required: + - id + - code + - message + type: object + properties: + id: + type: string + description: The ID of the pending safety check. + code: + type: string + description: The type of the pending safety check. + message: + type: string + description: Details about the pending safety check. + description: "A pending safety check for the computer call.\n" + Content: + oneOf: + - $ref: '#/components/schemas/InputContent' + - $ref: '#/components/schemas/OutputContent' + description: "Multi-modal input and output contents.\n" + Coordinate: + title: Coordinate + required: + - x + - y + type: object + properties: + x: + type: integer + description: "The x-coordinate.\n" + y: + type: integer + description: "The y-coordinate.\n" + description: "An x/y coordinate pair, e.g. `{ x: 100, y: 200 }`.\n" + CostsResult: + required: + - object + type: object + properties: + object: + enum: + - organization.costs.result + type: string + x-stainless-const: true + amount: + type: object + properties: + value: + type: number + description: The numeric value of the cost. + currency: + type: string + description: Lowercase ISO-4217 currency e.g. "usd" + description: The monetary value in its associated currency. + line_item: + type: string + description: "When `group_by=line_item`, this field provides the line item of the\ngrouped costs result." + nullable: true + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped costs result." + nullable: true + description: The aggregated costs details of the specific time bucket. + x-oaiMeta: + name: Costs object + example: "{\n \"object\": \"organization.costs.result\",\n \"amount\": {\n \"value\": 0.06,\n \"currency\": \"usd\"\n },\n \"line_item\": \"Image models\",\n \"project_id\": \"proj_abc\"\n}\n" + CreateAssistantRequest: + required: + - model + type: object + properties: + model: + anyOf: + - type: string + - $ref: '#/components/schemas/AssistantSupportedModels' + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models) for descriptions of them.\n" + example: gpt-4o + x-oaiTypeLabel: string + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + reasoning_effort: + $ref: '#/components/schemas/ReasoningEffort' + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The [vector store](/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + chunking_strategy: + type: object + oneOf: + - title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + x-stainless-const: true + additionalProperties: false + description: "The default strategy. This strategy currently uses a\n`max_chunk_size_tokens` of `800` and\n`chunk_overlap_tokens` of `400`." + - title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-stainless-const: true + static: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: "The maximum number of tokens in each chunk. The default value is\n`800`. The minimum value is `100` and the\nmaximum value is `4096`." + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + additionalProperties: false + description: "The chunking strategy used to chunk the file(s). If not set, will\nuse the `auto` strategy." + metadata: + $ref: '#/components/schemas/Metadata' + description: "A helper to create a [vector store](/docs/api-reference/vector-stores/object) with file_ids and attach it to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + additionalProperties: false + CreateChatCompletionRequest: + allOf: + - $ref: '#/components/schemas/CreateModelResponseProperties' + - required: + - model + - messages + type: object + properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestMessage' + description: "A list of messages comprising the conversation so far. Depending on the\n[model](/docs/models) you use, different message types (modalities) are\nsupported, like [text](/docs/guides/text-generation),\n[images](/docs/guides/vision), and [audio](/docs/guides/audio).\n" + model: + $ref: '#/components/schemas/ModelIdsShared' + modalities: + $ref: '#/components/schemas/ResponseModalities' + reasoning_effort: + $ref: '#/components/schemas/ReasoningEffort' + max_completion_tokens: + type: integer + description: "An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n" + nullable: true + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on\ntheir existing frequency in the text so far, decreasing the model's\nlikelihood to repeat the same line verbatim.\n" + default: 0 + nullable: true + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on\nwhether they appear in the text so far, increasing the model's likelihood\nto talk about new topics.\n" + default: 0 + nullable: true + web_search_options: + title: Web search + type: object + properties: + user_location: + required: + - type + - approximate + type: object + properties: + type: + enum: + - approximate + type: string + description: "The type of location approximation. Always `approximate`.\n" + x-stainless-const: true + approximate: + $ref: '#/components/schemas/WebSearchLocation' + description: "Approximate location parameters for the search.\n" + nullable: true + search_context_size: + $ref: '#/components/schemas/WebSearchContextSize' + description: "This tool searches the web for relevant results to use in a response.\nLearn more about the [web search tool](/docs/guides/tools-web-search?api-mode=chat).\n" + top_logprobs: + maximum: 20.0 + minimum: 0.0 + type: integer + description: "An integer between 0 and 20 specifying the number of most likely tokens to\nreturn at each token position, each with an associated log probability.\n`logprobs` must be set to `true` if this parameter is used.\n" + nullable: true + response_format: + oneOf: + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/ResponseFormatJsonSchema' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + description: "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables\nStructured Outputs which ensures the model will match your supplied JSON\nschema. Learn more in the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n" + audio: + required: + - voice + - format + type: object + properties: + voice: + $ref: '#/components/schemas/VoiceIdsShared' + format: + enum: + - wav + - aac + - mp3 + - flac + - opus + - pcm16 + type: string + description: "Specifies the output audio format. Must be one of `wav`, `mp3`, `flac`,\n`opus`, or `pcm16`.\n" + description: "Parameters for audio output. Required when audio output is requested with\n`modalities: [\"audio\"]`. [Learn more](/docs/guides/audio).\n" + nullable: true + store: + type: boolean + description: "Whether or not to store the output of this chat completion request for\nuse in our [model distillation](/docs/guides/distillation) or\n[evals](/docs/guides/evals) products.\n" + default: false + nullable: true + stream: + type: boolean + description: "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](/docs/api-reference/chat/streaming)\nfor more information, along with the [streaming responses](/docs/guides/streaming-responses)\nguide for more information on how to handle the streaming events.\n" + default: false + nullable: true + stop: + $ref: '#/components/schemas/StopConfiguration' + logit_bias: + type: object + additionalProperties: + type: integer + description: "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the\ntokenizer) to an associated bias value from -100 to 100. Mathematically,\nthe bias is added to the logits generated by the model prior to sampling.\nThe exact effect will vary per model, but values between -1 and 1 should\ndecrease or increase likelihood of selection; values like -100 or 100\nshould result in a ban or exclusive selection of the relevant token.\n" + default: + nullable: true + x-oaiTypeLabel: map + logprobs: + type: boolean + description: "Whether to return log probabilities of the output tokens or not. If true,\nreturns the log probabilities of each output token returned in the\n`content` of `message`.\n" + default: false + nullable: true + max_tokens: + type: integer + description: "The maximum number of [tokens](/tokenizer) that can be generated in the\nchat completion. This value can be used to control\n[costs](https://openai.com/api/pricing/) for text generated via API.\n\nThis value is now deprecated in favor of `max_completion_tokens`, and is\nnot compatible with [o-series models](/docs/guides/reasoning).\n" + nullable: true + deprecated: true + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: "How many chat completion choices to generate for each input\nmessage. Note that you will be charged based on the number of\ngenerated tokens across all of the choices. Keep `n` as `1` to\nminimize costs." + default: 1 + nullable: true + example: 1 + prediction: + oneOf: + - $ref: '#/components/schemas/PredictionContent' + description: "Configuration for a [Predicted Output](/docs/guides/predicted-outputs),\nwhich can greatly improve response times when large parts of the model\nresponse are known ahead of time. This is most common when you are\nregenerating a file with only minor changes to most of the content.\n" + nullable: true + seed: + maximum: 9223372036854775800 + minimum: -9223372036854775800 + type: integer + description: "This feature is in Beta.\nIf specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + nullable: true + x-oaiMeta: + beta: true + stream_options: + $ref: '#/components/schemas/ChatCompletionStreamOptions' + tools: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTool' + description: "A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported.\n" + tool_choice: + $ref: '#/components/schemas/ChatCompletionToolChoiceOption' + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + function_call: + oneOf: + - enum: + - none + - auto + type: string + description: "`none` means the model will not call a function and instead generates a message. `auto` means the model can pick between generating a message or calling a function.\n" + - $ref: '#/components/schemas/ChatCompletionFunctionCallOption' + description: "Deprecated in favor of `tool_choice`.\n\nControls which (if any) function is called by the model.\n\n`none` means the model will not call a function and instead generates a\nmessage.\n\n`auto` means the model can pick between generating a message or calling a\nfunction.\n\nSpecifying a particular function via `{\"name\": \"my_function\"}` forces the\nmodel to call that function.\n\n`none` is the default when no functions are present. `auto` is the default\nif functions are present.\n" + deprecated: true + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionFunctions' + description: "Deprecated in favor of `tools`.\n\nA list of functions the model may generate JSON inputs for.\n" + deprecated: true + CreateChatCompletionResponse: + required: + - choices + - created + - id + - model + - object + type: object + properties: + id: + type: string + description: A unique identifier for the chat completion. + choices: + type: array + items: + required: + - finish_reason + - index + - message + - logprobs + type: object + properties: + finish_reason: + enum: + - stop + - length + - tool_calls + - content_filter + - function_call + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n" + index: + type: integer + description: The index of the choice in the list of choices. + message: + $ref: '#/components/schemas/ChatCompletionResponseMessage' + logprobs: + required: + - content + - refusal + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + refusal: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTokenLogprob' + description: A list of message refusal tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + description: "A list of chat completion choices. Can be more than one if `n` is\ngreater than 1." + created: + type: integer + description: "The Unix timestamp (in seconds) of when the chat completion was\ncreated." + model: + type: string + description: The model used for the chat completion. + service_tier: + $ref: '#/components/schemas/ServiceTier' + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + enum: + - chat.completion + type: string + description: 'The object type, which is always `chat.completion`.' + x-stainless-const: true + usage: + $ref: '#/components/schemas/CompletionUsage' + description: "Represents a chat completion response returned by model, based on\nthe provided input." + x-oaiMeta: + name: The chat completion object + group: chat + example: "{ \"id\": \"chatcmpl-B9MHDbslfkBeAs8l4bebGdFOJ6PeG\",\n \"object\": \"chat.completion\",\n \"created\": 1741570283,\n \"model\": \"gpt-4o-2024-08-06\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"The image shows a wooden boardwalk path running through a lush green field or meadow. The sky is bright blue with some scattered clouds, giving the scene a serene and peaceful atmosphere. Trees and shrubs are visible in the background.\",\n \"refusal\": null,\n \"annotations\": []\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 1117,\n \"completion_tokens\": 46,\n \"total_tokens\": 1163,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": 0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\": \"default\",\n \"system_fingerprint\": \"fp_fc9f1d7035\"\n}\n" + CreateChatCompletionStreamResponse: + required: + - choices + - created + - id + - model + - object + type: object + properties: + id: + type: string + description: "A unique identifier for the chat completion. Each chunk has the\nsame ID." + choices: + type: array + items: + required: + - delta + - finish_reason + - index + type: object + properties: + delta: + $ref: '#/components/schemas/ChatCompletionStreamResponseDelta' + logprobs: + required: + - content + - refusal + type: object + properties: + content: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTokenLogprob' + description: A list of message content tokens with log probability information. + nullable: true + refusal: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTokenLogprob' + description: A list of message refusal tokens with log probability information. + nullable: true + description: Log probability information for the choice. + nullable: true + finish_reason: + enum: + - stop + - length + - tool_calls + - content_filter + - function_call + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\n`content_filter` if content was omitted due to a flag from our content filters,\n`tool_calls` if the model called a tool, or `function_call` (deprecated) if the model called a function.\n" + nullable: true + index: + type: integer + description: The index of the choice in the list of choices. + description: "A list of chat completion choices. Can contain more than one elements if `n` is greater than 1. Can also be empty for the\nlast chunk if you set `stream_options: {\"include_usage\": true}`.\n" + created: + type: integer + description: "The Unix timestamp (in seconds) of when the chat completion was\ncreated. Each chunk has the same timestamp." + model: + type: string + description: The model to generate the completion. + service_tier: + $ref: '#/components/schemas/ServiceTier' + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + enum: + - chat.completion.chunk + type: string + description: 'The object type, which is always `chat.completion.chunk`.' + x-stainless-const: true + usage: + $ref: '#/components/schemas/CompletionUsage' + description: "Represents a streamed chunk of a chat completion response returned\nby the model, based on the provided input. \n[Learn more](/docs/guides/streaming-responses).\n" + x-oaiMeta: + name: The chat completion chunk object + group: chat + example: "{\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1694268190,\"model\":\"gpt-4o-mini\", \"system_fingerprint\": \"fp_44709d6fcb\", \"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"logprobs\":null,\"finish_reason\":null}]}\n\n{\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1694268190,\"model\":\"gpt-4o-mini\", \"system_fingerprint\": \"fp_44709d6fcb\", \"choices\":[{\"index\":0,\"delta\":{\"content\":\"Hello\"},\"logprobs\":null,\"finish_reason\":null}]}\n\n....\n\n{\"id\":\"chatcmpl-123\",\"object\":\"chat.completion.chunk\",\"created\":1694268190,\"model\":\"gpt-4o-mini\", \"system_fingerprint\": \"fp_44709d6fcb\", \"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}]}\n" + CreateCompletionRequest: + required: + - model + - prompt + type: object + properties: + model: + anyOf: + - type: string + - enum: + - gpt-3.5-turbo-instruct + - davinci-002 + - babbage-002 + type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models) for descriptions of them.\n" + x-oaiTypeLabel: string + prompt: + oneOf: + - type: string + default: '' + example: This is a test. + - type: array + items: + type: string + default: '' + example: This is a test. + - minItems: 1 + type: array + items: + type: integer + example: '[1212, 318, 257, 1332, 13]' + - minItems: 1 + type: array + items: + minItems: 1 + type: array + items: + type: integer + example: '[[1212, 318, 257, 1332, 13]]' + description: "The prompt(s) to generate completions for, encoded as a string, array of strings, array of tokens, or array of token arrays.\n\nNote that <|endoftext|> is the document separator that the model sees during training, so if a prompt is not specified the model will generate as if from the beginning of a new document.\n" + default: <|endoftext|> + nullable: true + best_of: + maximum: 20.0 + minimum: 0.0 + type: integer + description: "Generates `best_of` completions server-side and returns the \"best\" (the one with the highest log probability per token). Results cannot be streamed.\n\nWhen used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return ΓÇô `best_of` must be greater than `n`.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + default: 1 + nullable: true + echo: + type: boolean + description: "Echo back the prompt in addition to the completion\n" + default: false + nullable: true + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n\n[See more information about frequency and presence penalties.](/docs/guides/text-generation)\n" + default: 0 + nullable: true + logit_bias: + type: object + additionalProperties: + type: integer + description: "Modify the likelihood of specified tokens appearing in the completion.\n\nAccepts a JSON object that maps tokens (specified by their token ID in the GPT tokenizer) to an associated bias value from -100 to 100. You can use this [tokenizer tool](/tokenizer?view=bpe) to convert text to token IDs. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.\n\nAs an example, you can pass `{\"50256\": -100}` to prevent the <|endoftext|> token from being generated.\n" + default: + nullable: true + x-oaiTypeLabel: map + logprobs: + maximum: 5.0 + minimum: 0.0 + type: integer + description: "Include the log probabilities on the `logprobs` most likely output tokens, as well the chosen tokens. For example, if `logprobs` is 5, the API will return a list of the 5 most likely tokens. The API will always return the `logprob` of the sampled token, so there may be up to `logprobs+1` elements in the response.\n\nThe maximum value for `logprobs` is 5.\n" + default: + nullable: true + max_tokens: + minimum: 0.0 + type: integer + description: "The maximum number of [tokens](/tokenizer) that can be generated in the completion.\n\nThe token count of your prompt plus `max_tokens` cannot exceed the model's context length. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens.\n" + default: 16 + nullable: true + example: 16 + n: + maximum: 128.0 + minimum: 1.0 + type: integer + description: "How many completions to generate for each prompt.\n\n**Note:** Because this parameter generates many completions, it can quickly consume your token quota. Use carefully and ensure that you have reasonable settings for `max_tokens` and `stop`.\n" + default: 1 + nullable: true + example: 1 + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n\n[See more information about frequency and presence penalties.](/docs/guides/text-generation)\n" + default: 0 + nullable: true + seed: + type: integer + description: "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.\n\nDeterminism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.\n" + format: int64 + nullable: true + stop: + $ref: '#/components/schemas/StopConfiguration' + stream: + type: boolean + description: "Whether to stream back partial progress. If set, tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).\n" + default: false + nullable: true + stream_options: + $ref: '#/components/schemas/ChatCompletionStreamOptions' + suffix: + type: string + description: "The suffix that comes after a completion of inserted text.\n\nThis parameter is only supported for `gpt-3.5-turbo-instruct`.\n" + default: + nullable: true + example: test. + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or `top_p` but not both.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + default: 1 + nullable: true + example: 1 + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + CreateCompletionResponse: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + description: A unique identifier for the completion. + choices: + type: array + items: + required: + - finish_reason + - index + - logprobs + - text + type: object + properties: + finish_reason: + enum: + - stop + - length + - content_filter + type: string + description: "The reason the model stopped generating tokens. This will be `stop` if the model hit a natural stop point or a provided stop sequence,\n`length` if the maximum number of tokens specified in the request was reached,\nor `content_filter` if content was omitted due to a flag from our content filters.\n" + index: + type: integer + logprobs: + type: object + properties: + text_offset: + type: array + items: + type: integer + token_logprobs: + type: array + items: + type: number + tokens: + type: array + items: + type: string + top_logprobs: + type: array + items: + type: object + additionalProperties: + type: number + nullable: true + text: + type: string + description: "The list of completion choices the model generated for the input\nprompt." + created: + type: integer + description: The Unix timestamp (in seconds) of when the completion was created. + model: + type: string + description: The model used for completion. + system_fingerprint: + type: string + description: "This fingerprint represents the backend configuration that the model runs with.\n\nCan be used in conjunction with the `seed` request parameter to understand when backend changes have been made that might impact determinism.\n" + object: + enum: + - text_completion + type: string + description: 'The object type, which is always "text_completion"' + x-stainless-const: true + usage: + $ref: '#/components/schemas/CompletionUsage' + description: "Represents a completion response from the API. Note: both the streamed and non-streamed response objects share the same shape (unlike the chat endpoint).\n" + x-oaiMeta: + name: The completion object + legacy: true + example: "{\n \"id\": \"cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7\",\n \"object\": \"text_completion\",\n \"created\": 1589478378,\n \"model\": \"gpt-4-turbo\",\n \"choices\": [\n {\n \"text\": \"\\n\\nThis is indeed a test\",\n \"index\": 0,\n \"logprobs\": null,\n \"finish_reason\": \"length\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 5,\n \"completion_tokens\": 7,\n \"total_tokens\": 12\n }\n}\n" + CreateEmbeddingRequest: + required: + - model + - input + type: object + properties: + input: + oneOf: + - title: string + type: string + description: The string that will be turned into an embedding. + default: '' + example: This is a test. + - title: array + maxItems: 2048 + minItems: 1 + type: array + items: + type: string + default: '' + example: '[''This is a test.'']' + description: The array of strings that will be turned into an embedding. + - title: array + maxItems: 2048 + minItems: 1 + type: array + items: + type: integer + description: The array of integers that will be turned into an embedding. + example: '[1212, 318, 257, 1332, 13]' + - title: array + maxItems: 2048 + minItems: 1 + type: array + items: + minItems: 1 + type: array + items: + type: integer + description: "The array of arrays containing integers that will be turned into an\nembedding." + example: '[[1212, 318, 257, 1332, 13]]' + description: "Input text to embed, encoded as a string or array of tokens. To embed multiple inputs in a single request, pass an array of strings or array of token arrays. The input must not exceed the max input tokens for the model (8192 tokens for `text-embedding-ada-002`), cannot be an empty string, and any array must be 2048 dimensions or less. [Example Python code](https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken) for counting tokens. Some models may also impose a limit on total number of tokens summed across inputs.\n" + example: The quick brown fox jumped over the lazy dog + model: + anyOf: + - type: string + - enum: + - text-embedding-ada-002 + - text-embedding-3-small + - text-embedding-3-large + type: string + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models) for descriptions of them.\n" + example: text-embedding-3-small + x-oaiTypeLabel: string + encoding_format: + enum: + - float + - base64 + type: string + description: "The format to return the embeddings in. Can be either `float` or\n[`base64`](https://pypi.org/project/pybase64/)." + default: float + example: float + dimensions: + minimum: 1.0 + type: integer + description: "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.\n" + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + additionalProperties: false + CreateEmbeddingResponse: + required: + - object + - model + - data + - usage + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Embedding' + description: The list of embeddings generated by the model. + model: + type: string + description: The name of the model used to generate the embedding. + object: + enum: + - list + type: string + description: 'The object type, which is always "list".' + x-stainless-const: true + usage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: The number of tokens used by the prompt. + total_tokens: + type: integer + description: The total number of tokens used by the request. + description: The usage information for the request. + CreateEvalCompletionsRunDataSource: + title: CompletionsRunDataSource + required: + - type + - source + type: object + properties: + type: + enum: + - completions + type: string + description: The type of run data source. Always `completions`. + default: completions + input_messages: + oneOf: + - title: TemplateInputMessages + required: + - type + - template + type: object + properties: + type: + enum: + - template + type: string + description: The type of input messages. Always `template`. + template: + type: array + items: + oneOf: + - $ref: '#/components/schemas/EasyInputMessage' + - $ref: '#/components/schemas/EvalItem' + description: "A list of chat messages forming the prompt or context. May include\nvariable references to the \"item\" namespace, ie\n{{item.name}}." + - title: ItemReferenceInputMessages + required: + - type + - item_reference + type: object + properties: + type: + enum: + - item_reference + type: string + description: The type of input messages. Always `item_reference`. + item_reference: + type: string + description: 'A reference to a variable in the "item" namespace. Ie, "item.name"' + sampling_params: + type: object + properties: + temperature: + type: number + description: A higher temperature increases randomness in the outputs. + default: 1 + max_completion_tokens: + type: integer + description: The maximum number of tokens in the generated output. + top_p: + type: number + description: "An alternative to temperature for nucleus sampling; 1.0 includes\nall tokens." + default: 1 + seed: + type: integer + description: 'A seed value to initialize the randomness, during sampling.' + default: 42 + model: + type: string + description: "The name of the model to use for generating completions (e.g.\n\"o3-mini\")." + source: + oneOf: + - $ref: '#/components/schemas/EvalJsonlFileContentSource' + - $ref: '#/components/schemas/EvalJsonlFileIdSource' + - $ref: '#/components/schemas/EvalStoredCompletionsSource' + description: "A CompletionsRunDataSource object describing a model sampling configuration.\n" + x-oaiMeta: + name: The completions data source object used to configure an individual run + group: eval runs + example: "{\n \"name\": \"gpt-4o-mini-2024-07-18\",\n \"data_source\": {\n \"type\": \"completions\",\n \"input_messages\": {\n \"type\": \"item_reference\",\n \"item_reference\": \"item.input\"\n },\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"source\": {\n \"type\": \"stored_completions\",\n \"model\": \"gpt-4o-mini-2024-07-18\"\n }\n }\n}\n" + CreateEvalCustomDataSourceConfig: + title: CustomDataSourceConfig + required: + - item_schema + - type + type: object + properties: + type: + enum: + - custom + type: string + description: The type of data source. Always `custom`. + default: custom + x-stainless-const: true + item_schema: + type: object + description: The json schema for each row in the data source. + example: "{\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\"type\": \"string\"},\n \"age\": {\"type\": \"integer\"}\n },\n \"required\": [\"name\", \"age\"]\n}\n" + include_sample_schema: + type: boolean + description: "Whether the eval should expect you to populate the sample namespace\n(ie, by generating responses off of your data source)" + default: false + description: "A CustomDataSourceConfig object that defines the schema for the data source used for the evaluation runs.\nThis schema is used to define the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run\n" + x-oaiMeta: + name: The eval file data source config object + group: evals + example: "{\n \"type\": \"custom\",\n \"item_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\"type\": \"string\"},\n \"age\": {\"type\": \"integer\"}\n },\n \"required\": [\"name\", \"age\"]\n },\n \"include_sample_schema\": true\n}\n" + CreateEvalItem: + title: CreateEvalItem + type: object + oneOf: + - title: SimpleInputMessage + required: + - role + - content + type: object + properties: + role: + type: string + description: 'The role of the message (e.g. "system", "assistant", "user").' + content: + type: string + description: The content of the message. + - $ref: '#/components/schemas/EvalItem' + description: "A chat message that makes up the prompt or context. May include\nvariable references to the \"item\" namespace, ie {{item.name}}." + x-oaiMeta: + name: The chat message object used to configure an individual run + CreateEvalJsonlRunDataSource: + title: JsonlRunDataSource + required: + - type + - source + type: object + properties: + type: + enum: + - jsonl + type: string + description: The type of data source. Always `jsonl`. + default: jsonl + x-stainless-const: true + source: + oneOf: + - $ref: '#/components/schemas/EvalJsonlFileContentSource' + - $ref: '#/components/schemas/EvalJsonlFileIdSource' + description: "A JsonlRunDataSource object with that specifies a JSONL file that matches the eval\n" + x-oaiMeta: + name: The file data source object for the eval run configuration + group: evals + example: "{\n \"type\": \"jsonl\",\n \"source\": {\n \"type\": \"file_id\",\n \"id\": \"file-9GYS6xbkWgWhmE7VoLUWFg\"\n }\n}\n" + CreateEvalLabelModelGrader: + title: LabelModelGrader + required: + - type + - model + - input + - passing_labels + - labels + - name + type: object + properties: + type: + enum: + - label_model + type: string + description: 'The object type, which is always `label_model`.' + x-stainless-const: true + name: + type: string + description: The name of the grader. + model: + type: string + description: The model to use for the evaluation. Must support structured outputs. + input: + type: array + items: + $ref: '#/components/schemas/CreateEvalItem' + description: "A list of chat messages forming the prompt or context. May include\nvariable references to the \"item\" namespace, ie {{item.name}}." + labels: + type: array + items: + type: string + description: The labels to classify to each item in the evaluation. + passing_labels: + type: array + items: + type: string + description: "The labels that indicate a passing result. Must be a subset of\nlabels." + description: "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation.\n" + x-oaiMeta: + name: The eval label model grader object + group: evals + example: "{ \"type\": \"label_model\",\n \"model\": \"gpt-4o-2024-08-06\",\n \"input\": [\n {\n \"role\": \"system\",\n \"content\": \"Classify the sentiment of the following statement as one of 'positive', 'neutral', or 'negative'\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Statement: {{item.response}}\"\n }\n ],\n \"passing_labels\": [\"positive\"],\n \"labels\": [\"positive\", \"neutral\", \"negative\"],\n \"name\": \"Sentiment label grader\"\n}\n" + CreateEvalLogsDataSourceConfig: + title: LogsDataSourceConfig + required: + - type + type: object + properties: + type: + enum: + - logs + type: string + description: The type of data source. Always `logs`. + default: logs + x-stainless-const: true + metadata: + type: object + description: Metadata filters for the logs data source. + example: "{\n \"use_case\": \"customer_support_agent\"\n}\n" + description: "A data source config which specifies the metadata property of your stored completions query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.\n" + x-oaiMeta: + name: The logs data source object for evals + group: evals + example: "{\n \"type\": \"logs\",\n \"metadata\": {\n \"use_case\": \"customer_support_agent\"\n }\n}\n" + CreateEvalRequest: + title: CreateEvalRequest + required: + - data_source_config + - testing_criteria + type: object + properties: + name: + type: string + description: The name of the evaluation. + metadata: + $ref: '#/components/schemas/Metadata' + data_source_config: + type: object + oneOf: + - $ref: '#/components/schemas/CreateEvalCustomDataSourceConfig' + - $ref: '#/components/schemas/CreateEvalLogsDataSourceConfig' + description: The configuration for the data source used for the evaluation runs. + testing_criteria: + type: array + items: + oneOf: + - $ref: '#/components/schemas/CreateEvalLabelModelGrader' + - $ref: '#/components/schemas/EvalStringCheckGrader' + - $ref: '#/components/schemas/EvalTextSimilarityGrader' + - $ref: '#/components/schemas/EvalPythonGrader' + - $ref: '#/components/schemas/EvalScoreModelGrader' + description: A list of graders for all eval runs in this group. + CreateEvalResponsesRunDataSource: + title: ResponsesRunDataSource + required: + - type + - source + type: object + properties: + type: + enum: + - completions + type: string + description: The type of run data source. Always `completions`. + default: completions + input_messages: + oneOf: + - required: + - type + - template + type: object + properties: + type: + enum: + - template + type: string + description: The type of input messages. Always `template`. + template: + type: array + items: + oneOf: + - title: ChatMessage + required: + - role + - content + type: object + properties: + role: + type: string + description: 'The role of the message (e.g. "system", "assistant", "user").' + content: + type: string + description: The content of the message. + - $ref: '#/components/schemas/EvalItem' + description: "A list of chat messages forming the prompt or context. May include\nvariable references to the \"item\" namespace, ie\n{{item.name}}." + - required: + - type + - item_reference + type: object + properties: + type: + enum: + - item_reference + type: string + description: The type of input messages. Always `item_reference`. + item_reference: + type: string + description: 'A reference to a variable in the "item" namespace. Ie, "item.name"' + sampling_params: + type: object + properties: + temperature: + type: number + description: A higher temperature increases randomness in the outputs. + default: 1 + max_completion_tokens: + type: integer + description: The maximum number of tokens in the generated output. + top_p: + type: number + description: "An alternative to temperature for nucleus sampling; 1.0 includes\nall tokens." + default: 1 + seed: + type: integer + description: 'A seed value to initialize the randomness, during sampling.' + default: 42 + model: + type: string + description: "The name of the model to use for generating completions (e.g.\n\"o3-mini\")." + source: + oneOf: + - $ref: '#/components/schemas/EvalJsonlFileContentSource' + - $ref: '#/components/schemas/EvalJsonlFileIdSource' + - $ref: '#/components/schemas/EvalResponsesSource' + description: "A ResponsesRunDataSource object describing a model sampling configuration.\n" + x-oaiMeta: + name: The completions data source object used to configure an individual run + group: eval runs + example: "{\n \"name\": \"gpt-4o-mini-2024-07-18\",\n \"data_source\": {\n \"type\": \"completions\",\n \"input_messages\": {\n \"type\": \"item_reference\",\n \"item_reference\": \"item.input\"\n },\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"source\": {\n \"type\": \"stored_completions\",\n \"model\": \"gpt-4o-mini-2024-07-18\"\n }\n }\n}\n" + CreateEvalRunRequest: + title: CreateEvalRunRequest + required: + - data_source + type: object + properties: + name: + type: string + description: The name of the run. + metadata: + $ref: '#/components/schemas/Metadata' + data_source: + type: object + oneOf: + - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource' + - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource' + - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource' + description: Details about the run's data source. + CreateFileRequest: + required: + - file + - purpose + type: object + properties: + file: + type: string + description: "The File object (not file name) to be uploaded.\n" + format: binary + purpose: + enum: + - assistants + - batch + - fine-tune + - vision + - user_data + - evals + type: string + description: "The intended purpose of the uploaded file. One of: - `assistants`: Used in the Assistants API - `batch`: Used in the Batch API - `fine-tune`: Used for fine-tuning - `vision`: Images used for vision fine-tuning - `user_data`: Flexible file type for any purpose - `evals`: Used for eval data sets\n" + additionalProperties: false + CreateFineTuningCheckpointPermissionRequest: + required: + - project_ids + type: object + properties: + project_ids: + type: array + items: + type: string + description: The project identifiers to grant access to. + additionalProperties: false + CreateFineTuningJobRequest: + required: + - model + - training_file + type: object + properties: + model: + anyOf: + - type: string + - enum: + - babbage-002 + - davinci-002 + - gpt-3.5-turbo + - gpt-4o-mini + type: string + description: "The name of the model to fine-tune. You can select one of the\n[supported models](/docs/guides/fine-tuning#which-models-can-be-fine-tuned).\n" + example: gpt-4o-mini + x-oaiTypeLabel: string + training_file: + type: string + description: "The ID of an uploaded file that contains training data.\n\nSee [upload file](/docs/api-reference/files/create) for how to upload a file.\n\nYour dataset must be formatted as a JSONL file. Additionally, you must upload your file with the purpose `fine-tune`.\n\nThe contents of the file should differ depending on if the model uses the [chat](/docs/api-reference/fine-tuning/chat-input), [completions](/docs/api-reference/fine-tuning/completions-input) format, or if the fine-tuning method uses the [preference](/docs/api-reference/fine-tuning/preference-input) format.\n\nSee the [fine-tuning guide](/docs/guides/fine-tuning) for more details.\n" + example: file-abc123 + hyperparameters: + type: object + properties: + batch_size: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 256.0 + minimum: 1.0 + type: integer + description: "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.\n" + default: auto + learning_rate_multiplier: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - minimum: 0.0 + exclusiveMinimum: true + type: number + description: "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.\n" + default: auto + n_epochs: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 50.0 + minimum: 1.0 + type: integer + description: "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.\n" + default: auto + description: "The hyperparameters used for the fine-tuning job.\nThis value is now deprecated in favor of `method`, and should be passed in under the `method` parameter.\n" + deprecated: true + suffix: + maxLength: 64 + minLength: 1 + type: string + description: "A string of up to 64 characters that will be added to your fine-tuned model name.\n\nFor example, a `suffix` of \"custom-model-name\" would produce a model name like `ft:gpt-4o-mini:openai:custom-model-name:7p4lURel`.\n" + default: + nullable: true + validation_file: + type: string + description: "The ID of an uploaded file that contains validation data.\n\nIf you provide this file, the data is used to generate validation\nmetrics periodically during fine-tuning. These metrics can be viewed in\nthe fine-tuning results file.\nThe same data should not be present in both train and validation files.\n\nYour dataset must be formatted as a JSONL file. You must upload your file with the purpose `fine-tune`.\n\nSee the [fine-tuning guide](/docs/guides/fine-tuning) for more details.\n" + nullable: true + example: file-abc123 + integrations: + type: array + items: + required: + - type + - wandb + type: object + properties: + type: + oneOf: + - enum: + - wandb + type: string + x-stainless-const: true + description: "The type of integration to enable. Currently, only \"wandb\" (Weights and Biases) is supported.\n" + wandb: + required: + - project + type: object + properties: + project: + type: string + description: "The name of the project that the new run will be created under.\n" + example: my-wandb-project + name: + type: string + description: "A display name to set for the run. If not set, we will use the Job ID as the name.\n" + nullable: true + entity: + type: string + description: "The entity to use for the run. This allows you to set the team or username of the WandB user that you would\nlike associated with the run. If not set, the default entity for the registered WandB API key is used.\n" + nullable: true + tags: + type: array + items: + type: string + example: custom-tag + description: "A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some\ndefault tags are generated by OpenAI: \"openai/finetune\", \"openai/{base-model}\", \"openai/{ftjob-abcdef}\".\n" + description: "The settings for your integration with Weights and Biases. This payload specifies the project that\nmetrics will be sent to. Optionally, you can set an explicit display name for your run, add tags\nto your run, and set a default entity (team, username, etc) to be associated with your run.\n" + description: A list of integrations to enable for your fine-tuning job. + nullable: true + seed: + maximum: 2147483647.0 + minimum: 0.0 + type: integer + description: "The seed controls the reproducibility of the job. Passing in the same seed and job parameters should produce the same results, but may differ in rare cases.\nIf a seed is not specified, one will be generated for you.\n" + nullable: true + example: 42 + method: + $ref: '#/components/schemas/FineTuneMethod' + metadata: + $ref: '#/components/schemas/Metadata' + CreateImageEditRequest: + required: + - prompt + - image + type: object + properties: + image: + anyOf: + - type: string + format: binary + - maxItems: 16 + type: array + items: + type: string + format: binary + description: "The image(s) to edit. Must be a supported image file or an array of images.\n\nFor `gpt-image-1`, each image should be a `png`, `webp`, or `jpg` file less\nthan 25MB. You can provide up to 16 images.\n\nFor `dall-e-2`, you can only provide one image, and it should be a square\n`png` file less than 4MB.\n" + prompt: + type: string + description: "A text description of the desired image(s). The maximum length is\n1000 characters for `dall-e-2`, and 32000 characters for\n`gpt-image-1`." + example: A cute baby sea otter wearing a beret + mask: + type: string + description: "An additional image whose fully transparent areas (e.g. where alpha\nis zero) indicate where `image` should be edited. If there are\nmultiple images provided, the mask will be applied on the first\nimage. Must be a valid PNG file, less than 4MB, and have the same\ndimensions as `image`." + format: binary + model: + anyOf: + - type: string + - enum: + - dall-e-2 + - gpt-image-1 + type: string + x-stainless-const: true + description: "The model to use for image generation. Only `dall-e-2` and\n`gpt-image-1` are supported. Defaults to `dall-e-2` unless a\nparameter specific to `gpt-image-1` is used." + default: dall-e-2 + nullable: true + example: gpt-image-1 + x-oaiTypeLabel: string + n: + maximum: 10.0 + minimum: 1.0 + type: integer + description: The number of images to generate. Must be between 1 and 10. + default: 1 + nullable: true + example: 1 + size: + enum: + - 256x256 + - 512x512 + - 1024x1024 + - 1536x1024 + - 1024x1536 + - auto + type: string + description: "The size of the generated images. Must be one of `1024x1024`,\n`1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default\nvalue) for `gpt-image-1`, and one of `256x256`, `512x512`, or\n`1024x1024` for `dall-e-2`." + default: 1024x1024 + nullable: true + example: 1024x1024 + response_format: + enum: + - url + - b64_json + type: string + description: "The format in which the generated images are returned. Must be one\nof `url` or `b64_json`. URLs are only valid for 60 minutes after the\nimage has been generated. This parameter is only supported for\n`dall-e-2`, as `gpt-image-1` will always return base64-encoded\nimages." + default: url + nullable: true + example: url + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + quality: + enum: + - standard + - low + - medium + - high + - auto + type: string + description: "The quality of the image that will be generated. `high`, `medium` and `low` are only supported for `gpt-image-1`. `dall-e-2` only supports `standard` quality. Defaults to `auto`.\n" + default: auto + nullable: true + example: high + CreateImageRequest: + required: + - prompt + type: object + properties: + prompt: + type: string + description: "A text description of the desired image(s). The maximum length is\n32000 characters for `gpt-image-1`, 1000 characters for `dall-e-2`\nand 4000 characters for `dall-e-3`." + example: A cute baby sea otter + model: + anyOf: + - type: string + - enum: + - dall-e-2 + - dall-e-3 + - gpt-image-1 + type: string + description: "The model to use for image generation. One of `dall-e-2`,\n`dall-e-3`, or `gpt-image-1`. Defaults to `dall-e-2` unless a\nparameter specific to `gpt-image-1` is used." + default: dall-e-2 + nullable: true + example: gpt-image-1 + x-oaiTypeLabel: string + n: + maximum: 10.0 + minimum: 1.0 + type: integer + description: "The number of images to generate. Must be between 1 and 10. For\n`dall-e-3`, only `n=1` is supported." + default: 1 + nullable: true + example: 1 + quality: + enum: + - standard + - hd + - low + - medium + - high + - auto + type: string + description: "The quality of the image that will be generated.\n\n- `auto` (default value) will automatically select the best quality for the given model.\n- `high`, `medium` and `low` are supported for `gpt-image-1`.\n- `hd` and `standard` are supported for `dall-e-3`.\n- `standard` is the only option for `dall-e-2`.\n" + default: auto + nullable: true + example: medium + response_format: + enum: + - url + - b64_json + type: string + description: "The format in which generated images with `dall-e-2` and `dall-e-3`\nare returned. Must be one of `url` or `b64_json`. URLs are only\nvalid for 60 minutes after the image has been generated. This\nparameter isn't supported for `gpt-image-1` which will always return\nbase64-encoded images." + default: url + nullable: true + example: url + output_format: + enum: + - png + - jpeg + - webp + type: string + description: "The format in which the generated images are returned. This\nparameter is only supported for `gpt-image-1`. Must be one of `png`,\n`jpeg`, or `webp`." + default: png + nullable: true + example: png + output_compression: + type: integer + description: "The compression level (0-100%) for the generated images. This\nparameter is only supported for `gpt-image-1` with the `webp` or\n`jpeg` output formats, and defaults to 100." + default: 100 + nullable: true + example: 100 + size: + enum: + - auto + - 1024x1024 + - 1536x1024 + - 1024x1536 + - 256x256 + - 512x512 + - 1792x1024 + - 1024x1792 + type: string + description: "The size of the generated images. Must be one of `1024x1024`,\n`1536x1024` (landscape), `1024x1536` (portrait), or `auto` (default\nvalue) for `gpt-image-1`, one of `256x256`, `512x512`, or\n`1024x1024` for `dall-e-2`, and one of `1024x1024`, `1792x1024`, or\n`1024x1792` for `dall-e-3`." + default: auto + nullable: true + example: 1024x1024 + moderation: + enum: + - low + - auto + type: string + description: "Control the content-moderation level for images generated by\n`gpt-image-1`. Must be either `low` for less restrictive filtering\nor `auto` (default value)." + default: auto + nullable: true + example: low + background: + enum: + - transparent + - opaque + - auto + type: string + description: "Allows to set transparency for the background of the generated image(s).\nThis parameter is only supported for `gpt-image-1`. Must be one of\n`transparent`, `opaque` or `auto` (default value). When `auto` is used, the\nmodel will automatically determine the best background for the image.\n\nIf `transparent`, the output format needs to support transparency, so it\nshould be set to either `png` (default value) or `webp`.\n" + default: auto + nullable: true + example: transparent + style: + enum: + - vivid + - natural + type: string + description: "The style of the generated images. This parameter is only supported\nfor `dall-e-3`. Must be one of `vivid` or `natural`. Vivid causes\nthe model to lean towards generating hyper-real and dramatic images.\nNatural causes the model to produce more natural, less hyper-real\nlooking images." + default: vivid + nullable: true + example: vivid + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + CreateImageVariationRequest: + required: + - image + type: object + properties: + image: + type: string + description: "The image to use as the basis for the variation(s). Must be a valid\nPNG file, less than 4MB, and square." + format: binary + model: + anyOf: + - type: string + - enum: + - dall-e-2 + type: string + x-stainless-const: true + description: "The model to use for image generation. Only `dall-e-2` is supported\nat this time." + default: dall-e-2 + nullable: true + example: dall-e-2 + x-oaiTypeLabel: string + n: + maximum: 10.0 + minimum: 1.0 + type: integer + description: The number of images to generate. Must be between 1 and 10. + default: 1 + nullable: true + example: 1 + response_format: + enum: + - url + - b64_json + type: string + description: "The format in which the generated images are returned. Must be one\nof `url` or `b64_json`. URLs are only valid for 60 minutes after the\nimage has been generated." + default: url + nullable: true + example: url + size: + enum: + - 256x256 + - 512x512 + - 1024x1024 + type: string + description: "The size of the generated images. Must be one of `256x256`,\n`512x512`, or `1024x1024`." + default: 1024x1024 + nullable: true + example: 1024x1024 + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + CreateMessageRequest: + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: "The role of the entity that is creating the message. Allowed values include:\n- `user`: Indicates the message is sent by an actual user and should be used in most cases to represent user-generated messages.\n- `assistant`: Indicates the message is generated by the assistant. Use this value to insert messages from the assistant into the conversation.\n" + content: + oneOf: + - title: Text content + type: string + description: The text contents of the message. + - title: Array of content parts + minItems: 1 + type: array + items: + oneOf: + - $ref: '#/components/schemas/MessageContentImageFileObject' + - $ref: '#/components/schemas/MessageContentImageUrlObject' + - $ref: '#/components/schemas/MessageRequestContentTextObject' + description: "An array of content parts with a defined type, each can be of type\n`text` or images can be passed with `image_url` or `image_file`.\nImage types are only supported on [Vision-compatible\nmodels](/docs/models)." + attachments: + required: + - file_id + - tools + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly' + description: The tools to add this file to. + description: "A list of files attached to the message, and the tools they should\nbe added to." + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + CreateModelResponseProperties: + allOf: + - $ref: '#/components/schemas/ModelResponseProperties' + CreateModerationRequest: + required: + - input + type: object + properties: + input: + oneOf: + - type: string + description: A string of text to classify for moderation. + default: '' + example: I want to kill them. + - type: array + items: + type: string + default: '' + example: I want to kill them. + description: An array of strings to classify for moderation. + - type: array + items: + oneOf: + - required: + - type + - image_url + type: object + properties: + type: + enum: + - image_url + type: string + description: Always `image_url`. + x-stainless-const: true + image_url: + required: + - url + type: object + properties: + url: + type: string + description: Either a URL of the image or the base64 encoded image data. + format: uri + example: https://example.com/image.jpg + description: "Contains either an image URL or a data URL for a base64 encoded\nimage." + description: An object describing an image to classify. + - required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: Always `text`. + x-stainless-const: true + text: + type: string + description: A string of text to classify. + example: I want to kill them + description: An object describing text to classify. + description: An array of multi-modal inputs to the moderation model. + description: "Input (or inputs) to classify. Can be a single string, an array of strings, or\nan array of multi-modal input objects similar to other models.\n" + model: + anyOf: + - type: string + - enum: + - omni-moderation-latest + - omni-moderation-2024-09-26 + - text-moderation-latest + - text-moderation-stable + type: string + description: "The content moderation model you would like to use. Learn more in\n[the moderation guide](/docs/guides/moderation), and learn about\navailable models [here](/docs/models#moderation).\n" + default: omni-moderation-latest + example: omni-moderation-2024-09-26 + x-oaiTypeLabel: string + CreateModerationResponse: + required: + - id + - model + - results + type: object + properties: + id: + type: string + description: The unique identifier for the moderation request. + model: + type: string + description: The model used to generate the moderation results. + results: + type: array + items: + required: + - flagged + - categories + - category_scores + - category_applied_input_types + type: object + properties: + flagged: + type: boolean + description: Whether any of the below categories are flagged. + categories: + required: + - hate + - hate/threatening + - harassment + - harassment/threatening + - illicit + - illicit/violent + - self-harm + - self-harm/intent + - self-harm/instructions + - sexual + - sexual/minors + - violence + - violence/graphic + type: object + properties: + hate: + type: boolean + description: "Content that expresses, incites, or promotes hate based on race,\ngender, ethnicity, religion, nationality, sexual\norientation, disability status, or caste. Hateful content\naimed at non-protected groups (e.g., chess players) is\nharassment." + hate/threatening: + type: boolean + description: "Hateful content that also includes violence or serious harm towards\nthe targeted group based on race, gender, ethnicity,\nreligion, nationality, sexual orientation, disability\nstatus, or caste." + harassment: + type: boolean + description: "Content that expresses, incites, or promotes harassing language\ntowards any target." + harassment/threatening: + type: boolean + description: "Harassment content that also includes violence or serious harm\ntowards any target." + illicit: + type: boolean + description: "Content that includes instructions or advice that facilitate the\nplanning or execution of wrongdoing, or that gives advice\nor instruction on how to commit illicit acts. For example,\n\"how to shoplift\" would fit this category." + nullable: true + illicit/violent: + type: boolean + description: "Content that includes instructions or advice that facilitate the\nplanning or execution of wrongdoing that also includes\nviolence, or that gives advice or instruction on the\nprocurement of any weapon." + nullable: true + self-harm: + type: boolean + description: "Content that promotes, encourages, or depicts acts of self-harm,\nsuch as suicide, cutting, and eating disorders." + self-harm/intent: + type: boolean + description: "Content where the speaker expresses that they are engaging or\nintend to engage in acts of self-harm, such as suicide,\ncutting, and eating disorders." + self-harm/instructions: + type: boolean + description: "Content that encourages performing acts of self-harm, such as\nsuicide, cutting, and eating disorders, or that gives\ninstructions or advice on how to commit such acts." + sexual: + type: boolean + description: "Content meant to arouse sexual excitement, such as the description\nof sexual activity, or that promotes sexual services\n(excluding sex education and wellness)." + sexual/minors: + type: boolean + description: "Sexual content that includes an individual who is under 18 years\nold." + violence: + type: boolean + description: 'Content that depicts death, violence, or physical injury.' + violence/graphic: + type: boolean + description: "Content that depicts death, violence, or physical injury in graphic\ndetail." + description: 'A list of the categories, and whether they are flagged or not.' + category_scores: + required: + - hate + - hate/threatening + - harassment + - harassment/threatening + - illicit + - illicit/violent + - self-harm + - self-harm/intent + - self-harm/instructions + - sexual + - sexual/minors + - violence + - violence/graphic + type: object + properties: + hate: + type: number + description: The score for the category 'hate'. + hate/threatening: + type: number + description: The score for the category 'hate/threatening'. + harassment: + type: number + description: The score for the category 'harassment'. + harassment/threatening: + type: number + description: The score for the category 'harassment/threatening'. + illicit: + type: number + description: The score for the category 'illicit'. + illicit/violent: + type: number + description: The score for the category 'illicit/violent'. + self-harm: + type: number + description: The score for the category 'self-harm'. + self-harm/intent: + type: number + description: The score for the category 'self-harm/intent'. + self-harm/instructions: + type: number + description: The score for the category 'self-harm/instructions'. + sexual: + type: number + description: The score for the category 'sexual'. + sexual/minors: + type: number + description: The score for the category 'sexual/minors'. + violence: + type: number + description: The score for the category 'violence'. + violence/graphic: + type: number + description: The score for the category 'violence/graphic'. + description: "A list of the categories along with their scores as predicted by\nmodel." + category_applied_input_types: + required: + - hate + - hate/threatening + - harassment + - harassment/threatening + - illicit + - illicit/violent + - self-harm + - self-harm/intent + - self-harm/instructions + - sexual + - sexual/minors + - violence + - violence/graphic + type: object + properties: + hate: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'hate'. + hate/threatening: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'hate/threatening'. + harassment: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'harassment'. + harassment/threatening: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: "The applied input type(s) for the category\n'harassment/threatening'." + illicit: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'illicit'. + illicit/violent: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'illicit/violent'. + self-harm: + type: array + items: + enum: + - text + - image + type: string + description: The applied input type(s) for the category 'self-harm'. + self-harm/intent: + type: array + items: + enum: + - text + - image + type: string + description: The applied input type(s) for the category 'self-harm/intent'. + self-harm/instructions: + type: array + items: + enum: + - text + - image + type: string + description: "The applied input type(s) for the category\n'self-harm/instructions'." + sexual: + type: array + items: + enum: + - text + - image + type: string + description: The applied input type(s) for the category 'sexual'. + sexual/minors: + type: array + items: + enum: + - text + type: string + x-stainless-const: true + description: The applied input type(s) for the category 'sexual/minors'. + violence: + type: array + items: + enum: + - text + - image + type: string + description: The applied input type(s) for the category 'violence'. + violence/graphic: + type: array + items: + enum: + - text + - image + type: string + description: The applied input type(s) for the category 'violence/graphic'. + description: "A list of the categories along with the input type(s) that the\nscore applies to." + description: A list of moderation objects. + description: Represents if a given text input is potentially harmful. + x-oaiMeta: + name: The moderation object + example: "{\n \"id\": \"modr-0d9740456c391e43c445bf0f010940c7\",\n \"model\": \"omni-moderation-latest\",\n \"results\": [\n {\n \"flagged\": true,\n \"categories\": {\n \"harassment\": true,\n \"harassment/threatening\": true,\n \"sexual\": false,\n \"hate\": false,\n \"hate/threatening\": false,\n \"illicit\": false,\n \"illicit/violent\": false,\n \"self-harm/intent\": false,\n \"self-harm/instructions\": false,\n \"self-harm\": false,\n \"sexual/minors\": false,\n \"violence\": true,\n \"violence/graphic\": true\n },\n \"category_scores\": {\n \"harassment\": 0.8189693396524255,\n \"harassment/threatening\": 0.804985420696006,\n \"sexual\": 1.573112165348997e-6,\n \"hate\": 0.007562942636942845,\n \"hate/threatening\": 0.004208854591835476,\n \"illicit\": 0.030535955153511665,\n \"illicit/violent\": 0.008925306722380033,\n \"self-harm/intent\": 0.00023023930975076432,\n \"self-harm/instructions\": 0.0002293869201073356,\n \"self-harm\": 0.012598046106750154,\n \"sexual/minors\": 2.212566909570261e-8,\n \"violence\": 0.9999992735124786,\n \"violence/graphic\": 0.843064871157054\n },\n \"category_applied_input_types\": {\n \"harassment\": [\n \"text\"\n ],\n \"harassment/threatening\": [\n \"text\"\n ],\n \"sexual\": [\n \"text\",\n \"image\"\n ],\n \"hate\": [\n \"text\"\n ],\n \"hate/threatening\": [\n \"text\"\n ],\n \"illicit\": [\n \"text\"\n ],\n \"illicit/violent\": [\n \"text\"\n ],\n \"self-harm/intent\": [\n \"text\",\n \"image\"\n ],\n \"self-harm/instructions\": [\n \"text\",\n \"image\"\n ],\n \"self-harm\": [\n \"text\",\n \"image\"\n ],\n \"sexual/minors\": [\n \"text\"\n ],\n \"violence\": [\n \"text\",\n \"image\"\n ],\n \"violence/graphic\": [\n \"text\",\n \"image\"\n ]\n }\n }\n ]\n}\n" + CreateResponse: + allOf: + - $ref: '#/components/schemas/CreateModelResponseProperties' + - $ref: '#/components/schemas/ResponseProperties' + - required: + - model + - input + type: object + properties: + input: + oneOf: + - title: Text input + type: string + description: "A text input to the model, equivalent to a text input with the\n`user` role.\n" + - title: Input item list + type: array + items: + $ref: '#/components/schemas/InputItem' + description: "A list of one or many input items to the model, containing \ndifferent content types.\n" + description: "Text, image, or file inputs to the model, used to generate a response.\n\nLearn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Image inputs](/docs/guides/images)\n- [File inputs](/docs/guides/pdf-files)\n- [Conversation state](/docs/guides/conversation-state)\n- [Function calling](/docs/guides/function-calling)\n" + include: + type: array + items: + $ref: '#/components/schemas/Includable' + description: "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n" + nullable: true + parallel_tool_calls: + type: boolean + description: "Whether to allow the model to run tool calls in parallel.\n" + default: true + nullable: true + store: + type: boolean + description: "Whether to store the generated model response for later retrieval via\nAPI.\n" + default: true + nullable: true + stream: + type: boolean + description: "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format).\nSee the [Streaming section below](/docs/api-reference/responses-streaming)\nfor more information.\n" + default: false + nullable: true + CreateRunRequest: + required: + - assistant_id + type: object + properties: + assistant_id: + type: string + description: "The ID of the [assistant](/docs/api-reference/assistants) to use to\nexecute this run." + model: + anyOf: + - type: string + - $ref: '#/components/schemas/AssistantSupportedModels' + description: "The ID of the [Model](/docs/api-reference/models) to be used to\nexecute this run. If a value is provided here, it will override the\nmodel associated with the assistant. If not, the model associated\nwith the assistant will be used." + nullable: true + example: gpt-4o + x-oaiTypeLabel: string + reasoning_effort: + $ref: '#/components/schemas/ReasoningEffort' + instructions: + type: string + description: "Overrides the\n[instructions](/docs/api-reference/assistants/createAssistant) of\nthe assistant. This is useful for modifying the behavior on a\nper-run basis." + nullable: true + additional_instructions: + type: string + description: "Appends additional instructions at the end of the instructions for\nthe run. This is useful for modifying the behavior on a per-run\nbasis without overriding other instructions." + nullable: true + additional_messages: + type: array + items: + $ref: '#/components/schemas/CreateMessageRequest' + description: Adds additional messages to the thread before creating the run. + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "Override the tools the assistant can use for this run. This is\nuseful for modifying the behavior on a per-run basis." + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + allOf: + - $ref: '#/components/schemas/TruncationObject' + - nullable: true + tool_choice: + allOf: + - $ref: '#/components/schemas/AssistantsApiToolChoiceOption' + - nullable: true + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + additionalProperties: false + CreateSpeechRequest: + required: + - model + - input + - voice + type: object + properties: + model: + anyOf: + - type: string + - enum: + - tts-1 + - tts-1-hd + - gpt-4o-mini-tts + type: string + description: "One of the available [TTS models](/docs/models#tts): `tts-1`, `tts-1-hd` or `gpt-4o-mini-tts`.\n" + x-oaiTypeLabel: string + input: + maxLength: 4096 + type: string + description: "The text to generate audio for. The maximum length is 4096\ncharacters." + instructions: + maxLength: 4096 + type: string + description: "Control the voice of your generated audio with additional\ninstructions. Does not work with `tts-1` or `tts-1-hd`." + voice: + $ref: '#/components/schemas/VoiceIdsShared' + response_format: + enum: + - mp3 + - opus + - aac + - flac + - wav + - pcm + type: string + description: "The format to audio in. Supported formats are `mp3`, `opus`, `aac`,\n`flac`, `wav`, and `pcm`." + default: mp3 + speed: + maximum: 4.0 + minimum: 0.25 + type: number + description: "The speed of the generated audio. Select a value from `0.25` to\n`4.0`. `1.0` is the default." + default: 1 + additionalProperties: false + CreateThreadAndRunRequest: + required: + - assistant_id + type: object + properties: + assistant_id: + type: string + description: "The ID of the [assistant](/docs/api-reference/assistants) to use to\nexecute this run." + thread: + $ref: '#/components/schemas/CreateThreadRequest' + model: + anyOf: + - type: string + - enum: + - gpt-4.1 + - gpt-4.1-mini + - gpt-4.1-nano + - gpt-4.1-2025-04-14 + - gpt-4.1-mini-2025-04-14 + - gpt-4.1-nano-2025-04-14 + - gpt-4o + - gpt-4o-2024-11-20 + - gpt-4o-2024-08-06 + - gpt-4o-2024-05-13 + - gpt-4o-mini + - gpt-4o-mini-2024-07-18 + - gpt-4.5-preview + - gpt-4.5-preview-2025-02-27 + - gpt-4-turbo + - gpt-4-turbo-2024-04-09 + - gpt-4-0125-preview + - gpt-4-turbo-preview + - gpt-4-1106-preview + - gpt-4-vision-preview + - gpt-4 + - gpt-4-0314 + - gpt-4-0613 + - gpt-4-32k + - gpt-4-32k-0314 + - gpt-4-32k-0613 + - gpt-3.5-turbo + - gpt-3.5-turbo-16k + - gpt-3.5-turbo-0613 + - gpt-3.5-turbo-1106 + - gpt-3.5-turbo-0125 + - gpt-3.5-turbo-16k-0613 + type: string + description: "The ID of the [Model](/docs/api-reference/models) to be used to\nexecute this run. If a value is provided here, it will override the\nmodel associated with the assistant. If not, the model associated\nwith the assistant will be used." + nullable: true + example: gpt-4o + x-oaiTypeLabel: string + instructions: + type: string + description: "Override the default system message of the assistant. This is\nuseful for modifying the behavior on a per-run basis." + nullable: true + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "Override the tools the assistant can use for this run. This is\nuseful for modifying the behavior on a per-run basis." + nullable: true + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The ID of the [vector store](/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens that may be used over the course of the run. The run will make a best effort to use only the number of prompt tokens specified, across multiple turns of the run. If the run exceeds the number of prompt tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens that may be used over the course of the run. The run will make a best effort to use only the number of completion tokens specified, across multiple turns of the run. If the run exceeds the number of completion tokens specified, the run will end with status `incomplete`. See `incomplete_details` for more info.\n" + nullable: true + truncation_strategy: + allOf: + - $ref: '#/components/schemas/TruncationObject' + - nullable: true + tool_choice: + allOf: + - $ref: '#/components/schemas/AssistantsApiToolChoiceOption' + - nullable: true + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + additionalProperties: false + CreateThreadRequest: + type: object + properties: + messages: + type: array + items: + $ref: '#/components/schemas/CreateMessageRequest' + description: "A list of [messages](/docs/api-reference/messages) to start the\nthread with." + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + oneOf: + - required: + - vector_store_ids + - required: + - vector_stores + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The [vector store](/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + vector_stores: + maxItems: 1 + type: array + items: + type: object + properties: + file_ids: + maxItems: 10000 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs to add to the vector store. There can be a maximum of 10000 files in a vector store.\n" + chunking_strategy: + type: object + oneOf: + - title: Auto Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - auto + type: string + description: Always `auto`. + x-stainless-const: true + additionalProperties: false + description: "The default strategy. This strategy currently uses a\n`max_chunk_size_tokens` of `800` and\n`chunk_overlap_tokens` of `400`." + - title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-stainless-const: true + static: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: "The maximum number of tokens in each chunk. The default value is\n`800`. The minimum value is `100` and the\nmaximum value is `4096`." + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + additionalProperties: false + description: "The chunking strategy used to chunk the file(s). If not set, will\nuse the `auto` strategy." + metadata: + $ref: '#/components/schemas/Metadata' + description: "A helper to create a [vector store](/docs/api-reference/vector-stores/object) with file_ids and attach it to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + description: "Options to create a new thread. If no thread is provided when running a \nrequest, an empty thread will be created.\n" + CreateTranscriptionRequest: + required: + - file + - model + type: object + properties: + file: + type: string + description: "The audio file object (not file name) to transcribe, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.\n" + format: binary + x-oaiTypeLabel: file + model: + anyOf: + - type: string + - enum: + - whisper-1 + - gpt-4o-transcribe + - gpt-4o-mini-transcribe + type: string + x-stainless-const: true + description: "ID of the model to use. The options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1` (which is powered by our open source Whisper V2 model).\n" + example: gpt-4o-transcribe + x-oaiTypeLabel: string + language: + type: string + description: "The language of the input audio. Supplying the input language in [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format will improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment. The [prompt](/docs/guides/speech-to-text#prompting) should match the audio language.\n" + response_format: + $ref: '#/components/schemas/AudioResponseFormat' + temperature: + type: number + description: "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + default: 0 + 'include[]': + type: array + items: + $ref: '#/components/schemas/TranscriptionInclude' + description: "Additional information to include in the transcription response.\n`logprobs` will return the log probabilities of the tokens in the\nresponse to understand the model's confidence in the transcription.\n`logprobs` only works with response_format set to `json` and only with\nthe models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe`.\n" + 'timestamp_granularities[]': + type: array + items: + enum: + - word + - segment + type: string + description: "The timestamp granularities to populate for this transcription. `response_format` must be set `verbose_json` to use timestamp granularities. Either or both of these options are supported: `word`, or `segment`. Note: There is no additional latency for segment timestamps, but generating word timestamps incurs additional latency.\n" + default: + - segment + stream: + type: boolean + description: "If set to true, the model response data will be streamed to the client\nas it is generated using [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format). \nSee the [Streaming section of the Speech-to-Text guide](/docs/guides/speech-to-text?lang=curl#streaming-transcriptions)\nfor more information.\n\nNote: Streaming is not supported for the `whisper-1` model and will be ignored.\n" + default: false + nullable: true + additionalProperties: false + CreateTranscriptionResponseJson: + required: + - text + type: object + properties: + text: + type: string + description: The transcribed text. + logprobs: + type: array + items: + type: object + properties: + token: + type: string + description: The token in the transcription. + logprob: + type: number + description: The log probability of the token. + bytes: + type: array + items: + type: number + description: The bytes of the token. + description: "The log probabilities of the tokens in the transcription. Only returned with the models `gpt-4o-transcribe` and `gpt-4o-mini-transcribe` if `logprobs` is added to the `include` array.\n" + description: "Represents a transcription response returned by model, based on the\nprovided input." + x-oaiMeta: + name: The transcription object (JSON) + group: audio + example: "{ \"text\": \"Imagine the wildest idea that you've ever had, and you're curious about how it might scale to something that's a 100, a 1,000 times bigger. This is a place where you can get to do that.\"\n}\n" + CreateTranscriptionResponseStreamEvent: + anyOf: + - $ref: '#/components/schemas/TranscriptTextDeltaEvent' + - $ref: '#/components/schemas/TranscriptTextDoneEvent' + discriminator: + propertyName: type + CreateTranscriptionResponseVerboseJson: + required: + - language + - duration + - text + type: object + properties: + language: + type: string + description: The language of the input audio. + duration: + type: number + description: The duration of the input audio. + text: + type: string + description: The transcribed text. + words: + type: array + items: + $ref: '#/components/schemas/TranscriptionWord' + description: Extracted words and their corresponding timestamps. + segments: + type: array + items: + $ref: '#/components/schemas/TranscriptionSegment' + description: Segments of the transcribed text and their corresponding details. + description: "Represents a verbose json transcription response returned by model,\nbased on the provided input." + x-oaiMeta: + name: The transcription object (Verbose JSON) + group: audio + example: "{ \"task\": \"transcribe\",\n \"language\": \"english\",\n \"duration\": 8.470000267028809,\n \"text\": \"The beach was a popular spot on a hot summer day. People were swimming in the ocean, building sandcastles, and playing beach volleyball.\",\n \"segments\": [\n {\n \"id\": 0,\n \"seek\": 0,\n \"start\": 0.0,\n \"end\": 3.319999933242798,\n \"text\": \" The beach was a popular spot on a hot summer day.\",\n \"tokens\": [\n 50364, 440, 7534, 390, 257, 3743, 4008, 322, 257, 2368, 4266, 786, 13, 50530\n ],\n \"temperature\": 0.0,\n \"avg_logprob\": -0.2860786020755768,\n \"compression_ratio\": 1.2363636493682861,\n \"no_speech_prob\": 0.00985979475080967\n },\n ...\n ]\n}\n" + CreateTranslationRequest: + required: + - file + - model + type: object + properties: + file: + type: string + description: "The audio file object (not file name) translate, in one of these formats: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, or webm.\n" + format: binary + x-oaiTypeLabel: file + model: + anyOf: + - type: string + - enum: + - whisper-1 + type: string + x-stainless-const: true + description: "ID of the model to use. Only `whisper-1` (which is powered by our open source Whisper V2 model) is currently available.\n" + example: whisper-1 + x-oaiTypeLabel: string + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio segment. The [prompt](/docs/guides/speech-to-text#prompting) should be in English.\n" + response_format: + enum: + - json + - text + - srt + - verbose_json + - vtt + type: string + description: "The format of the output, in one of these options: `json`, `text`, `srt`, `verbose_json`, or `vtt`.\n" + default: json + temperature: + type: number + description: "The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. If set to 0, the model will use [log probability](https://en.wikipedia.org/wiki/Log_probability) to automatically increase the temperature until certain thresholds are hit.\n" + default: 0 + additionalProperties: false + CreateTranslationResponseJson: + required: + - text + type: object + properties: + text: + type: string + CreateTranslationResponseVerboseJson: + required: + - language + - duration + - text + type: object + properties: + language: + type: string + description: The language of the output translation (always `english`). + duration: + type: number + description: The duration of the input audio. + text: + type: string + description: The translated text. + segments: + type: array + items: + $ref: '#/components/schemas/TranscriptionSegment' + description: Segments of the translated text and their corresponding details. + CreateUploadRequest: + required: + - filename + - purpose + - bytes + - mime_type + type: object + properties: + filename: + type: string + description: "The name of the file to upload.\n" + purpose: + enum: + - assistants + - batch + - fine-tune + - vision + type: string + description: "The intended purpose of the uploaded file.\n\nSee the [documentation on File purposes](/docs/api-reference/files/create#files-create-purpose).\n" + bytes: + type: integer + description: "The number of bytes in the file you are uploading.\n" + mime_type: + type: string + description: "The MIME type of the file.\n\nThis must fall within the supported MIME types for your file purpose. See the supported MIME types for assistants and vision.\n" + additionalProperties: false + CreateVectorStoreFileBatchRequest: + required: + - file_ids + type: object + properties: + file_ids: + maxItems: 500 + minItems: 1 + type: array + items: + type: string + description: "A list of [File](/docs/api-reference/files) IDs that the vector\nstore should use. Useful for tools like `file_search` that can\naccess files." + chunking_strategy: + $ref: '#/components/schemas/ChunkingStrategyRequestParam' + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + additionalProperties: false + CreateVectorStoreFileRequest: + required: + - file_id + type: object + properties: + file_id: + type: string + description: "A [File](/docs/api-reference/files) ID that the vector store should\nuse. Useful for tools like `file_search` that can access files." + chunking_strategy: + $ref: '#/components/schemas/ChunkingStrategyRequestParam' + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + additionalProperties: false + CreateVectorStoreRequest: + type: object + properties: + file_ids: + maxItems: 500 + type: array + items: + type: string + description: "A list of [File](/docs/api-reference/files) IDs that the vector\nstore should use. Useful for tools like `file_search` that can\naccess files." + name: + type: string + description: The name of the vector store. + expires_after: + $ref: '#/components/schemas/VectorStoreExpirationAfter' + chunking_strategy: + type: object + oneOf: + - $ref: '#/components/schemas/AutoChunkingStrategyRequestParam' + - $ref: '#/components/schemas/StaticChunkingStrategyRequestParam' + description: "The chunking strategy used to chunk the file(s). If not set, will\nuse the `auto` strategy. Only applicable if `file_ids` is non-empty." + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + DeleteAssistantResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - assistant.deleted + type: string + x-stainless-const: true + DeleteCertificateResponse: + required: + - object + - id + type: object + properties: + object: + enum: + - certificate.deleted + type: string + description: 'The object type, must be `certificate.deleted`.' + x-stainless-const: true + id: + type: string + description: The ID of the certificate that was deleted. + DeleteFileResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + object: + enum: + - file + type: string + x-stainless-const: true + deleted: + type: boolean + DeleteFineTuningCheckpointPermissionResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + description: "The ID of the fine-tuned model checkpoint permission that was\ndeleted." + object: + enum: + - checkpoint.permission + type: string + description: 'The object type, which is always "checkpoint.permission".' + x-stainless-const: true + deleted: + type: boolean + description: "Whether the fine-tuned model checkpoint permission was successfully\ndeleted." + DeleteMessageResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.message.deleted + type: string + x-stainless-const: true + DeleteModelResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + type: string + DeleteThreadResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - thread.deleted + type: string + x-stainless-const: true + DeleteVectorStoreFileResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.file.deleted + type: string + x-stainless-const: true + DeleteVectorStoreResponse: + required: + - id + - object + - deleted + type: object + properties: + id: + type: string + deleted: + type: boolean + object: + enum: + - vector_store.deleted + type: string + x-stainless-const: true + DoneEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - done + type: string + x-stainless-const: true + data: + enum: + - '[DONE]' + type: string + x-stainless-const: true + description: Occurs when a stream ends. + x-oaiMeta: + dataDescription: '`data` is `[DONE]`' + DoubleClick: + title: DoubleClick + required: + - type + - x + - y + type: object + properties: + type: + enum: + - double_click + type: string + description: "Specifies the event type. For a double click action, this property is\nalways set to `double_click`.\n" + default: double_click + x-stainless-const: true + x: + type: integer + description: "The x-coordinate where the double click occurred.\n" + y: + type: integer + description: "The y-coordinate where the double click occurred.\n" + description: "A double click action.\n" + Drag: + title: Drag + required: + - type + - path + type: object + properties: + type: + enum: + - drag + type: string + description: "Specifies the event type. For a drag action, this property is \nalways set to `drag`.\n" + default: drag + x-stainless-const: true + path: + type: array + items: + $ref: '#/components/schemas/Coordinate' + description: "An array of coordinates representing the path of the drag action. Coordinates will appear as an array\nof objects, eg\n```\n[ { x: 100, y: 200 },\n { x: 200, y: 300 }\n]\n```\n" + description: "A drag action.\n" + EasyInputMessage: + title: Input message + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + - system + - developer + type: string + description: "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + content: + oneOf: + - title: Text input + type: string + description: "A text input to the model.\n" + - $ref: '#/components/schemas/InputMessageContentList' + description: "Text, image, or audio input to the model, used to generate a response.\nCan also contain previous assistant responses.\n" + type: + enum: + - message + type: string + description: "The type of the message input. Always `message`.\n" + x-stainless-const: true + description: "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n" + Embedding: + required: + - index + - object + - embedding + type: object + properties: + index: + type: integer + description: The index of the embedding in the list of embeddings. + embedding: + type: array + items: + type: number + description: "The embedding vector, which is a list of floats. The length of vector depends on the model as listed in the [embedding guide](/docs/guides/embeddings).\n" + object: + enum: + - embedding + type: string + description: 'The object type, which is always "embedding".' + x-stainless-const: true + description: "Represents an embedding vector returned by embedding endpoint.\n" + x-oaiMeta: + name: The embedding object + example: "{\n \"object\": \"embedding\",\n \"embedding\": [\n 0.0023064255,\n -0.009327292,\n .... (1536 floats total for ada-002)\n -0.0028842222,\n ],\n \"index\": 0\n}\n" + Error: + required: + - type + - message + - param + - code + type: object + properties: + code: + type: string + nullable: true + message: + type: string + param: + type: string + nullable: true + type: + type: string + ErrorEvent: + required: + - event + - data + type: object + properties: + event: + enum: + - error + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/Error' + description: "Occurs when an [error](/docs/guides/error-codes#api-errors) occurs.\nThis can happen due to an internal server error or a timeout." + x-oaiMeta: + dataDescription: '`data` is an [error](/docs/guides/error-codes#api-errors)' + ErrorResponse: + required: + - error + type: object + properties: + error: + $ref: '#/components/schemas/Error' + Eval: + title: Eval + required: + - id + - data_source_config + - object + - testing_criteria + - name + - created_at + - metadata + type: object + properties: + object: + enum: + - eval + type: string + description: The object type. + default: eval + x-stainless-const: true + id: + type: string + description: Unique identifier for the evaluation. + name: + type: string + description: The name of the evaluation. + example: Chatbot effectiveness Evaluation + data_source_config: + type: object + oneOf: + - $ref: '#/components/schemas/EvalCustomDataSourceConfig' + - $ref: '#/components/schemas/EvalStoredCompletionsDataSourceConfig' + description: Configuration of data sources used in runs of the evaluation. + testing_criteria: + type: array + items: + oneOf: + - $ref: '#/components/schemas/EvalLabelModelGrader' + - $ref: '#/components/schemas/EvalStringCheckGrader' + - $ref: '#/components/schemas/EvalTextSimilarityGrader' + - $ref: '#/components/schemas/EvalPythonGrader' + - $ref: '#/components/schemas/EvalScoreModelGrader' + description: A list of testing criteria. + default: eval + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the eval was created. + metadata: + $ref: '#/components/schemas/Metadata' + description: "An Eval object with a data source config and testing criteria.\nAn Eval represents a task to be done for your LLM integration.\nLike:\n - Improve the quality of my chatbot\n - See how well my chatbot handles customer support\n - Check if o3-mini is better at my usecase than gpt-4o\n" + x-oaiMeta: + name: The eval object + group: evals + example: "{\n \"object\": \"eval\",\n \"id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"data_source_config\": {\n \"type\": \"custom\",\n \"item_schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n },\n \"include_sample_schema\": true\n },\n \"testing_criteria\": [\n {\n \"name\": \"My string check grader\",\n \"type\": \"string_check\",\n \"input\": \"{{sample.output_text}}\",\n \"reference\": \"{{item.label}}\",\n \"operation\": \"eq\",\n }\n ],\n \"name\": \"External Data Eval\",\n \"created_at\": 1739314509,\n \"metadata\": {\n \"test\": \"synthetics\",\n }\n}\n" + EvalApiError: + title: EvalApiError + required: + - code + - message + type: object + properties: + code: + type: string + description: The error code. + message: + type: string + description: The error message. + description: "An object representing an error response from the Eval API.\n" + x-oaiMeta: + name: The API error object + group: evals + example: "{\n \"code\": \"internal_error\",\n \"message\": \"The eval run failed due to an internal error.\"\n}\n" + EvalCustomDataSourceConfig: + title: CustomDataSourceConfig + required: + - type + - schema + type: object + properties: + type: + enum: + - custom + type: string + description: The type of data source. Always `custom`. + default: custom + x-stainless-const: true + schema: + type: object + description: "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + example: "{\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n}\n" + description: "A CustomDataSourceConfig which specifies the schema of your `item` and optionally `sample` namespaces.\nThe response schema defines the shape of the data that will be:\n- Used to define your testing criteria and\n- What data is required when creating a run\n" + x-oaiMeta: + name: The eval custom data source config object + group: evals + example: "{\n \"type\": \"custom\",\n \"schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"label\": {\"type\": \"string\"},\n },\n \"required\": [\"label\"]\n }\n },\n \"required\": [\"item\"]\n }\n}\n" + EvalItem: + title: Eval message object + required: + - role + - content + type: object + properties: + role: + enum: + - user + - assistant + - system + - developer + type: string + description: "The role of the message input. One of `user`, `assistant`, `system`, or\n`developer`.\n" + content: + oneOf: + - title: Text input + type: string + description: "A text input to the model.\n" + - $ref: '#/components/schemas/InputTextContent' + - title: Output text + required: + - type + - text + type: object + properties: + type: + enum: + - output_text + type: string + description: "The type of the output text. Always `output_text`.\n" + x-stainless-const: true + text: + type: string + description: "The text output from the model.\n" + description: "A text output from the model.\n" + description: "Text inputs to the model - can contain template strings.\n" + type: + enum: + - message + type: string + description: "The type of the message input. Always `message`.\n" + x-stainless-const: true + description: "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role. Messages with the\n`assistant` role are presumed to have been generated by the model in previous\ninteractions.\n" + EvalJsonlFileContentSource: + title: EvalJsonlFileContentSource + required: + - type + - content + type: object + properties: + type: + enum: + - file_content + type: string + description: The type of jsonl source. Always `file_content`. + default: file_content + x-stainless-const: true + content: + type: array + items: + required: + - item + type: object + properties: + item: + type: object + sample: + type: object + description: The content of the jsonl file. + EvalJsonlFileIdSource: + title: EvalJsonlFileIdSource + required: + - type + - id + type: object + properties: + type: + enum: + - file_id + type: string + description: The type of jsonl source. Always `file_id`. + default: file_id + x-stainless-const: true + id: + type: string + description: The identifier of the file. + EvalLabelModelGrader: + title: LabelModelGrader + required: + - type + - model + - input + - passing_labels + - labels + - name + type: object + properties: + type: + enum: + - label_model + type: string + description: 'The object type, which is always `label_model`.' + x-stainless-const: true + name: + type: string + description: The name of the grader. + model: + type: string + description: The model to use for the evaluation. Must support structured outputs. + input: + type: array + items: + $ref: '#/components/schemas/EvalItem' + labels: + type: array + items: + type: string + description: The labels to assign to each item in the evaluation. + passing_labels: + type: array + items: + type: string + description: "The labels that indicate a passing result. Must be a subset of\nlabels." + description: "A LabelModelGrader object which uses a model to assign labels to each item\nin the evaluation.\n" + x-oaiMeta: + name: The eval label model grader object + group: evals + example: "{ \"name\": \"First label grader\",\n \"type\": \"label_model\",\n \"model\": \"gpt-4o-2024-08-06\",\n \"input\": [\n {\n \"type\": \"message\",\n \"role\": \"system\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"Classify the sentiment of the following statement as one of positive, neutral, or negative\"\n }\n },\n {\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"Statement: {{item.response}}\"\n }\n }\n ],\n \"passing_labels\": [\n \"positive\"\n ],\n \"labels\": [\n \"positive\",\n \"neutral\",\n \"negative\"\n ]\n}\n" + EvalList: + title: EvalList + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + description: "The type of this object. It is always set to \"list\".\n" + default: list + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/Eval' + description: "An array of eval objects.\n" + first_id: + type: string + description: The identifier of the first eval in the data array. + last_id: + type: string + description: The identifier of the last eval in the data array. + has_more: + type: boolean + description: Indicates whether there are more evals available. + description: "An object representing a list of evals.\n" + x-oaiMeta: + name: The eval list object + group: evals + example: "{\n \"object\": \"list\",\n \"data\": [\n {\n \"object\": \"eval\",\n \"id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"data_source_config\": {\n \"type\": \"custom\",\n \"schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\",\n \"properties\": {\n \"input\": {\n \"type\": \"string\"\n },\n \"ground_truth\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"input\",\n \"ground_truth\"\n ]\n }\n },\n \"required\": [\n \"item\"\n ]\n }\n },\n \"testing_criteria\": [\n {\n \"name\": \"String check\",\n \"id\": \"String check-2eaf2d8d-d649-4335-8148-9535a7ca73c2\",\n \"type\": \"string_check\",\n \"input\": \"{{item.input}}\",\n \"reference\": \"{{item.ground_truth}}\",\n \"operation\": \"eq\"\n }\n ],\n \"name\": \"External Data Eval\",\n \"created_at\": 1739314509,\n \"metadata\": {},\n }\n ],\n \"first_id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"last_id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"has_more\": true\n}\n" + EvalPythonGrader: + title: PythonGrader + required: + - type + - name + - source + type: object + properties: + type: + enum: + - python + type: string + description: 'The object type, which is always `python`.' + x-stainless-const: true + name: + type: string + description: The name of the grader. + source: + type: string + description: The source code of the python script. + pass_threshold: + type: number + description: The threshold for the score. + image_tag: + type: string + description: The image tag to use for the python script. + description: "A PythonGrader object that runs a python script on the input.\n" + x-oaiMeta: + name: The eval python grader object + group: evals + example: "{\n \"type\": \"string_check\",\n \"name\": \"Example string check grader\",\n \"input\": \"{{sample.output_text}}\",\n \"reference\": \"{{item.label}}\",\n \"operation\": \"eq\"\n}\n" + EvalResponsesSource: + title: EvalResponsesSource + required: + - type + type: object + properties: + type: + enum: + - responses + type: string + description: The type of run data source. Always `responses`. + metadata: + type: object + description: "Metadata filter for the responses. This is a query parameter used\nto select responses." + nullable: true + model: + type: string + description: "The name of the model to find responses for. This is a query\nparameter used to select responses." + nullable: true + instructions_search: + type: string + description: "Optional search string for instructions. This is a query parameter\nused to select responses." + nullable: true + created_after: + minimum: 0.0 + type: integer + description: "Only include items created after this timestamp (inclusive). This\nis a query parameter used to select responses." + nullable: true + created_before: + minimum: 0.0 + type: integer + description: "Only include items created before this timestamp (inclusive). This\nis a query parameter used to select responses." + nullable: true + has_tool_calls: + type: boolean + description: "Whether the response has tool calls. This is a query parameter used\nto select responses." + nullable: true + reasoning_effort: + $ref: '#/components/schemas/ReasoningEffort' + temperature: + type: number + description: "Sampling temperature. This is a query parameter used to select\nresponses." + nullable: true + top_p: + type: number + description: "Nucleus sampling parameter. This is a query parameter used to\nselect responses." + nullable: true + users: + type: array + items: + type: string + description: "List of user identifiers. This is a query parameter used to select\nresponses." + nullable: true + allow_parallel_tool_calls: + type: boolean + description: "Whether to allow parallel tool calls. This is a query parameter\nused to select responses." + nullable: true + description: "A EvalResponsesSource object describing a run data source configuration.\n" + x-oaiMeta: + name: The run data source object used to configure an individual run + group: eval runs + example: "{\n \"type\": \"responses\",\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"temperature\": 0.7,\n \"top_p\": 1.0,\n \"users\": [\"user1\", \"user2\"],\n \"allow_parallel_tool_calls\": true\n}\n" + EvalRun: + title: EvalRun + required: + - object + - id + - eval_id + - status + - model + - name + - created_at + - report_url + - result_counts + - per_model_usage + - per_testing_criteria_results + - data_source + - metadata + - error + type: object + properties: + object: + enum: + - eval.run + type: string + description: The type of the object. Always "eval.run". + default: eval.run + x-stainless-const: true + id: + type: string + description: Unique identifier for the evaluation run. + eval_id: + type: string + description: The identifier of the associated evaluation. + status: + type: string + description: The status of the evaluation run. + model: + type: string + description: 'The model that is evaluated, if applicable.' + name: + type: string + description: The name of the evaluation run. + created_at: + type: integer + description: Unix timestamp (in seconds) when the evaluation run was created. + report_url: + type: string + description: The URL to the rendered evaluation run report on the UI dashboard. + result_counts: + required: + - total + - errored + - failed + - passed + type: object + properties: + total: + type: integer + description: Total number of executed output items. + errored: + type: integer + description: Number of output items that resulted in an error. + failed: + type: integer + description: Number of output items that failed to pass the evaluation. + passed: + type: integer + description: Number of output items that passed the evaluation. + description: Counters summarizing the outcomes of the evaluation run. + per_model_usage: + type: array + items: + required: + - model_name + - invocation_count + - prompt_tokens + - completion_tokens + - total_tokens + - cached_tokens + type: object + properties: + model_name: + type: string + description: The name of the model. + invocation_count: + type: integer + description: The number of invocations. + prompt_tokens: + type: integer + description: The number of prompt tokens used. + completion_tokens: + type: integer + description: The number of completion tokens generated. + total_tokens: + type: integer + description: The total number of tokens used. + cached_tokens: + type: integer + description: The number of tokens retrieved from cache. + description: Usage statistics for each model during the evaluation run. + per_testing_criteria_results: + type: array + items: + required: + - testing_criteria + - passed + - failed + type: object + properties: + testing_criteria: + type: string + description: A description of the testing criteria. + passed: + type: integer + description: Number of tests passed for this criteria. + failed: + type: integer + description: Number of tests failed for this criteria. + description: Results per testing criteria applied during the evaluation run. + data_source: + type: object + oneOf: + - $ref: '#/components/schemas/CreateEvalJsonlRunDataSource' + - $ref: '#/components/schemas/CreateEvalCompletionsRunDataSource' + - $ref: '#/components/schemas/CreateEvalResponsesRunDataSource' + description: Information about the run's data source. + metadata: + $ref: '#/components/schemas/Metadata' + error: + $ref: '#/components/schemas/EvalApiError' + description: "A schema representing an evaluation run.\n" + x-oaiMeta: + name: The eval run object + group: evals + example: "{ \"object\": \"eval.run\",\n \"id\": \"evalrun_67e57965b480819094274e3a32235e4c\",\n \"eval_id\": \"eval_67e579652b548190aaa83ada4b125f47\",\n \"report_url\": \"https://platform.openai.com/evaluations/eval_67e579652b548190aaa83ada4b125f47?run_id=evalrun_67e57965b480819094274e3a32235e4c\",\n \"status\": \"queued\",\n \"model\": \"gpt-4o-mini\",\n \"name\": \"gpt-4o-mini\",\n \"created_at\": 1743092069,\n \"result_counts\": {\n \"total\": 0,\n \"errored\": 0,\n \"failed\": 0,\n \"passed\": 0\n },\n \"per_model_usage\": null,\n \"per_testing_criteria_results\": null,\n \"data_source\": {\n \"type\": \"completions\",\n \"source\": {\n \"type\": \"file_content\",\n \"content\": [\n {\n \"item\": {\n \"input\": \"Tech Company Launches Advanced Artificial Intelligence Platform\",\n \"ground_truth\": \"Technology\"\n }\n },\n {\n \"item\": {\n \"input\": \"Central Bank Increases Interest Rates Amid Inflation Concerns\",\n \"ground_truth\": \"Markets\"\n }\n },\n {\n \"item\": {\n \"input\": \"International Summit Addresses Climate Change Strategies\",\n \"ground_truth\": \"World\"\n }\n },\n {\n \"item\": {\n \"input\": \"Major Retailer Reports Record-Breaking Holiday Sales\",\n \"ground_truth\": \"Business\"\n }\n },\n {\n \"item\": {\n \"input\": \"National Team Qualifies for World Championship Finals\",\n \"ground_truth\": \"Sports\"\n }\n },\n {\n \"item\": {\n \"input\": \"Stock Markets Rally After Positive Economic Data Released\",\n \"ground_truth\": \"Markets\"\n }\n },\n {\n \"item\": {\n \"input\": \"Global Manufacturer Announces Merger with Competitor\",\n \"ground_truth\": \"Business\"\n }\n },\n {\n \"item\": {\n \"input\": \"Breakthrough in Renewable Energy Technology Unveiled\",\n \"ground_truth\": \"Technology\"\n }\n },\n {\n \"item\": {\n \"input\": \"World Leaders Sign Historic Climate Agreement\",\n \"ground_truth\": \"World\"\n }\n },\n {\n \"item\": {\n \"input\": \"Professional Athlete Sets New Record in Championship Event\",\n \"ground_truth\": \"Sports\"\n }\n },\n {\n \"item\": {\n \"input\": \"Financial Institutions Adapt to New Regulatory Requirements\",\n \"ground_truth\": \"Business\"\n }\n },\n {\n \"item\": {\n \"input\": \"Tech Conference Showcases Advances in Artificial Intelligence\",\n \"ground_truth\": \"Technology\"\n }\n },\n {\n \"item\": {\n \"input\": \"Global Markets Respond to Oil Price Fluctuations\",\n \"ground_truth\": \"Markets\"\n }\n },\n {\n \"item\": {\n \"input\": \"International Cooperation Strengthened Through New Treaty\",\n \"ground_truth\": \"World\"\n }\n },\n {\n \"item\": {\n \"input\": \"Sports League Announces Revised Schedule for Upcoming Season\",\n \"ground_truth\": \"Sports\"\n }\n }\n ]\n },\n \"input_messages\": {\n \"type\": \"template\",\n \"template\": [\n {\n \"type\": \"message\",\n \"role\": \"developer\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"Categorize a given news headline into one of the following topics: Technology, Markets, World, Business, or Sports.\\n\\n# Steps\\n\\n1. Analyze the content of the news headline to understand its primary focus.\\n2. Extract the subject matter, identifying any key indicators or keywords.\\n3. Use the identified indicators to determine the most suitable category out of the five options: Technology, Markets, World, Business, or Sports.\\n4. Ensure only one category is selected per headline.\\n\\n# Output Format\\n\\nRespond with the chosen category as a single word. For instance: \\\"Technology\\\", \\\"Markets\\\", \\\"World\\\", \\\"Business\\\", or \\\"Sports\\\".\\n\\n# Examples\\n\\n**Input**: \\\"Apple Unveils New iPhone Model, Featuring Advanced AI Features\\\" \\n**Output**: \\\"Technology\\\"\\n\\n**Input**: \\\"Global Stocks Mixed as Investors Await Central Bank Decisions\\\" \\n**Output**: \\\"Markets\\\"\\n\\n**Input**: \\\"War in Ukraine: Latest Updates on Negotiation Status\\\" \\n**Output**: \\\"World\\\"\\n\\n**Input**: \\\"Microsoft in Talks to Acquire Gaming Company for $2 Billion\\\" \\n**Output**: \\\"Business\\\"\\n\\n**Input**: \\\"Manchester United Secures Win in Premier League Football Match\\\" \\n**Output**: \\\"Sports\\\" \\n\\n# Notes\\n\\n- If the headline appears to fit into more than one category, choose the most dominant theme.\\n- Keywords or phrases such as \\\"stocks\\\", \\\"company acquisition\\\", \\\"match\\\", or technological brands can be good indicators for classification.\\n\"\n }\n },\n {\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"{{item.input}}\"\n }\n }\n ]\n },\n \"model\": \"gpt-4o-mini\",\n \"sampling_params\": {\n \"seed\": 42,\n \"temperature\": 1.0,\n \"top_p\": 1.0,\n \"max_completions_tokens\": 2048\n }\n },\n \"error\": null,\n \"metadata\": {}\n}\n" + EvalRunList: + title: EvalRunList + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + description: "The type of this object. It is always set to \"list\".\n" + default: list + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/EvalRun' + description: "An array of eval run objects.\n" + first_id: + type: string + description: The identifier of the first eval run in the data array. + last_id: + type: string + description: The identifier of the last eval run in the data array. + has_more: + type: boolean + description: Indicates whether there are more evals available. + description: "An object representing a list of runs for an evaluation.\n" + x-oaiMeta: + name: The eval run list object + group: evals + example: "{ \"object\": \"list\",\n \"data\": [\n {\n \"object\": \"eval.run\",\n \"id\": \"evalrun_67b7fbdad46c819092f6fe7a14189620\",\n \"eval_id\": \"eval_67b7fa9a81a88190ab4aa417e397ea21\",\n \"report_url\": \"https://platform.openai.com/evaluations/eval_67b7fa9a81a88190ab4aa417e397ea21?run_id=evalrun_67b7fbdad46c819092f6fe7a14189620\",\n \"status\": \"completed\",\n \"model\": \"o3-mini\",\n \"name\": \"Academic Assistant\",\n \"created_at\": 1740110812,\n \"result_counts\": {\n \"total\": 171,\n \"errored\": 0,\n \"failed\": 80,\n \"passed\": 91\n },\n \"per_model_usage\": null,\n \"per_testing_criteria_results\": [\n {\n \"testing_criteria\": \"String check grader\",\n \"passed\": 91,\n \"failed\": 80\n }\n ],\n \"run_data_source\": {\n \"type\": \"completions\",\n \"template_messages\": [\n {\n \"type\": \"message\",\n \"role\": \"system\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"You are a helpful assistant.\"\n }\n },\n {\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": {\n \"type\": \"input_text\",\n \"text\": \"Hello, can you help me with my homework?\"\n }\n }\n ],\n \"datasource_reference\": null,\n \"model\": \"o3-mini\",\n \"max_completion_tokens\": null,\n \"seed\": null,\n \"temperature\": null,\n \"top_p\": null\n },\n \"error\": null,\n \"metadata\": {\"test\": \"synthetics\"}\n }\n ],\n \"first_id\": \"evalrun_67abd54d60ec8190832b46859da808f7\",\n \"last_id\": \"evalrun_67abd54d60ec8190832b46859da808f7\",\n \"has_more\": false\n}\n" + EvalRunOutputItem: + title: EvalRunOutputItem + required: + - object + - id + - run_id + - eval_id + - created_at + - status + - datasource_item_id + - datasource_item + - results + - sample + type: object + properties: + object: + enum: + - eval.run.output_item + type: string + description: The type of the object. Always "eval.run.output_item". + default: eval.run.output_item + x-stainless-const: true + id: + type: string + description: Unique identifier for the evaluation run output item. + run_id: + type: string + description: "The identifier of the evaluation run associated with this output\nitem." + eval_id: + type: string + description: The identifier of the evaluation group. + created_at: + type: integer + description: Unix timestamp (in seconds) when the evaluation run was created. + status: + type: string + description: The status of the evaluation run. + datasource_item_id: + type: integer + description: The identifier for the data source item. + datasource_item: + type: object + description: Details of the input data source item. + results: + type: array + items: + type: object + description: A result object. + description: A list of results from the evaluation run. + sample: + required: + - input + - output + - finish_reason + - model + - usage + - error + - temperature + - max_completion_tokens + - top_p + - seed + type: object + properties: + input: + type: array + items: + required: + - role + - content + type: object + properties: + role: + type: string + description: 'The role of the message sender (e.g., system, user, developer).' + content: + type: string + description: The content of the message. + description: An input message. + description: An array of input messages. + output: + type: array + items: + type: object + properties: + role: + type: string + description: 'The role of the message (e.g. "system", "assistant", "user").' + content: + type: string + description: The content of the message. + description: An array of output messages. + finish_reason: + type: string + description: The reason why the sample generation was finished. + model: + type: string + description: The model used for generating the sample. + usage: + required: + - total_tokens + - completion_tokens + - prompt_tokens + - cached_tokens + type: object + properties: + total_tokens: + type: integer + description: The total number of tokens used. + completion_tokens: + type: integer + description: The number of completion tokens generated. + prompt_tokens: + type: integer + description: The number of prompt tokens used. + cached_tokens: + type: integer + description: The number of tokens retrieved from cache. + description: Token usage details for the sample. + error: + $ref: '#/components/schemas/EvalApiError' + temperature: + type: number + description: The sampling temperature used. + max_completion_tokens: + type: integer + description: The maximum number of tokens allowed for completion. + top_p: + type: number + description: The top_p value used for sampling. + seed: + type: integer + description: The seed used for generating the sample. + description: A sample containing the input and output of the evaluation run. + description: "A schema representing an evaluation run output item.\n" + x-oaiMeta: + name: The eval run output item object + group: evals + example: "{ \"object\": \"eval.run.output_item\",\n \"id\": \"outputitem_67abd55eb6548190bb580745d5644a33\",\n \"run_id\": \"evalrun_67abd54d60ec8190832b46859da808f7\",\n \"eval_id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"created_at\": 1739314509,\n \"status\": \"pass\",\n \"datasource_item_id\": 137,\n \"datasource_item\": {\n \"teacher\": \"To grade essays, I only check for style, content, and grammar.\",\n \"student\": \"I am a student who is trying to write the best essay.\"\n },\n \"results\": [\n {\n \"name\": \"String Check Grader\",\n \"type\": \"string-check-grader\",\n \"score\": 1.0,\n \"passed\": true,\n }\n ],\n \"sample\": {\n \"input\": [\n {\n \"role\": \"system\",\n \"content\": \"You are an evaluator bot...\"\n },\n {\n \"role\": \"user\",\n \"content\": \"You are assessing...\"\n }\n ],\n \"output\": [\n {\n \"role\": \"assistant\",\n \"content\": \"The rubric is not clear nor concise.\"\n }\n ],\n \"finish_reason\": \"stop\",\n \"model\": \"gpt-4o-2024-08-06\",\n \"usage\": {\n \"total_tokens\": 521,\n \"completion_tokens\": 2,\n \"prompt_tokens\": 519,\n \"cached_tokens\": 0\n },\n \"error\": null,\n \"temperature\": 1.0,\n \"max_completion_tokens\": 2048,\n \"top_p\": 1.0,\n \"seed\": 42\n }\n}\n" + EvalRunOutputItemList: + title: EvalRunOutputItemList + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + description: "The type of this object. It is always set to \"list\".\n" + default: list + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/EvalRunOutputItem' + description: "An array of eval run output item objects.\n" + first_id: + type: string + description: The identifier of the first eval run output item in the data array. + last_id: + type: string + description: The identifier of the last eval run output item in the data array. + has_more: + type: boolean + description: Indicates whether there are more eval run output items available. + description: "An object representing a list of output items for an evaluation run.\n" + x-oaiMeta: + name: The eval run output item list object + group: evals + example: "{ \"object\": \"list\",\n \"data\": [\n {\n \"object\": \"eval.run.output_item\",\n \"id\": \"outputitem_67abd55eb6548190bb580745d5644a33\",\n \"run_id\": \"evalrun_67abd54d60ec8190832b46859da808f7\",\n \"eval_id\": \"eval_67abd54d9b0081909a86353f6fb9317a\",\n \"created_at\": 1739314509,\n \"status\": \"pass\",\n \"datasource_item_id\": 137,\n \"datasource_item\": {\n \"teacher\": \"To grade essays, I only check for style, content, and grammar.\",\n \"student\": \"I am a student who is trying to write the best essay.\"\n },\n \"results\": [\n {\n \"name\": \"String Check Grader\",\n \"type\": \"string-check-grader\",\n \"score\": 1.0,\n \"passed\": true,\n }\n ],\n \"sample\": {\n \"input\": [\n {\n \"role\": \"system\",\n \"content\": \"You are an evaluator bot...\"\n },\n {\n \"role\": \"user\",\n \"content\": \"You are assessing...\"\n }\n ],\n \"output\": [\n {\n \"role\": \"assistant\",\n \"content\": \"The rubric is not clear nor concise.\"\n }\n ],\n \"finish_reason\": \"stop\",\n \"model\": \"gpt-4o-2024-08-06\",\n \"usage\": {\n \"total_tokens\": 521,\n \"completion_tokens\": 2,\n \"prompt_tokens\": 519,\n \"cached_tokens\": 0\n },\n \"error\": null,\n \"temperature\": 1.0,\n \"max_completion_tokens\": 2048,\n \"top_p\": 1.0,\n \"seed\": 42\n }\n },\n ],\n \"first_id\": \"outputitem_67abd55eb6548190bb580745d5644a33\",\n \"last_id\": \"outputitem_67abd55eb6548190bb580745d5644a33\",\n \"has_more\": false\n}\n" + EvalScoreModelGrader: + title: ScoreModelGrader + required: + - type + - name + - input + - model + type: object + properties: + type: + enum: + - score_model + type: string + description: 'The object type, which is always `score_model`.' + x-stainless-const: true + name: + type: string + description: The name of the grader. + model: + type: string + description: The model to use for the evaluation. + sampling_params: + type: object + description: The sampling parameters for the model. + input: + type: array + items: + $ref: '#/components/schemas/EvalItem' + description: The input text. This may include template strings. + pass_threshold: + type: number + description: The threshold for the score. + range: + type: array + items: + type: number + description: 'The range of the score. Defaults to `[0, 1]`.' + description: "A ScoreModelGrader object that uses a model to assign a score to the input.\n" + x-oaiMeta: + name: The eval score model grader object + group: evals + example: "{\n \"type\": \"score_model\",\n \"name\": \"Example score model grader\",\n \"input\": \"{{sample.output_text}}\",\n \"reference\": \"{{item.label}}\",\n \"operation\": \"eq\"\n}\n" + EvalStoredCompletionsDataSourceConfig: + title: StoredCompletionsDataSourceConfig + required: + - type + - schema + type: object + properties: + type: + enum: + - stored_completions + type: string + description: The type of data source. Always `stored_completions`. + default: stored_completions + x-stainless-const: true + metadata: + $ref: '#/components/schemas/Metadata' + schema: + type: object + description: "The json schema for the run data source items.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + description: "A StoredCompletionsDataSourceConfig which specifies the metadata property of your stored completions query.\nThis is usually metadata like `usecase=chatbot` or `prompt-version=v2`, etc.\nThe schema returned by this data source config is used to defined what variables are available in your evals.\n`item` and `sample` are both defined when using this data source config.\n" + x-oaiMeta: + name: The stored completions data source object for evals + group: evals + example: "{\n \"type\": \"stored_completions\",\n \"metadata\": {\n \"language\": \"english\"\n },\n \"schema\": {\n \"type\": \"object\",\n \"properties\": {\n \"item\": {\n \"type\": \"object\"\n },\n \"sample\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"item\",\n \"sample\"\n }\n}\n" + EvalStoredCompletionsSource: + title: StoredCompletionsRunDataSource + required: + - type + type: object + properties: + type: + enum: + - stored_completions + type: string + description: The type of source. Always `stored_completions`. + default: stored_completions + x-stainless-const: true + metadata: + $ref: '#/components/schemas/Metadata' + model: + type: string + description: 'An optional model to filter by (e.g., ''gpt-4o'').' + nullable: true + created_after: + type: integer + description: An optional Unix timestamp to filter items created after this time. + nullable: true + created_before: + type: integer + description: An optional Unix timestamp to filter items created before this time. + nullable: true + limit: + type: integer + description: An optional maximum number of items to return. + nullable: true + description: "A StoredCompletionsRunDataSource configuration describing a set of filters\n" + x-oaiMeta: + name: "The stored completions data source object used to configure an individual\nrun" + group: eval runs + example: "{\n \"type\": \"stored_completions\",\n \"model\": \"gpt-4o\",\n \"created_after\": 1668124800,\n \"created_before\": 1668124900,\n \"limit\": 100,\n \"metadata\": {}\n}\n" + EvalStringCheckGrader: + title: StringCheckGrader + required: + - type + - name + - input + - reference + - operation + type: object + properties: + type: + enum: + - string_check + type: string + description: 'The object type, which is always `string_check`.' + x-stainless-const: true + name: + type: string + description: The name of the grader. + input: + type: string + description: The input text. This may include template strings. + reference: + type: string + description: The reference text. This may include template strings. + operation: + enum: + - eq + - ne + - like + - ilike + type: string + description: "The string check operation to perform. One of `eq`, `ne`, `like`,\nor `ilike`." + description: "A StringCheckGrader object that performs a string comparison between input and reference using a specified operation.\n" + x-oaiMeta: + name: The eval string check grader object + group: evals + example: "{\n \"type\": \"string_check\",\n \"name\": \"Example string check grader\",\n \"input\": \"{{sample.output_text}}\",\n \"reference\": \"{{item.label}}\",\n \"operation\": \"eq\"\n}\n" + EvalTextSimilarityGrader: + title: TextSimilarityGrader + required: + - type + - input + - reference + - pass_threshold + - evaluation_metric + type: object + properties: + type: + enum: + - text_similarity + type: string + description: The type of grader. + default: text_similarity + x-stainless-const: true + name: + type: string + description: The name of the grader. + input: + type: string + description: The text being graded. + reference: + type: string + description: The text being graded against. + pass_threshold: + type: number + description: "A float score where a value greater than or equal indicates a\npassing grade." + evaluation_metric: + enum: + - fuzzy_match + - bleu + - gleu + - meteor + - rouge_1 + - rouge_2 + - rouge_3 + - rouge_4 + - rouge_5 + - rouge_l + type: string + description: "The evaluation metric to use. One of `fuzzy_match`, `bleu`, `gleu`,\n`meteor`, `rouge_1`, `rouge_2`, `rouge_3`, `rouge_4`, `rouge_5`, or\n`rouge_l`." + description: "A TextSimilarityGrader object which grades text based on similarity metrics.\n" + x-oaiMeta: + name: The eval text similarity grader object + group: evals + example: "{\n \"type\": \"text_similarity\",\n \"name\": \"example text similarity grader\",\n \"input\": \"The graded text\",\n \"reference\": \"The reference text\",\n \"pass_threshold\": 0.8,\n \"evaluation_metric\": \"fuzzy_match\"\n}\n" + FilePath: + title: File path + required: + - type + - file_id + - index + type: object + properties: + type: + enum: + - file_path + type: string + description: "The type of the file path. Always `file_path`.\n" + x-stainless-const: true + file_id: + type: string + description: "The ID of the file.\n" + index: + type: integer + description: "The index of the file in the list of files.\n" + description: "A path to a file.\n" + FileSearchRanker: + enum: + - auto + - default_2024_08_21 + type: string + description: "The ranker to use for the file search. If not specified will use\nthe `auto` ranker." + FileSearchRankingOptions: + title: File search tool call ranking options + required: + - score_threshold + type: object + properties: + ranker: + $ref: '#/components/schemas/FileSearchRanker' + score_threshold: + maximum: 1.0 + minimum: 0.0 + type: number + description: "The score threshold for the file search. All values must be a\nfloating point number between 0 and 1." + description: "The ranking options for the file search. If not specified, the file search tool will use the `auto` ranker and a score_threshold of 0.\n\nSee the [file search tool documentation](/docs/assistants/tools/file-search#customizing-file-search-settings) for more information.\n" + FileSearchToolCall: + title: File search tool call + required: + - id + - type + - status + - queries + type: object + properties: + id: + type: string + description: "The unique ID of the file search tool call.\n" + type: + enum: + - file_search_call + type: string + description: "The type of the file search tool call. Always `file_search_call`.\n" + x-stainless-const: true + status: + enum: + - in_progress + - searching + - completed + - incomplete + - failed + type: string + description: "The status of the file search tool call. One of `in_progress`, \n`searching`, `incomplete` or `failed`,\n" + queries: + type: array + items: + type: string + description: "The queries used to search for files.\n" + results: + type: array + items: + type: object + properties: + file_id: + type: string + description: "The unique ID of the file.\n" + text: + type: string + description: "The text that was retrieved from the file.\n" + filename: + type: string + description: "The name of the file.\n" + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + score: + type: number + description: "The relevance score of the file - a value between 0 and 1.\n" + format: float + description: "The results of the file search tool call.\n" + nullable: true + description: "The results of a file search tool call. See the\n[file search guide](/docs/guides/tools-file-search) for more information.\n" + FineTuneChatCompletionRequestAssistantMessage: + required: + - role + allOf: + - title: Assistant message + type: object + properties: + weight: + enum: + - 0 + - 1 + type: integer + description: Controls whether the assistant message is trained against (0 or 1) + - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage' + FineTuneChatRequestInput: + type: object + properties: + messages: + minItems: 1 + type: array + items: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestSystemMessage' + - $ref: '#/components/schemas/ChatCompletionRequestUserMessage' + - $ref: '#/components/schemas/FineTuneChatCompletionRequestAssistantMessage' + - $ref: '#/components/schemas/ChatCompletionRequestToolMessage' + - $ref: '#/components/schemas/ChatCompletionRequestFunctionMessage' + tools: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTool' + description: A list of tools the model may generate JSON inputs for. + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + functions: + maxItems: 128 + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionFunctions' + description: A list of functions the model may generate JSON inputs for. + deprecated: true + description: "The per-line training example of a fine-tuning input file for chat\nmodels using the supervised method." + x-oaiMeta: + name: Training format for chat models using the supervised method + example: "{ \"messages\": [\n { \"role\": \"user\", \"content\": \"What is the weather in San Francisco?\" },\n {\n \"role\": \"assistant\",\n \"tool_calls\": [\n {\n \"id\": \"call_id\",\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"arguments\": \"{\\\"location\\\": \\\"San Francisco, USA\\\", \\\"format\\\": \\\"celsius\\\"}\"\n }\n }\n ]\n }\n ],\n \"parallel_tool_calls\": false,\n \"tools\": [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and country, eg. San Francisco, USA\"\n },\n \"format\": { \"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"] }\n },\n \"required\": [\"location\", \"format\"]\n }\n }\n }\n ]\n}\n" + FineTuneCompletionRequestInput: + type: object + properties: + prompt: + type: string + description: The input prompt for this training example. + completion: + type: string + description: The desired completion for this training example. + description: "The per-line training example of a fine-tuning input file for\ncompletions models" + x-oaiMeta: + name: Training format for completions models + example: "{\n \"prompt\": \"What is the answer to 2+2\",\n \"completion\": \"4\"\n}\n" + FineTuneDPOMethod: + type: object + properties: + hyperparameters: + type: object + properties: + beta: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 2.0 + minimum: 0.0 + exclusiveMinimum: true + type: number + description: "The beta value for the DPO method. A higher beta value will increase the weight of the penalty between the policy and reference model.\n" + default: auto + batch_size: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 256.0 + minimum: 1.0 + type: integer + description: "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.\n" + default: auto + learning_rate_multiplier: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - minimum: 0.0 + exclusiveMinimum: true + type: number + description: "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.\n" + default: auto + n_epochs: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 50.0 + minimum: 1.0 + type: integer + description: "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.\n" + default: auto + description: The hyperparameters used for the fine-tuning job. + description: Configuration for the DPO fine-tuning method. + FineTuneMethod: + type: object + properties: + type: + enum: + - supervised + - dpo + type: string + description: The type of method. Is either `supervised` or `dpo`. + supervised: + $ref: '#/components/schemas/FineTuneSupervisedMethod' + dpo: + $ref: '#/components/schemas/FineTuneDPOMethod' + description: The method used for fine-tuning. + FineTunePreferenceRequestInput: + type: object + properties: + input: + type: object + properties: + messages: + minItems: 1 + type: array + items: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestSystemMessage' + - $ref: '#/components/schemas/ChatCompletionRequestUserMessage' + - $ref: '#/components/schemas/FineTuneChatCompletionRequestAssistantMessage' + - $ref: '#/components/schemas/ChatCompletionRequestToolMessage' + - $ref: '#/components/schemas/ChatCompletionRequestFunctionMessage' + tools: + type: array + items: + $ref: '#/components/schemas/ChatCompletionTool' + description: A list of tools the model may generate JSON inputs for. + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + preferred_completion: + maxItems: 1 + type: array + items: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage' + description: The preferred completion message for the output. + non_preferred_completion: + maxItems: 1 + type: array + items: + oneOf: + - $ref: '#/components/schemas/ChatCompletionRequestAssistantMessage' + description: The non-preferred completion message for the output. + description: "The per-line training example of a fine-tuning input file for chat\nmodels using the dpo method." + x-oaiMeta: + name: Training format for chat models using the preference method + example: "{ \"input\": {\n \"messages\": [\n { \"role\": \"user\", \"content\": \"What is the weather in San Francisco?\" }\n ]\n },\n \"preferred_completion\": [\n {\n \"role\": \"assistant\",\n \"content\": \"The weather in San Francisco is 70 degrees Fahrenheit.\"\n }\n ],\n \"non_preferred_completion\": [\n {\n \"role\": \"assistant\",\n \"content\": \"The weather in San Francisco is 21 degrees Celsius.\"\n }\n ]\n}\n" + FineTuneSupervisedMethod: + type: object + properties: + hyperparameters: + type: object + properties: + batch_size: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 256.0 + minimum: 1.0 + type: integer + description: "Number of examples in each batch. A larger batch size means that model parameters are updated less frequently, but with lower variance.\n" + default: auto + learning_rate_multiplier: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - minimum: 0.0 + exclusiveMinimum: true + type: number + description: "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid overfitting.\n" + default: auto + n_epochs: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 50.0 + minimum: 1.0 + type: integer + description: "The number of epochs to train the model for. An epoch refers to one full cycle through the training dataset.\n" + default: auto + description: The hyperparameters used for the fine-tuning job. + description: Configuration for the supervised fine-tuning method. + FineTuningCheckpointPermission: + title: FineTuningCheckpointPermission + required: + - created_at + - id + - object + - project_id + type: object + properties: + id: + type: string + description: "The permission identifier, which can be referenced in the API\nendpoints." + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the permission was created. + project_id: + type: string + description: The project identifier that the permission is for. + object: + enum: + - checkpoint.permission + type: string + description: 'The object type, which is always "checkpoint.permission".' + x-stainless-const: true + description: "The `checkpoint.permission` object represents a permission for a fine-tuned model checkpoint.\n" + x-oaiMeta: + name: The fine-tuned model checkpoint permission object + example: "{\n \"object\": \"checkpoint.permission\",\n \"id\": \"cp_zc4Q7MP6XxulcVzj4MZdwsAB\",\n \"created_at\": 1712211699,\n \"project_id\": \"proj_abGMw1llN8IrBb6SvvY5A1iH\"\n}\n" + FineTuningIntegration: + title: Fine-Tuning Job Integration + required: + - type + - wandb + type: object + properties: + type: + enum: + - wandb + type: string + description: The type of the integration being enabled for the fine-tuning job + x-stainless-const: true + wandb: + required: + - project + type: object + properties: + project: + type: string + description: "The name of the project that the new run will be created under.\n" + example: my-wandb-project + name: + type: string + description: "A display name to set for the run. If not set, we will use the Job ID as the name.\n" + nullable: true + entity: + type: string + description: "The entity to use for the run. This allows you to set the team or username of the WandB user that you would\nlike associated with the run. If not set, the default entity for the registered WandB API key is used.\n" + nullable: true + tags: + type: array + items: + type: string + example: custom-tag + description: "A list of tags to be attached to the newly created run. These tags are passed through directly to WandB. Some\ndefault tags are generated by OpenAI: \"openai/finetune\", \"openai/{base-model}\", \"openai/{ftjob-abcdef}\".\n" + description: "The settings for your integration with Weights and Biases. This payload specifies the project that\nmetrics will be sent to. Optionally, you can set an explicit display name for your run, add tags\nto your run, and set a default entity (team, username, etc) to be associated with your run.\n" + FineTuningJob: + title: FineTuningJob + required: + - created_at + - error + - finished_at + - fine_tuned_model + - hyperparameters + - id + - model + - object + - organization_id + - result_files + - status + - trained_tokens + - training_file + - validation_file + - seed + type: object + properties: + id: + type: string + description: 'The object identifier, which can be referenced in the API endpoints.' + created_at: + type: integer + description: "The Unix timestamp (in seconds) for when the fine-tuning job was\ncreated." + error: + required: + - code + - message + - param + type: object + properties: + code: + type: string + description: A machine-readable error code. + message: + type: string + description: A human-readable error message. + param: + type: string + description: "The parameter that was invalid, usually `training_file` or\n`validation_file`. This field will be null if the failure was\nnot parameter-specific." + nullable: true + description: "For fine-tuning jobs that have `failed`, this will contain more\ninformation on the cause of the failure." + nullable: true + fine_tuned_model: + type: string + description: "The name of the fine-tuned model that is being created. The value\nwill be null if the fine-tuning job is still running." + nullable: true + finished_at: + type: integer + description: "The Unix timestamp (in seconds) for when the fine-tuning job was\nfinished. The value will be null if the fine-tuning job is still\nrunning." + nullable: true + hyperparameters: + type: object + properties: + batch_size: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 256.0 + minimum: 1.0 + type: integer + description: "Number of examples in each batch. A larger batch size means that model parameters\nare updated less frequently, but with lower variance.\n" + default: auto + learning_rate_multiplier: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - minimum: 0.0 + exclusiveMinimum: true + type: number + description: "Scaling factor for the learning rate. A smaller learning rate may be useful to avoid\noverfitting.\n" + default: auto + n_epochs: + oneOf: + - enum: + - auto + type: string + x-stainless-const: true + - maximum: 50.0 + minimum: 1.0 + type: integer + description: "The number of epochs to train the model for. An epoch refers to one full cycle\nthrough the training dataset.\n" + default: auto + description: "The hyperparameters used for the fine-tuning job. This value will\nonly be returned when running `supervised` jobs." + model: + type: string + description: The base model that is being fine-tuned. + object: + enum: + - fine_tuning.job + type: string + description: 'The object type, which is always "fine_tuning.job".' + x-stainless-const: true + organization_id: + type: string + description: The organization that owns the fine-tuning job. + result_files: + type: array + items: + type: string + example: file-abc123 + description: "The compiled results file ID(s) for the fine-tuning job. You can\nretrieve the results with the [Files\nAPI](/docs/api-reference/files/retrieve-contents)." + status: + enum: + - validating_files + - queued + - running + - succeeded + - failed + - cancelled + type: string + description: "The current status of the fine-tuning job, which can be either\n`validating_files`, `queued`, `running`, `succeeded`, `failed`, or\n`cancelled`." + trained_tokens: + type: integer + description: "The total number of billable tokens processed by this fine-tuning\njob. The value will be null if the fine-tuning job is still running." + nullable: true + training_file: + type: string + description: "The file ID used for training. You can retrieve the training data\nwith the [Files API](/docs/api-reference/files/retrieve-contents)." + validation_file: + type: string + description: "The file ID used for validation. You can retrieve the validation\nresults with the [Files\nAPI](/docs/api-reference/files/retrieve-contents)." + nullable: true + integrations: + maxItems: 5 + type: array + items: + oneOf: + - $ref: '#/components/schemas/FineTuningIntegration' + description: A list of integrations to enable for this fine-tuning job. + nullable: true + seed: + type: integer + description: The seed used for the fine-tuning job. + estimated_finish: + type: integer + description: "The Unix timestamp (in seconds) for when the fine-tuning job is\nestimated to finish. The value will be null if the fine-tuning job\nis not running." + nullable: true + method: + $ref: '#/components/schemas/FineTuneMethod' + metadata: + $ref: '#/components/schemas/Metadata' + description: "The `fine_tuning.job` object represents a fine-tuning job that has been created through the API.\n" + x-oaiMeta: + name: The fine-tuning job object + example: "{\n \"object\": \"fine_tuning.job\",\n \"id\": \"ftjob-abc123\",\n \"model\": \"davinci-002\",\n \"created_at\": 1692661014,\n \"finished_at\": 1692661190,\n \"fine_tuned_model\": \"ft:davinci-002:my-org:custom_suffix:7q8mpxmy\",\n \"organization_id\": \"org-123\",\n \"result_files\": [\n \"file-abc123\"\n ],\n \"status\": \"succeeded\",\n \"validation_file\": null,\n \"training_file\": \"file-abc123\",\n \"hyperparameters\": {\n \"n_epochs\": 4,\n \"batch_size\": 1,\n \"learning_rate_multiplier\": 1.0\n },\n \"trained_tokens\": 5768,\n \"integrations\": [],\n \"seed\": 0,\n \"estimated_finish\": 0,\n \"method\": {\n \"type\": \"supervised\",\n \"supervised\": {\n \"hyperparameters\": {\n \"n_epochs\": 4,\n \"batch_size\": 1,\n \"learning_rate_multiplier\": 1.0\n }\n }\n },\n \"metadata\": {\n \"key\": \"value\"\n }\n}\n" + FineTuningJobCheckpoint: + title: FineTuningJobCheckpoint + required: + - created_at + - fine_tuning_job_id + - fine_tuned_model_checkpoint + - id + - metrics + - object + - step_number + type: object + properties: + id: + type: string + description: "The checkpoint identifier, which can be referenced in the API\nendpoints." + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the checkpoint was created. + fine_tuned_model_checkpoint: + type: string + description: The name of the fine-tuned checkpoint model that is created. + step_number: + type: integer + description: The step number that the checkpoint was created at. + metrics: + type: object + properties: + step: + type: number + train_loss: + type: number + train_mean_token_accuracy: + type: number + valid_loss: + type: number + valid_mean_token_accuracy: + type: number + full_valid_loss: + type: number + full_valid_mean_token_accuracy: + type: number + description: Metrics at the step number during the fine-tuning job. + fine_tuning_job_id: + type: string + description: "The name of the fine-tuning job that this checkpoint was created\nfrom." + object: + enum: + - fine_tuning.job.checkpoint + type: string + description: 'The object type, which is always "fine_tuning.job.checkpoint".' + x-stainless-const: true + description: "The `fine_tuning.job.checkpoint` object represents a model checkpoint for a fine-tuning job that is ready to use.\n" + x-oaiMeta: + name: The fine-tuning job checkpoint object + example: "{ \"object\": \"fine_tuning.job.checkpoint\",\n \"id\": \"ftckpt_qtZ5Gyk4BLq1SfLFWp3RtO3P\",\n \"created_at\": 1712211699,\n \"fine_tuned_model_checkpoint\": \"ft:gpt-4o-mini-2024-07-18:my-org:custom_suffix:9ABel2dg:ckpt-step-88\",\n \"fine_tuning_job_id\": \"ftjob-fpbNQ3H1GrMehXRf8cO97xTN\",\n \"metrics\": {\n \"step\": 88,\n \"train_loss\": 0.478,\n \"train_mean_token_accuracy\": 0.924,\n \"valid_loss\": 10.112,\n \"valid_mean_token_accuracy\": 0.145,\n \"full_valid_loss\": 0.567,\n \"full_valid_mean_token_accuracy\": 0.944\n },\n \"step_number\": 88\n}\n" + FineTuningJobEvent: + required: + - id + - object + - created_at + - level + - message + type: object + properties: + object: + enum: + - fine_tuning.job.event + type: string + description: 'The object type, which is always "fine_tuning.job.event".' + x-stainless-const: true + id: + type: string + description: The object identifier. + created_at: + type: integer + description: "The Unix timestamp (in seconds) for when the fine-tuning job was\ncreated." + level: + enum: + - info + - warn + - error + type: string + description: The log level of the event. + message: + type: string + description: The message of the event. + type: + enum: + - message + - metrics + type: string + description: The type of event. + data: + type: object + description: The data associated with the event. + description: Fine-tuning job event object + x-oaiMeta: + name: The fine-tuning job event object + example: "{\n \"object\": \"fine_tuning.job.event\",\n \"id\": \"ftevent-abc123\"\n \"created_at\": 1677610602,\n \"level\": \"info\",\n \"message\": \"Created fine-tuning job\",\n \"data\": {},\n \"type\": \"message\"\n}\n" + FunctionObject: + required: + - name + type: object + properties: + description: + type: string + description: "A description of what the function does, used by the model to\nchoose when and how to call the function." + name: + type: string + description: "The name of the function to be called. Must be a-z, A-Z, 0-9, or\ncontain underscores and dashes, with a maximum length of 64." + parameters: + $ref: '#/components/schemas/FunctionParameters' + strict: + type: boolean + description: "Whether to enable strict schema adherence when generating the\nfunction call. If set to true, the model will follow the exact\nschema defined in the `parameters` field. Only a subset of JSON\nSchema is supported when `strict` is `true`. Learn more about\nStructured Outputs in the [function calling\nguide](docs/guides/function-calling)." + default: false + nullable: true + FunctionParameters: + type: object + description: "The parameters the functions accepts, described as a JSON Schema object. See the [guide](/docs/guides/function-calling) for examples, and the [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for documentation about the format.\n\nOmitting `parameters` defines a function with an empty parameter list." + FunctionToolCall: + title: Function tool call + required: + - type + - call_id + - name + - arguments + type: object + properties: + id: + type: string + description: "The unique ID of the function tool call.\n" + type: + enum: + - function_call + type: string + description: "The type of the function tool call. Always `function_call`.\n" + x-stainless-const: true + call_id: + type: string + description: "The unique ID of the function tool call generated by the model.\n" + name: + type: string + description: "The name of the function to run.\n" + arguments: + type: string + description: "A JSON string of the arguments to pass to the function.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A tool call to run a function. See the\n[function calling guide](/docs/guides/function-calling) for more information.\n" + FunctionToolCallOutput: + title: Function tool call output + required: + - type + - call_id + - output + type: object + properties: + id: + type: string + description: "The unique ID of the function tool call output. Populated when this item\nis returned via API.\n" + type: + enum: + - function_call_output + type: string + description: "The type of the function tool call output. Always `function_call_output`.\n" + x-stainless-const: true + call_id: + type: string + description: "The unique ID of the function tool call generated by the model.\n" + output: + type: string + description: "A JSON string of the output of the function tool call.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "The output of a function tool call.\n" + FunctionToolCallOutputResource: + allOf: + - $ref: '#/components/schemas/FunctionToolCallOutput' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the function call tool output.\n" + FunctionToolCallResource: + allOf: + - $ref: '#/components/schemas/FunctionToolCall' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the function tool call.\n" + Image: + type: object + properties: + b64_json: + type: string + description: "The base64-encoded JSON of the generated image. Default value for\n`gpt-image-1`, and only present if `response_format` is set to\n`b64_json` for `dall-e-2` and `dall-e-3`." + url: + type: string + description: "When using `dall-e-2` or `dall-e-3`, the URL of the generated image\nif `response_format` is set to `url` (default value). Unsupported\nfor `gpt-image-1`." + revised_prompt: + type: string + description: "For `dall-e-3` only, the revised prompt that was used to generate\nthe image." + description: "Represents the content or the URL of an image generated by the\nOpenAI API." + ImagesResponse: + title: Image generation response + required: + - created + type: object + properties: + created: + type: integer + description: The Unix timestamp (in seconds) of when the image was created. + data: + type: array + items: + $ref: '#/components/schemas/Image' + description: The list of generated images. + usage: + required: + - total_tokens + - input_tokens + - output_tokens + - input_tokens_details + type: object + properties: + total_tokens: + type: integer + description: "The total number of tokens (images and text) used for the image\ngeneration." + input_tokens: + type: integer + description: The number of tokens (images and text) in the input prompt. + output_tokens: + type: integer + description: The number of image tokens in the output image. + input_tokens_details: + required: + - text_tokens + - image_tokens + type: object + properties: + text_tokens: + type: integer + description: The number of text tokens in the input prompt. + image_tokens: + type: integer + description: The number of image tokens in the input prompt. + description: The input tokens detailed information for the image generation. + description: "For `gpt-image-1` only, the token usage information for the image generation.\n" + description: The response from the image generation endpoint. + x-oaiMeta: + name: The image generation response + group: images + example: "{\n \"created\": 1713833628,\n \"data\": [\n {\n \"b64_json\": \"...\"\n }\n ],\n \"usage\": {\n \"total_tokens\": 100,\n \"input_tokens\": 50,\n \"output_tokens\": 50,\n \"input_tokens_details\": {\n \"text_tokens\": 10,\n \"image_tokens\": 40\n }\n }\n}\n" + Includable: + enum: + - file_search_call.results + - message.input_image.image_url + - computer_call_output.output.image_url + type: string + description: "Specify additional output data to include in the model response. Currently\nsupported values are:\n- `file_search_call.results`: Include the search results of the file search tool call.\n- `message.input_image.image_url`: Include image urls from the input message.\n- `computer_call_output.output.image_url`: Include image urls from the computer call output.\n" + InputAudio: + title: Audio input + required: + - type + - data + - format + type: object + properties: + type: + enum: + - input_audio + type: string + description: "The type of the input item. Always `input_audio`.\n" + x-stainless-const: true + data: + type: string + description: "Base64-encoded audio data.\n" + format: + enum: + - mp3 + - wav + type: string + description: "The format of the audio data. Currently supported formats are `mp3` and\n`wav`.\n" + description: "An audio input to the model.\n" + InputContent: + oneOf: + - $ref: '#/components/schemas/InputTextContent' + - $ref: '#/components/schemas/InputImageContent' + - $ref: '#/components/schemas/InputFileContent' + InputItem: + oneOf: + - $ref: '#/components/schemas/EasyInputMessage' + - $ref: '#/components/schemas/Item' + - $ref: '#/components/schemas/ItemReferenceParam' + discriminator: + propertyName: type + InputMessage: + title: Input message + required: + - role + - content + type: object + properties: + type: + enum: + - message + type: string + description: "The type of the message input. Always set to `message`.\n" + x-stainless-const: true + role: + enum: + - user + - system + - developer + type: string + description: "The role of the message input. One of `user`, `system`, or `developer`.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + content: + $ref: '#/components/schemas/InputMessageContentList' + description: "A message input to the model with a role indicating instruction following\nhierarchy. Instructions given with the `developer` or `system` role take\nprecedence over instructions given with the `user` role.\n" + InputMessageContentList: + title: Input item content list + type: array + items: + $ref: '#/components/schemas/InputContent' + description: "A list of one or many input items to the model, containing different content\ntypes.\n" + InputMessageResource: + allOf: + - $ref: '#/components/schemas/InputMessage' + - required: + - id + type: object + properties: + id: + type: string + description: "The unique ID of the message input.\n" + Invite: + required: + - object + - id + - email + - role + - status + - invited_at + - expires_at + type: object + properties: + object: + enum: + - organization.invite + type: string + description: 'The object type, which is always `organization.invite`' + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + email: + type: string + description: The email address of the individual to whom the invite was sent + role: + enum: + - owner + - reader + type: string + description: '`owner` or `reader`' + status: + enum: + - accepted + - expired + - pending + type: string + description: '`accepted`,`expired`, or `pending`' + invited_at: + type: integer + description: The Unix timestamp (in seconds) of when the invite was sent. + expires_at: + type: integer + description: The Unix timestamp (in seconds) of when the invite expires. + accepted_at: + type: integer + description: The Unix timestamp (in seconds) of when the invite was accepted. + projects: + type: array + items: + type: object + properties: + id: + type: string + description: Project's public ID + role: + enum: + - member + - owner + type: string + description: Project membership role + description: "The projects that were granted membership upon acceptance of the\ninvite." + description: Represents an individual `invite` to the organization. + x-oaiMeta: + name: The invite object + example: "{\n \"object\": \"organization.invite\",\n \"id\": \"invite-abc\",\n \"email\": \"user@example.com\",\n \"role\": \"owner\",\n \"status\": \"accepted\",\n \"invited_at\": 1711471533,\n \"expires_at\": 1711471533,\n \"accepted_at\": 1711471533,\n \"projects\": [\n {\n \"id\": \"project-xyz\",\n \"role\": \"member\"\n }\n ]\n}\n" + InviteDeleteResponse: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - organization.invite.deleted + type: string + description: 'The object type, which is always `organization.invite.deleted`' + x-stainless-const: true + id: + type: string + deleted: + type: boolean + InviteListResponse: + required: + - object + - data + type: object + properties: + object: + enum: + - list + type: string + description: 'The object type, which is always `list`' + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/Invite' + first_id: + type: string + description: The first `invite_id` in the retrieved `list` + last_id: + type: string + description: The last `invite_id` in the retrieved `list` + has_more: + type: boolean + description: "The `has_more` property is used for pagination to indicate there\nare additional results." + InviteRequest: + required: + - email + - role + type: object + properties: + email: + type: string + description: Send an email to this address + role: + enum: + - reader + - owner + type: string + description: '`owner` or `reader`' + projects: + type: array + items: + required: + - id + - role + type: object + properties: + id: + type: string + description: Project's public ID + role: + enum: + - member + - owner + type: string + description: Project membership role + description: "An array of projects to which membership is granted at the same\ntime the org invite is accepted. If omitted, the user will be\ninvited to the default project for compatibility with legacy\nbehavior." + Item: + type: object + oneOf: + - $ref: '#/components/schemas/InputMessage' + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ComputerCallOutputItemParam' + - $ref: '#/components/schemas/WebSearchToolCall' + - $ref: '#/components/schemas/FunctionToolCall' + - $ref: '#/components/schemas/FunctionCallOutputItemParam' + - $ref: '#/components/schemas/ReasoningItem' + description: "Content item used to generate a response.\n" + discriminator: + propertyName: type + ItemResource: + oneOf: + - $ref: '#/components/schemas/InputMessageResource' + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ComputerToolCallOutputResource' + - $ref: '#/components/schemas/WebSearchToolCall' + - $ref: '#/components/schemas/FunctionToolCallResource' + - $ref: '#/components/schemas/FunctionToolCallOutputResource' + description: "Content item used to generate a response.\n" + discriminator: + propertyName: type + KeyPress: + title: KeyPress + required: + - type + - keys + type: object + properties: + type: + enum: + - keypress + type: string + description: "Specifies the event type. For a keypress action, this property is \nalways set to `keypress`.\n" + default: keypress + x-stainless-const: true + keys: + type: array + items: + type: string + description: "One of the keys the model is requesting to be pressed.\n" + description: "The combination of keys the model is requesting to be pressed. This is an\narray of strings, each representing a key.\n" + description: "A collection of keypresses the model would like to perform.\n" + ListAssistantsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/AssistantObject' + first_id: + type: string + example: asst_abc123 + last_id: + type: string + example: asst_abc456 + has_more: + type: boolean + example: false + x-oaiMeta: + name: List assistants response object + group: chat + example: "{ \"object\": \"list\",\n \"data\": [\n {\n \"id\": \"asst_abc123\",\n \"object\": \"assistant\",\n \"created_at\": 1698982736,\n \"name\": \"Coding Tutor\",\n \"description\": null,\n \"model\": \"gpt-4o\",\n \"instructions\": \"You are a helpful assistant designed to make me better at coding!\",\n \"tools\": [],\n \"tool_resources\": {},\n \"metadata\": {},\n \"top_p\": 1.0,\n \"temperature\": 1.0,\n \"response_format\": \"auto\"\n },\n {\n \"id\": \"asst_abc456\",\n \"object\": \"assistant\",\n \"created_at\": 1698982718,\n \"name\": \"My Assistant\",\n \"description\": null,\n \"model\": \"gpt-4o\",\n \"instructions\": \"You are a helpful assistant designed to make me better at coding!\",\n \"tools\": [],\n \"tool_resources\": {},\n \"metadata\": {},\n \"top_p\": 1.0,\n \"temperature\": 1.0,\n \"response_format\": \"auto\"\n },\n {\n \"id\": \"asst_abc789\",\n \"object\": \"assistant\",\n \"created_at\": 1698982643,\n \"name\": null,\n \"description\": null,\n \"model\": \"gpt-4o\",\n \"instructions\": null,\n \"tools\": [],\n \"tool_resources\": {},\n \"metadata\": {},\n \"top_p\": 1.0,\n \"temperature\": 1.0,\n \"response_format\": \"auto\"\n }\n ],\n \"first_id\": \"asst_abc123\",\n \"last_id\": \"asst_abc789\",\n \"has_more\": false\n}\n" + ListAuditLogsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/AuditLog' + first_id: + type: string + example: audit_log-defb456h8dks + last_id: + type: string + example: audit_log-hnbkd8s93s + has_more: + type: boolean + ListBatchesResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Batch' + first_id: + type: string + example: batch_abc123 + last_id: + type: string + example: batch_abc456 + has_more: + type: boolean + object: + enum: + - list + type: string + x-stainless-const: true + ListCertificatesResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/Certificate' + first_id: + type: string + example: cert_abc + last_id: + type: string + example: cert_abc + has_more: + type: boolean + object: + enum: + - list + type: string + x-stainless-const: true + ListFilesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/OpenAIFile' + first_id: + type: string + example: file-abc123 + last_id: + type: string + example: file-abc456 + has_more: + type: boolean + example: false + ListFineTuningCheckpointPermissionResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/FineTuningCheckpointPermission' + object: + enum: + - list + type: string + x-stainless-const: true + first_id: + type: string + nullable: true + last_id: + type: string + nullable: true + has_more: + type: boolean + ListFineTuningJobCheckpointsResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/FineTuningJobCheckpoint' + object: + enum: + - list + type: string + x-stainless-const: true + first_id: + type: string + nullable: true + last_id: + type: string + nullable: true + has_more: + type: boolean + ListFineTuningJobEventsResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/FineTuningJobEvent' + object: + enum: + - list + type: string + x-stainless-const: true + has_more: + type: boolean + ListMessagesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/MessageObject' + first_id: + type: string + example: msg_abc123 + last_id: + type: string + example: msg_abc123 + has_more: + type: boolean + example: false + ListModelsResponse: + required: + - object + - data + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/Model' + ListPaginatedFineTuningJobsResponse: + required: + - object + - data + - has_more + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/FineTuningJob' + has_more: + type: boolean + object: + enum: + - list + type: string + x-stainless-const: true + ListRunStepsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/RunStepObject' + first_id: + type: string + example: step_abc123 + last_id: + type: string + example: step_abc456 + has_more: + type: boolean + example: false + ListRunsResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/RunObject' + first_id: + type: string + example: run_abc123 + last_id: + type: string + example: run_abc456 + has_more: + type: boolean + example: false + ListVectorStoreFilesResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/VectorStoreFileObject' + first_id: + type: string + example: file-abc123 + last_id: + type: string + example: file-abc456 + has_more: + type: boolean + example: false + ListVectorStoresResponse: + required: + - object + - data + - first_id + - last_id + - has_more + properties: + object: + type: string + example: list + data: + type: array + items: + $ref: '#/components/schemas/VectorStoreObject' + first_id: + type: string + example: vs_abc123 + last_id: + type: string + example: vs_abc456 + has_more: + type: boolean + example: false + LogProbProperties: + required: + - token + - logprob + - bytes + type: object + properties: + token: + type: string + description: "The token that was used to generate the log probability.\n" + logprob: + type: number + description: "The log probability of the token.\n" + bytes: + type: array + items: + type: integer + description: "The bytes that were used to generate the log probability.\n" + description: "A log probability object.\n" + MessageContentImageFileObject: + title: Image file + required: + - type + - image_file + type: object + properties: + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-stainless-const: true + image_file: + required: + - file_id + type: object + properties: + file_id: + type: string + description: "The [File](/docs/api-reference/files) ID of the image in the\nmessage content. Set `purpose=\"vision\"` when uploading the File\nif you need to later display the file content." + detail: + enum: + - auto + - low + - high + type: string + description: "Specifies the detail level of the image if specified by the user.\n`low` uses fewer tokens, you can opt in to high resolution using\n`high`." + default: auto + description: "References an image [File](/docs/api-reference/files) in the\ncontent of a message." + MessageContentImageUrlObject: + title: Image URL + required: + - type + - image_url + type: object + properties: + type: + enum: + - image_url + type: string + description: The type of the content part. + x-stainless-const: true + image_url: + required: + - url + type: object + properties: + url: + type: string + description: "The external URL of the image, must be a supported image types:\njpeg, jpg, png, gif, webp." + format: uri + detail: + enum: + - auto + - low + - high + type: string + description: "Specifies the detail level of the image. `low` uses fewer tokens,\nyou can opt in to high resolution using `high`. Default value is\n`auto`" + default: auto + description: References an image URL in the content of a message. + MessageContentRefusalObject: + title: Refusal + required: + - type + - refusal + type: object + properties: + type: + enum: + - refusal + type: string + description: Always `refusal`. + x-stainless-const: true + refusal: + type: string + description: The refusal content generated by the assistant. + MessageContentTextAnnotationsFileCitationObject: + title: File citation + required: + - type + - text + - file_citation + - start_index + - end_index + type: object + properties: + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-stainless-const: true + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: "A citation within the message that points to a specific quote from\na specific File associated with the assistant or the message. Generated\nwhen the assistant uses the \"file_search\" tool to search files." + MessageContentTextAnnotationsFilePathObject: + title: File path + required: + - type + - text + - file_path + - start_index + - end_index + type: object + properties: + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-stainless-const: true + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + required: + - file_id + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: "A URL for the file that's generated when the assistant used the\n`code_interpreter` tool to generate a file." + MessageContentTextObject: + title: Text + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: Always `text`. + x-stainless-const: true + text: + required: + - value + - annotations + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/MessageContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/MessageContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + MessageDeltaContentImageFileObject: + title: Image file + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - image_file + type: string + description: Always `image_file`. + x-stainless-const: true + image_file: + type: object + properties: + file_id: + type: string + description: "The [File](/docs/api-reference/files) ID of the image in the\nmessage content. Set `purpose=\"vision\"` when uploading the File\nif you need to later display the file content." + detail: + enum: + - auto + - low + - high + type: string + description: "Specifies the detail level of the image if specified by the user.\n`low` uses fewer tokens, you can opt in to high resolution using\n`high`." + default: auto + description: "References an image [File](/docs/api-reference/files) in the\ncontent of a message." + MessageDeltaContentImageUrlObject: + title: Image URL + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - image_url + type: string + description: Always `image_url`. + x-stainless-const: true + image_url: + type: object + properties: + url: + type: string + description: "The URL of the image, must be a supported image types: jpeg, jpg,\npng, gif, webp." + detail: + enum: + - auto + - low + - high + type: string + description: "Specifies the detail level of the image. `low` uses fewer tokens,\nyou can opt in to high resolution using `high`." + default: auto + description: References an image URL in the content of a message. + MessageDeltaContentRefusalObject: + title: Refusal + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the refusal part in the message. + type: + enum: + - refusal + type: string + description: Always `refusal`. + x-stainless-const: true + refusal: + type: string + description: The refusal content that is part of a message. + MessageDeltaContentTextAnnotationsFileCitationObject: + title: File citation + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_citation + type: string + description: Always `file_citation`. + x-stainless-const: true + text: + type: string + description: The text in the message content that needs to be replaced. + file_citation: + type: object + properties: + file_id: + type: string + description: The ID of the specific File the citation is from. + quote: + type: string + description: The specific quote in the file. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: "A citation within the message that points to a specific quote from\na specific File associated with the assistant or the message. Generated\nwhen the assistant uses the \"file_search\" tool to search files." + MessageDeltaContentTextAnnotationsFilePathObject: + title: File path + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the annotation in the text content part. + type: + enum: + - file_path + type: string + description: Always `file_path`. + x-stainless-const: true + text: + type: string + description: The text in the message content that needs to be replaced. + file_path: + type: object + properties: + file_id: + type: string + description: The ID of the file that was generated. + start_index: + minimum: 0.0 + type: integer + end_index: + minimum: 0.0 + type: integer + description: "A URL for the file that's generated when the assistant used the\n`code_interpreter` tool to generate a file." + MessageDeltaContentTextObject: + title: Text + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the content part in the message. + type: + enum: + - text + type: string + description: Always `text`. + x-stainless-const: true + text: + type: object + properties: + value: + type: string + description: The data that makes up the text. + annotations: + type: array + items: + oneOf: + - $ref: '#/components/schemas/MessageDeltaContentTextAnnotationsFileCitationObject' + - $ref: '#/components/schemas/MessageDeltaContentTextAnnotationsFilePathObject' + description: The text content that is part of a message. + MessageDeltaObject: + title: Message delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: "The identifier of the message, which can be referenced in API\nendpoints." + object: + enum: + - thread.message.delta + type: string + description: 'The object type, which is always `thread.message.delta`.' + x-stainless-const: true + delta: + type: object + properties: + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/MessageDeltaContentImageFileObject' + - $ref: '#/components/schemas/MessageDeltaContentTextObject' + - $ref: '#/components/schemas/MessageDeltaContentRefusalObject' + - $ref: '#/components/schemas/MessageDeltaContentImageUrlObject' + description: The content of the message in array of text and/or images. + description: The delta containing the fields that have changed on the Message. + description: "Represents a message delta i.e. any changed fields on a message during streaming.\n" + x-oaiMeta: + name: The message delta object + beta: true + example: "{\n \"id\": \"msg_123\",\n \"object\": \"thread.message.delta\",\n \"delta\": {\n \"content\": [\n {\n \"index\": 0,\n \"type\": \"text\",\n \"text\": { \"value\": \"Hello\", \"annotations\": [] }\n }\n ]\n }\n}\n" + MessageObject: + title: The message object + required: + - id + - object + - created_at + - thread_id + - status + - incomplete_details + - completed_at + - incomplete_at + - role + - content + - assistant_id + - run_id + - attachments + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.message + type: string + description: 'The object type, which is always `thread.message`.' + x-stainless-const: true + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was created. + thread_id: + type: string + description: "The [thread](/docs/api-reference/threads) ID that this message\nbelongs to." + status: + enum: + - in_progress + - incomplete + - completed + type: string + description: "The status of the message, which can be either `in_progress`,\n`incomplete`, or `completed`." + incomplete_details: + required: + - reason + type: object + properties: + reason: + enum: + - content_filter + - max_tokens + - run_cancelled + - run_expired + - run_failed + type: string + description: The reason the message is incomplete. + description: "On an incomplete message, details about why the message is\nincomplete." + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the message was completed. + nullable: true + incomplete_at: + type: integer + description: "The Unix timestamp (in seconds) for when the message was marked as\nincomplete." + nullable: true + role: + enum: + - user + - assistant + type: string + description: The entity that produced the message. One of `user` or `assistant`. + content: + type: array + items: + oneOf: + - $ref: '#/components/schemas/MessageContentImageFileObject' + - $ref: '#/components/schemas/MessageContentImageUrlObject' + - $ref: '#/components/schemas/MessageContentTextObject' + - $ref: '#/components/schemas/MessageContentRefusalObject' + description: The content of the message in array of text and/or images. + assistant_id: + type: string + description: "If applicable, the ID of the\n[assistant](/docs/api-reference/assistants) that authored this\nmessage." + nullable: true + run_id: + type: string + description: "The ID of the [run](/docs/api-reference/runs) associated with the\ncreation of this message. Value is `null` when messages are created\nmanually using the create message or create thread endpoints." + nullable: true + attachments: + type: array + items: + type: object + properties: + file_id: + type: string + description: The ID of the file to attach to the message. + tools: + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearchTypeOnly' + description: The tools to add this file to. + description: "A list of files attached to the message, and the tools they were\nadded to." + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + description: 'Represents a message within a [thread](/docs/api-reference/threads).' + x-oaiMeta: + name: The message object + beta: true + example: "{\n \"id\": \"msg_abc123\",\n \"object\": \"thread.message\",\n \"created_at\": 1698983503,\n \"thread_id\": \"thread_abc123\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": {\n \"value\": \"Hi! How can I help you today?\",\n \"annotations\": []\n }\n }\n ],\n \"assistant_id\": \"asst_abc123\",\n \"run_id\": \"run_abc123\",\n \"attachments\": [],\n \"metadata\": {}\n}\n" + MessageRequestContentTextObject: + title: Text + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: Always `text`. + x-stainless-const: true + text: + type: string + description: Text content to be sent to the model + description: The text content that is part of a message. + MessageStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.created + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/MessageObject' + description: "Occurs when a [message](/docs/api-reference/messages/object) is\ncreated." + x-oaiMeta: + dataDescription: '`data` is a [message](/docs/api-reference/messages/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.in_progress + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/MessageObject' + description: "Occurs when a [message](/docs/api-reference/messages/object) moves\nto an `in_progress` state." + x-oaiMeta: + dataDescription: '`data` is a [message](/docs/api-reference/messages/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.delta + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/MessageDeltaObject' + description: "Occurs when parts of a\n[Message](/docs/api-reference/messages/object) are being streamed." + x-oaiMeta: + dataDescription: '`data` is a [message delta](/docs/api-reference/assistants-streaming/message-delta-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.completed + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/MessageObject' + description: "Occurs when a [message](/docs/api-reference/messages/object) is\ncompleted." + x-oaiMeta: + dataDescription: '`data` is a [message](/docs/api-reference/messages/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.message.incomplete + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/MessageObject' + description: "Occurs when a [message](/docs/api-reference/messages/object) ends\nbefore it is completed." + x-oaiMeta: + dataDescription: '`data` is a [message](/docs/api-reference/messages/object)' + Metadata: + type: object + additionalProperties: + type: string + description: "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard.\n\nKeys are strings with a maximum length of 64 characters. Values are strings\nwith a maximum length of 512 characters.\n" + nullable: true + x-oaiTypeLabel: map + Model: + title: Model + required: + - id + - object + - created + - owned_by + properties: + id: + type: string + description: 'The model identifier, which can be referenced in the API endpoints.' + created: + type: integer + description: The Unix timestamp (in seconds) when the model was created. + object: + enum: + - model + type: string + description: 'The object type, which is always "model".' + x-stainless-const: true + owned_by: + type: string + description: The organization that owns the model. + description: Describes an OpenAI model offering that can be used with the API. + x-oaiMeta: + name: The model object + example: "{\n \"id\": \"VAR_chat_model_id\",\n \"object\": \"model\",\n \"created\": 1686935002,\n \"owned_by\": \"openai\"\n}\n" + ModelIds: + anyOf: + - $ref: '#/components/schemas/ModelIdsShared' + - $ref: '#/components/schemas/ModelIdsResponses' + ModelIdsResponses: + anyOf: + - $ref: '#/components/schemas/ModelIdsShared' + - title: ResponsesOnlyModel + enum: + - o1-pro + - o1-pro-2025-03-19 + - computer-use-preview + - computer-use-preview-2025-03-11 + type: string + example: gpt-4o + ModelIdsShared: + anyOf: + - type: string + - enum: + - gpt-4.1 + - gpt-4.1-mini + - gpt-4.1-nano + - gpt-4.1-2025-04-14 + - gpt-4.1-mini-2025-04-14 + - gpt-4.1-nano-2025-04-14 + - o4-mini + - o4-mini-2025-04-16 + - o3 + - o3-2025-04-16 + - o3-mini + - o3-mini-2025-01-31 + - o1 + - o1-2024-12-17 + - o1-preview + - o1-preview-2024-09-12 + - o1-mini + - o1-mini-2024-09-12 + - gpt-4o + - gpt-4o-2024-11-20 + - gpt-4o-2024-08-06 + - gpt-4o-2024-05-13 + - gpt-4o-audio-preview + - gpt-4o-audio-preview-2024-10-01 + - gpt-4o-audio-preview-2024-12-17 + - gpt-4o-mini-audio-preview + - gpt-4o-mini-audio-preview-2024-12-17 + - gpt-4o-search-preview + - gpt-4o-mini-search-preview + - gpt-4o-search-preview-2025-03-11 + - gpt-4o-mini-search-preview-2025-03-11 + - chatgpt-4o-latest + - gpt-4o-mini + - gpt-4o-mini-2024-07-18 + - gpt-4-turbo + - gpt-4-turbo-2024-04-09 + - gpt-4-0125-preview + - gpt-4-turbo-preview + - gpt-4-1106-preview + - gpt-4-vision-preview + - gpt-4 + - gpt-4-0314 + - gpt-4-0613 + - gpt-4-32k + - gpt-4-32k-0314 + - gpt-4-32k-0613 + - gpt-3.5-turbo + - gpt-3.5-turbo-16k + - gpt-3.5-turbo-0301 + - gpt-3.5-turbo-0613 + - gpt-3.5-turbo-1106 + - gpt-3.5-turbo-0125 + - gpt-3.5-turbo-16k-0613 + type: string + example: gpt-4o + ModelResponseProperties: + type: object + properties: + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\nWe generally recommend altering this or `top_p` but not both.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling,\nwhere the model considers the results of the tokens with top_p probability\nmass. So 0.1 means only the tokens comprising the top 10% probability mass\nare considered.\n\nWe generally recommend altering this or `temperature` but not both.\n" + default: 1 + nullable: true + example: 1 + user: + type: string + description: "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. [Learn more](/docs/guides/safety-best-practices#end-user-ids).\n" + example: user-1234 + service_tier: + $ref: '#/components/schemas/ServiceTier' + ModifyAssistantRequest: + type: object + properties: + model: + anyOf: + - type: string + - $ref: '#/components/schemas/AssistantSupportedModels' + description: "ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your available models, or see our [Model overview](/docs/models) for descriptions of them.\n" + reasoning_effort: + $ref: '#/components/schemas/ReasoningEffort' + name: + maxLength: 256 + type: string + description: "The name of the assistant. The maximum length is 256 characters.\n" + nullable: true + description: + maxLength: 512 + type: string + description: "The description of the assistant. The maximum length is 512 characters.\n" + nullable: true + instructions: + maxLength: 256000 + type: string + description: "The system instructions that the assistant uses. The maximum length is 256,000 characters.\n" + nullable: true + tools: + maxItems: 128 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "A list of tool enabled on the assistant. There can be a maximum of 128 tools per assistant. Tools can be of types `code_interpreter`, `file_search`, or `function`.\n" + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "Overrides the list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "Overrides the [vector store](/docs/api-reference/vector-stores/object) attached to this assistant. There can be a maximum of 1 vector store attached to the assistant.\n" + description: "A set of resources that are used by the assistant's tools. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + temperature: + maximum: 2.0 + minimum: 0.0 + type: number + description: "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n" + default: 1 + nullable: true + example: 1 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.\n" + default: 1 + nullable: true + example: 1 + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + additionalProperties: false + ModifyCertificateRequest: + required: + - name + type: object + properties: + name: + type: string + description: The updated name for the certificate + ModifyMessageRequest: + type: object + properties: + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + ModifyRunRequest: + type: object + properties: + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + ModifyThreadRequest: + type: object + properties: + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The [vector store](/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + Move: + title: Move + required: + - type + - x + - y + type: object + properties: + type: + enum: + - move + type: string + description: "Specifies the event type. For a move action, this property is \nalways set to `move`.\n" + default: move + x-stainless-const: true + x: + type: integer + description: "The x-coordinate to move to.\n" + y: + type: integer + description: "The y-coordinate to move to.\n" + description: "A mouse move action.\n" + OpenAIFile: + title: OpenAIFile + required: + - id + - object + - bytes + - created_at + - filename + - purpose + - status + properties: + id: + type: string + description: 'The file identifier, which can be referenced in the API endpoints.' + bytes: + type: integer + description: 'The size of the file, in bytes.' + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the file was created. + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the file will expire. + filename: + type: string + description: The name of the file. + object: + enum: + - file + type: string + description: 'The object type, which is always `file`.' + x-stainless-const: true + purpose: + enum: + - assistants + - assistants_output + - batch + - batch_output + - fine-tune + - fine-tune-results + - vision + type: string + description: "The intended purpose of the file. Supported values are\n`assistants`, `assistants_output`, `batch`, `batch_output`,\n`fine-tune`, `fine-tune-results` and `vision`." + status: + enum: + - uploaded + - processed + - error + type: string + description: "Deprecated. The current status of the file, which can be either\n`uploaded`, `processed`, or `error`." + deprecated: true + status_details: + type: string + description: "Deprecated. For details on why a fine-tuning training file failed\nvalidation, see the `error` field on `fine_tuning.job`." + deprecated: true + description: The `File` object represents a document that has been uploaded to OpenAI. + x-oaiMeta: + name: The file object + example: "{\n \"id\": \"file-abc123\",\n \"object\": \"file\",\n \"bytes\": 120000,\n \"created_at\": 1677610602,\n \"expires_at\": 1680202602,\n \"filename\": \"salesOverview.pdf\",\n \"purpose\": \"assistants\",\n}\n" + OtherChunkingStrategyResponseParam: + title: Other Chunking Strategy + required: + - type + type: object + properties: + type: + enum: + - other + type: string + description: Always `other`. + x-stainless-const: true + additionalProperties: false + description: "This is returned when the chunking strategy is unknown. Typically,\nthis is because the file was indexed before the `chunking_strategy`\nconcept was introduced in the API." + OutputAudio: + title: Output audio + required: + - type + - data + - transcript + type: object + properties: + type: + enum: + - output_audio + type: string + description: "The type of the output audio. Always `output_audio`.\n" + x-stainless-const: true + data: + type: string + description: "Base64-encoded audio data from the model.\n" + transcript: + type: string + description: "The transcript of the audio data from the model.\n" + description: "An audio output from the model.\n" + OutputContent: + oneOf: + - $ref: '#/components/schemas/OutputTextContent' + - $ref: '#/components/schemas/RefusalContent' + OutputItem: + anyOf: + - $ref: '#/components/schemas/OutputMessage' + - $ref: '#/components/schemas/FileSearchToolCall' + - $ref: '#/components/schemas/FunctionToolCall' + - $ref: '#/components/schemas/WebSearchToolCall' + - $ref: '#/components/schemas/ComputerToolCall' + - $ref: '#/components/schemas/ReasoningItem' + discriminator: + propertyName: type + OutputMessage: + title: Output message + required: + - id + - type + - role + - content + - status + type: object + properties: + id: + type: string + description: "The unique ID of the output message.\n" + type: + enum: + - message + type: string + description: "The type of the output message. Always `message`.\n" + x-stainless-const: true + role: + enum: + - assistant + type: string + description: "The role of the output message. Always `assistant`.\n" + x-stainless-const: true + content: + type: array + items: + $ref: '#/components/schemas/OutputContent' + description: "The content of the output message.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the message input. One of `in_progress`, `completed`, or\n`incomplete`. Populated when input items are returned via API.\n" + description: "An output message from the model.\n" + ParallelToolCalls: + type: boolean + description: "Whether to enable [parallel function\ncalling](/docs/guides/function-calling#configuring-parallel-function-calling)\nduring tool use." + default: true + PredictionContent: + title: Static Content + required: + - type + - content + type: object + properties: + type: + enum: + - content + type: string + description: "The type of the predicted content you want to provide. This type is\ncurrently always `content`.\n" + x-stainless-const: true + content: + oneOf: + - title: Text content + type: string + description: "The content used for a Predicted Output. This is often the\ntext of a file you are regenerating with minor changes.\n" + - title: Array of content parts + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionRequestMessageContentPartText' + description: "An array of content parts with a defined type. Supported options\ndiffer based on the [model](/docs/models) being used to generate\nthe response. Can contain text inputs." + description: "The content that should be matched when generating a model response.\nIf generated tokens would match this content, the entire model response\ncan be returned much more quickly.\n" + description: "Static predicted output content, such as the content of a text file that is\nbeing regenerated.\n" + Project: + required: + - id + - object + - name + - created_at + - status + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + object: + enum: + - organization.project + type: string + description: 'The object type, which is always `organization.project`' + x-stainless-const: true + name: + type: string + description: The name of the project. This appears in reporting. + created_at: + type: integer + description: The Unix timestamp (in seconds) of when the project was created. + archived_at: + type: integer + description: "The Unix timestamp (in seconds) of when the project was archived or\n`null`." + nullable: true + status: + enum: + - active + - archived + type: string + description: '`active` or `archived`' + description: Represents an individual project. + x-oaiMeta: + name: The project object + example: "{\n \"id\": \"proj_abc\",\n \"object\": \"organization.project\",\n \"name\": \"Project example\",\n \"created_at\": 1711471533,\n \"archived_at\": null,\n \"status\": \"active\"\n}\n" + ProjectApiKey: + required: + - object + - redacted_value + - name + - created_at + - last_used_at + - id + - owner + type: object + properties: + object: + enum: + - organization.project.api_key + type: string + description: 'The object type, which is always `organization.project.api_key`' + x-stainless-const: true + redacted_value: + type: string + description: The redacted value of the API key + name: + type: string + description: The name of the API key + created_at: + type: integer + description: The Unix timestamp (in seconds) of when the API key was created + last_used_at: + type: integer + description: The Unix timestamp (in seconds) of when the API key was last used. + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + owner: + type: object + properties: + type: + enum: + - user + - service_account + type: string + description: '`user` or `service_account`' + user: + $ref: '#/components/schemas/ProjectUser' + service_account: + $ref: '#/components/schemas/ProjectServiceAccount' + description: Represents an individual API key in a project. + x-oaiMeta: + name: The project API key object + example: "{\n \"object\": \"organization.project.api_key\",\n \"redacted_value\": \"sk-abc...def\",\n \"name\": \"My API Key\",\n \"created_at\": 1711471533,\n \"last_used_at\": 1711471534,\n \"id\": \"key_abc\",\n \"owner\": {\n \"type\": \"user\",\n \"user\": {\n \"object\": \"organization.project.user\",\n \"id\": \"user_abc\",\n \"name\": \"First Last\",\n \"email\": \"user@example.com\",\n \"role\": \"owner\",\n \"created_at\": 1711471533\n }\n }\n}\n" + ProjectApiKeyDeleteResponse: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - organization.project.api_key.deleted + type: string + x-stainless-const: true + id: + type: string + deleted: + type: boolean + ProjectApiKeyListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/ProjectApiKey' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + ProjectCreateRequest: + required: + - name + type: object + properties: + name: + type: string + description: 'The friendly name of the project, this name appears in reports.' + ProjectListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/Project' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + ProjectRateLimit: + required: + - object + - id + - model + - max_requests_per_1_minute + - max_tokens_per_1_minute + type: object + properties: + object: + enum: + - project.rate_limit + type: string + description: 'The object type, which is always `project.rate_limit`' + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + model: + type: string + description: The model this rate limit applies to. + max_requests_per_1_minute: + type: integer + description: The maximum requests per minute. + max_tokens_per_1_minute: + type: integer + description: The maximum tokens per minute. + max_images_per_1_minute: + type: integer + description: The maximum images per minute. Only present for relevant models. + max_audio_megabytes_per_1_minute: + type: integer + description: "The maximum audio megabytes per minute. Only present for relevant\nmodels." + max_requests_per_1_day: + type: integer + description: The maximum requests per day. Only present for relevant models. + batch_1_day_max_input_tokens: + type: integer + description: "The maximum batch input tokens per day. Only present for relevant\nmodels." + description: Represents a project rate limit config. + x-oaiMeta: + name: The project rate limit object + example: "{\n \"object\": \"project.rate_limit\",\n \"id\": \"rl_ada\",\n \"model\": \"ada\",\n \"max_requests_per_1_minute\": 600,\n \"max_tokens_per_1_minute\": 150000,\n \"max_images_per_1_minute\": 10\n}\n" + ProjectRateLimitListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/ProjectRateLimit' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + ProjectRateLimitUpdateRequest: + type: object + properties: + max_requests_per_1_minute: + type: integer + description: The maximum requests per minute. + max_tokens_per_1_minute: + type: integer + description: The maximum tokens per minute. + max_images_per_1_minute: + type: integer + description: The maximum images per minute. Only relevant for certain models. + max_audio_megabytes_per_1_minute: + type: integer + description: "The maximum audio megabytes per minute. Only relevant for certain\nmodels." + max_requests_per_1_day: + type: integer + description: The maximum requests per day. Only relevant for certain models. + batch_1_day_max_input_tokens: + type: integer + description: "The maximum batch input tokens per day. Only relevant for certain\nmodels." + ProjectServiceAccount: + required: + - object + - id + - name + - role + - created_at + type: object + properties: + object: + enum: + - organization.project.service_account + type: string + description: "The object type, which is always\n`organization.project.service_account`" + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + name: + type: string + description: The name of the service account + role: + enum: + - owner + - member + type: string + description: '`owner` or `member`' + created_at: + type: integer + description: "The Unix timestamp (in seconds) of when the service account was\ncreated" + description: Represents an individual service account in a project. + x-oaiMeta: + name: The project service account object + example: "{\n \"object\": \"organization.project.service_account\",\n \"id\": \"svc_acct_abc\",\n \"name\": \"Service Account\",\n \"role\": \"owner\",\n \"created_at\": 1711471533\n}\n" + ProjectServiceAccountApiKey: + required: + - object + - value + - name + - created_at + - id + type: object + properties: + object: + enum: + - organization.project.service_account.api_key + type: string + description: "The object type, which is always\n`organization.project.service_account.api_key`" + x-stainless-const: true + value: + type: string + name: + type: string + created_at: + type: integer + id: + type: string + ProjectServiceAccountCreateRequest: + required: + - name + type: object + properties: + name: + type: string + description: The name of the service account being created. + ProjectServiceAccountCreateResponse: + required: + - object + - id + - name + - role + - created_at + - api_key + type: object + properties: + object: + enum: + - organization.project.service_account + type: string + x-stainless-const: true + id: + type: string + name: + type: string + role: + enum: + - member + type: string + description: Service accounts can only have one role of type `member` + x-stainless-const: true + created_at: + type: integer + api_key: + $ref: '#/components/schemas/ProjectServiceAccountApiKey' + ProjectServiceAccountDeleteResponse: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - organization.project.service_account.deleted + type: string + x-stainless-const: true + id: + type: string + deleted: + type: boolean + ProjectServiceAccountListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/ProjectServiceAccount' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + ProjectUpdateRequest: + required: + - name + type: object + properties: + name: + type: string + description: 'The updated name of the project, this name appears in reports.' + ProjectUser: + required: + - object + - id + - name + - email + - role + - added_at + type: object + properties: + object: + enum: + - organization.project.user + type: string + description: 'The object type, which is always `organization.project.user`' + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + name: + type: string + description: The name of the user + email: + type: string + description: The email address of the user + role: + enum: + - owner + - member + type: string + description: '`owner` or `member`' + added_at: + type: integer + description: The Unix timestamp (in seconds) of when the project was added. + description: Represents an individual user in a project. + x-oaiMeta: + name: The project user object + example: "{\n \"object\": \"organization.project.user\",\n \"id\": \"user_abc\",\n \"name\": \"First Last\",\n \"email\": \"user@example.com\",\n \"role\": \"owner\",\n \"added_at\": 1711471533\n}\n" + ProjectUserCreateRequest: + required: + - user_id + - role + type: object + properties: + user_id: + type: string + description: The ID of the user. + role: + enum: + - owner + - member + type: string + description: '`owner` or `member`' + ProjectUserDeleteResponse: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - organization.project.user.deleted + type: string + x-stainless-const: true + id: + type: string + deleted: + type: boolean + ProjectUserListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + type: string + data: + type: array + items: + $ref: '#/components/schemas/ProjectUser' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + ProjectUserUpdateRequest: + required: + - role + type: object + properties: + role: + enum: + - owner + - member + type: string + description: '`owner` or `member`' + RealtimeClientEvent: + anyOf: + - $ref: '#/components/schemas/RealtimeClientEventConversationItemCreate' + - $ref: '#/components/schemas/RealtimeClientEventConversationItemDelete' + - $ref: '#/components/schemas/RealtimeClientEventConversationItemRetrieve' + - $ref: '#/components/schemas/RealtimeClientEventConversationItemTruncate' + - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferAppend' + - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferClear' + - $ref: '#/components/schemas/RealtimeClientEventOutputAudioBufferClear' + - $ref: '#/components/schemas/RealtimeClientEventInputAudioBufferCommit' + - $ref: '#/components/schemas/RealtimeClientEventResponseCancel' + - $ref: '#/components/schemas/RealtimeClientEventResponseCreate' + - $ref: '#/components/schemas/RealtimeClientEventSessionUpdate' + - $ref: '#/components/schemas/RealtimeClientEventTranscriptionSessionUpdate' + description: "A realtime client event.\n" + discriminator: + propertyName: type + RealtimeClientEventConversationItemCreate: + required: + - type + - item + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - conversation.item.create + type: string + description: 'The event type, must be `conversation.item.create`.' + x-stainless-const: true + previous_item_id: + type: string + description: "The ID of the preceding item after which the new item will be inserted.\nIf not set, the new item will be appended to the end of the conversation.\nIf set to `root`, the new item will be added to the beginning of the conversation.\nIf set to an existing ID, it allows an item to be inserted mid-conversation. If the\nID cannot be found, an error will be returned and the item will not be added.\n" + item: + $ref: '#/components/schemas/RealtimeConversationItem' + description: "Add a new Item to the Conversation's context, including messages, function\ncalls, and function call responses. This event can be used both to populate a\n\"history\" of the conversation and to add new items mid-stream, but has the\ncurrent limitation that it cannot populate assistant audio messages.\n\nIf successful, the server will respond with a `conversation.item.created`\nevent, otherwise an `error` event will be sent.\n" + x-oaiMeta: + name: conversation.item.create + group: realtime + example: "{\n \"event_id\": \"event_345\",\n \"type\": \"conversation.item.create\",\n \"previous_item_id\": null,\n \"item\": {\n \"id\": \"msg_001\",\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_text\",\n \"text\": \"Hello, how are you?\"\n }\n ]\n }\n}\n" + RealtimeClientEventConversationItemDelete: + required: + - type + - item_id + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - conversation.item.delete + type: string + description: 'The event type, must be `conversation.item.delete`.' + x-stainless-const: true + item_id: + type: string + description: The ID of the item to delete. + description: "Send this event when you want to remove any item from the conversation\nhistory. The server will respond with a `conversation.item.deleted` event,\nunless the item does not exist in the conversation history, in which case the\nserver will respond with an error.\n" + x-oaiMeta: + name: conversation.item.delete + group: realtime + example: "{\n \"event_id\": \"event_901\",\n \"type\": \"conversation.item.delete\",\n \"item_id\": \"msg_003\"\n}\n" + RealtimeClientEventConversationItemRetrieve: + required: + - type + - item_id + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - conversation.item.retrieve + type: string + description: 'The event type, must be `conversation.item.retrieve`.' + x-stainless-const: true + item_id: + type: string + description: The ID of the item to retrieve. + description: "Send this event when you want to retrieve the server's representation of a specific item in the conversation history. This is useful, for example, to inspect user audio after noise cancellation and VAD.\nThe server will respond with a `conversation.item.retrieved` event,\nunless the item does not exist in the conversation history, in which case the\nserver will respond with an error.\n" + x-oaiMeta: + name: conversation.item.retrieve + group: realtime + example: "{\n \"event_id\": \"event_901\",\n \"type\": \"conversation.item.retrieve\",\n \"item_id\": \"msg_003\"\n}\n" + RealtimeClientEventConversationItemTruncate: + required: + - type + - item_id + - content_index + - audio_end_ms + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - conversation.item.truncate + type: string + description: 'The event type, must be `conversation.item.truncate`.' + x-stainless-const: true + item_id: + type: string + description: "The ID of the assistant message item to truncate. Only assistant message\nitems can be truncated.\n" + content_index: + type: integer + description: The index of the content part to truncate. Set this to 0. + audio_end_ms: + type: integer + description: "Inclusive duration up to which audio is truncated, in milliseconds. If\nthe audio_end_ms is greater than the actual audio duration, the server\nwill respond with an error.\n" + description: "Send this event to truncate a previous assistant messageΓÇÖs audio. The server\nwill produce audio faster than realtime, so this event is useful when the user\ninterrupts to truncate audio that has already been sent to the client but not\nyet played. This will synchronize the server's understanding of the audio with\nthe client's playback.\n\nTruncating audio will delete the server-side text transcript to ensure there\nis not text in the context that hasn't been heard by the user.\n\nIf successful, the server will respond with a `conversation.item.truncated`\nevent.\n" + x-oaiMeta: + name: conversation.item.truncate + group: realtime + example: "{\n \"event_id\": \"event_678\",\n \"type\": \"conversation.item.truncate\",\n \"item_id\": \"msg_002\",\n \"content_index\": 0,\n \"audio_end_ms\": 1500\n}\n" + RealtimeClientEventInputAudioBufferAppend: + required: + - type + - audio + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - input_audio_buffer.append + type: string + description: 'The event type, must be `input_audio_buffer.append`.' + x-stainless-const: true + audio: + type: string + description: "Base64-encoded audio bytes. This must be in the format specified by the\n`input_audio_format` field in the session configuration.\n" + description: "Send this event to append audio bytes to the input audio buffer. The audio\nbuffer is temporary storage you can write to and later commit. In Server VAD\nmode, the audio buffer is used to detect speech and the server will decide\nwhen to commit. When Server VAD is disabled, you must commit the audio buffer\nmanually.\n\nThe client may choose how much audio to place in each event up to a maximum\nof 15 MiB, for example streaming smaller chunks from the client may allow the\nVAD to be more responsive. Unlike made other client events, the server will\nnot send a confirmation response to this event.\n" + x-oaiMeta: + name: input_audio_buffer.append + group: realtime + example: "{\n \"event_id\": \"event_456\",\n \"type\": \"input_audio_buffer.append\",\n \"audio\": \"Base64EncodedAudioData\"\n}\n" + RealtimeClientEventInputAudioBufferClear: + required: + - type + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - input_audio_buffer.clear + type: string + description: 'The event type, must be `input_audio_buffer.clear`.' + x-stainless-const: true + description: "Send this event to clear the audio bytes in the buffer. The server will \nrespond with an `input_audio_buffer.cleared` event.\n" + x-oaiMeta: + name: input_audio_buffer.clear + group: realtime + example: "{\n \"event_id\": \"event_012\",\n \"type\": \"input_audio_buffer.clear\"\n}\n" + RealtimeClientEventInputAudioBufferCommit: + required: + - type + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - input_audio_buffer.commit + type: string + description: 'The event type, must be `input_audio_buffer.commit`.' + x-stainless-const: true + description: "Send this event to commit the user input audio buffer, which will create a\nnew user message item in the conversation. This event will produce an error\nif the input audio buffer is empty. When in Server VAD mode, the client does\nnot need to send this event, the server will commit the audio buffer\nautomatically.\n\nCommitting the input audio buffer will trigger input audio transcription\n(if enabled in session configuration), but it will not create a response\nfrom the model. The server will respond with an `input_audio_buffer.committed`\nevent.\n" + x-oaiMeta: + name: input_audio_buffer.commit + group: realtime + example: "{\n \"event_id\": \"event_789\",\n \"type\": \"input_audio_buffer.commit\"\n}\n" + RealtimeClientEventOutputAudioBufferClear: + required: + - type + type: object + properties: + event_id: + type: string + description: The unique ID of the client event used for error handling. + type: + enum: + - output_audio_buffer.clear + type: string + description: 'The event type, must be `output_audio_buffer.clear`.' + x-stainless-const: true + description: "**WebRTC Only:** Emit to cut off the current audio response. This will trigger the server to\nstop generating audio and emit a `output_audio_buffer.cleared` event. This \nevent should be preceded by a `response.cancel` client event to stop the \ngeneration of the current response.\n[Learn more](/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc).\n" + x-oaiMeta: + name: output_audio_buffer.clear + group: realtime + example: "{\n \"event_id\": \"optional_client_event_id\",\n \"type\": \"output_audio_buffer.clear\"\n}\n" + RealtimeClientEventResponseCancel: + required: + - type + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - response.cancel + type: string + description: 'The event type, must be `response.cancel`.' + x-stainless-const: true + response_id: + type: string + description: "A specific response ID to cancel - if not provided, will cancel an \nin-progress response in the default conversation.\n" + description: "Send this event to cancel an in-progress response. The server will respond\nwith a `response.cancelled` event or an error if there is no response to\ncancel.\n" + x-oaiMeta: + name: response.cancel + group: realtime + example: "{\n \"event_id\": \"event_567\",\n \"type\": \"response.cancel\"\n}\n" + RealtimeClientEventResponseCreate: + required: + - type + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - response.create + type: string + description: 'The event type, must be `response.create`.' + x-stainless-const: true + response: + $ref: '#/components/schemas/RealtimeResponseCreateParams' + description: "This event instructs the server to create a Response, which means triggering\nmodel inference. When in Server VAD mode, the server will create Responses\nautomatically.\n\nA Response will include at least one Item, and may have two, in which case\nthe second will be a function call. These Items will be appended to the\nconversation history.\n\nThe server will respond with a `response.created` event, events for Items\nand content created, and finally a `response.done` event to indicate the\nResponse is complete.\n\nThe `response.create` event includes inference configuration like\n`instructions`, and `temperature`. These fields will override the Session's\nconfiguration for this Response only.\n" + x-oaiMeta: + name: response.create + group: realtime + example: "{\n \"event_id\": \"event_234\",\n \"type\": \"response.create\",\n \"response\": {\n \"modalities\": [\"text\", \"audio\"],\n \"instructions\": \"Please assist the user.\",\n \"voice\": \"sage\",\n \"output_audio_format\": \"pcm16\",\n \"tools\": [\n {\n \"type\": \"function\",\n \"name\": \"calculate_sum\",\n \"description\": \"Calculates the sum of two numbers.\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"a\": { \"type\": \"number\" },\n \"b\": { \"type\": \"number\" }\n },\n \"required\": [\"a\", \"b\"]\n }\n }\n ],\n \"tool_choice\": \"auto\",\n \"temperature\": 0.8,\n \"max_output_tokens\": 1024\n }\n}\n" + RealtimeClientEventSessionUpdate: + required: + - type + - session + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - session.update + type: string + description: 'The event type, must be `session.update`.' + x-stainless-const: true + session: + $ref: '#/components/schemas/RealtimeSessionCreateRequest' + description: "Send this event to update the sessionΓÇÖs default configuration.\nThe client may send this event at any time to update any field,\nexcept for `voice`. However, note that once a session has been\ninitialized with a particular `model`, it canΓÇÖt be changed to\nanother model using `session.update`.\n\nWhen the server receives a `session.update`, it will respond\nwith a `session.updated` event showing the full, effective configuration.\nOnly the fields that are present are updated. To clear a field like\n`instructions`, pass an empty string.\n" + x-oaiMeta: + name: session.update + group: realtime + example: "{\n \"event_id\": \"event_123\",\n \"type\": \"session.update\",\n \"session\": {\n \"modalities\": [\"text\", \"audio\"],\n \"instructions\": \"You are a helpful assistant.\",\n \"voice\": \"sage\",\n \"input_audio_format\": \"pcm16\",\n \"output_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"whisper-1\"\n },\n \"turn_detection\": {\n \"type\": \"server_vad\",\n \"threshold\": 0.5,\n \"prefix_padding_ms\": 300,\n \"silence_duration_ms\": 500,\n \"create_response\": true\n },\n \"tools\": [\n {\n \"type\": \"function\",\n \"name\": \"get_weather\",\n \"description\": \"Get the current weather...\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": { \"type\": \"string\" }\n },\n \"required\": [\"location\"]\n }\n }\n ],\n \"tool_choice\": \"auto\",\n \"temperature\": 0.8,\n \"max_response_output_tokens\": \"inf\"\n }\n}\n" + RealtimeClientEventTranscriptionSessionUpdate: + required: + - type + - session + type: object + properties: + event_id: + type: string + description: Optional client-generated ID used to identify this event. + type: + enum: + - transcription_session.update + type: string + description: 'The event type, must be `transcription_session.update`.' + x-stainless-const: true + session: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateRequest' + description: "Send this event to update a transcription session.\n" + x-oaiMeta: + name: transcription_session.update + group: realtime + example: "{\n \"type\": \"transcription_session.update\",\n \"session\": {\n \"input_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"gpt-4o-transcribe\",\n \"prompt\": \"\",\n \"language\": \"\"\n },\n \"turn_detection\": {\n \"type\": \"server_vad\",\n \"threshold\": 0.5,\n \"prefix_padding_ms\": 300,\n \"silence_duration_ms\": 500,\n \"create_response\": true,\n },\n \"input_audio_noise_reduction\": {\n \"type\": \"near_field\"\n },\n \"include\": [\n \"item.input_audio_transcription.logprobs\",\n ]\n }\n}\n" + RealtimeConversationItem: + type: object + properties: + id: + type: string + description: "The unique ID of the item, this can be generated by the client to help\nmanage server-side context, but is not required because the server will\ngenerate one if not provided.\n" + type: + enum: + - message + - function_call + - function_call_output + type: string + description: "The type of the item (`message`, `function_call`, `function_call_output`).\n" + object: + enum: + - realtime.item + type: string + description: "Identifier for the API object being returned - always `realtime.item`.\n" + x-stainless-const: true + status: + enum: + - completed + - incomplete + type: string + description: "The status of the item (`completed`, `incomplete`). These have no effect\non the conversation, but are accepted for consistency with the\n`conversation.item.created` event.\n" + role: + enum: + - user + - assistant + - system + type: string + description: "The role of the message sender (`user`, `assistant`, `system`), only\napplicable for `message` items.\n" + content: + type: array + items: + type: object + properties: + type: + enum: + - input_audio + - input_text + - item_reference + - text + type: string + description: "The content type (`input_text`, `input_audio`, `item_reference`, `text`).\n" + text: + type: string + description: "The text content, used for `input_text` and `text` content types.\n" + id: + type: string + description: "ID of a previous conversation item to reference (for `item_reference`\ncontent types in `response.create` events). These can reference both\nclient and server created items.\n" + audio: + type: string + description: "Base64-encoded audio bytes, used for `input_audio` content type.\n" + transcript: + type: string + description: "The transcript of the audio, used for `input_audio` content type.\n" + description: "The content of the message, applicable for `message` items.\n- Message items of role `system` support only `input_text` content\n- Message items of role `user` support `input_text` and `input_audio` content\n- Message items of role `assistant` support `text` content.\n" + call_id: + type: string + description: "The ID of the function call (for `function_call` and\n`function_call_output` items). If passed on a `function_call_output`\nitem, the server will check that a `function_call` item with the same\nID exists in the conversation history.\n" + name: + type: string + description: "The name of the function being called (for `function_call` items).\n" + arguments: + type: string + description: "The arguments of the function call (for `function_call` items).\n" + output: + type: string + description: "The output of the function call (for `function_call_output` items).\n" + description: The item to add to the conversation. + RealtimeConversationItemWithReference: + type: object + properties: + id: + type: string + description: "For an item of type (`message` | `function_call` | `function_call_output`)\nthis field allows the client to assign the unique ID of the item. It is\nnot required because the server will generate one if not provided.\n\nFor an item of type `item_reference`, this field is required and is a\nreference to any item that has previously existed in the conversation.\n" + type: + enum: + - message + - function_call + - function_call_output + type: string + description: "The type of the item (`message`, `function_call`, `function_call_output`, `item_reference`).\n" + object: + enum: + - realtime.item + type: string + description: "Identifier for the API object being returned - always `realtime.item`.\n" + x-stainless-const: true + status: + enum: + - completed + - incomplete + type: string + description: "The status of the item (`completed`, `incomplete`). These have no effect\non the conversation, but are accepted for consistency with the\n`conversation.item.created` event.\n" + role: + enum: + - user + - assistant + - system + type: string + description: "The role of the message sender (`user`, `assistant`, `system`), only\napplicable for `message` items.\n" + content: + type: array + items: + type: object + properties: + type: + enum: + - input_audio + - input_text + - item_reference + - text + type: string + description: "The content type (`input_text`, `input_audio`, `item_reference`, `text`).\n" + text: + type: string + description: "The text content, used for `input_text` and `text` content types.\n" + id: + type: string + description: "ID of a previous conversation item to reference (for `item_reference`\ncontent types in `response.create` events). These can reference both\nclient and server created items.\n" + audio: + type: string + description: "Base64-encoded audio bytes, used for `input_audio` content type.\n" + transcript: + type: string + description: "The transcript of the audio, used for `input_audio` content type.\n" + description: "The content of the message, applicable for `message` items.\n- Message items of role `system` support only `input_text` content\n- Message items of role `user` support `input_text` and `input_audio` content\n- Message items of role `assistant` support `text` content.\n" + call_id: + type: string + description: "The ID of the function call (for `function_call` and\n`function_call_output` items). If passed on a `function_call_output`\nitem, the server will check that a `function_call` item with the same\nID exists in the conversation history.\n" + name: + type: string + description: "The name of the function being called (for `function_call` items).\n" + arguments: + type: string + description: "The arguments of the function call (for `function_call` items).\n" + output: + type: string + description: "The output of the function call (for `function_call_output` items).\n" + description: The item to add to the conversation. + RealtimeResponse: + type: object + properties: + id: + type: string + description: The unique ID of the response. + object: + enum: + - realtime.response + type: string + description: 'The object type, must be `realtime.response`.' + x-stainless-const: true + status: + enum: + - completed + - cancelled + - failed + - incomplete + type: string + description: "The final status of the response (`completed`, `cancelled`, `failed`, or\n`incomplete`).\n" + status_details: + type: object + properties: + type: + enum: + - completed + - cancelled + - failed + - incomplete + type: string + description: "The type of error that caused the response to fail, corresponding\nwith the `status` field (`completed`, `cancelled`, `incomplete`,\n`failed`).\n" + reason: + enum: + - turn_detected + - client_cancelled + - max_output_tokens + - content_filter + type: string + description: "The reason the Response did not complete. For a `cancelled` Response,\none of `turn_detected` (the server VAD detected a new start of speech)\nor `client_cancelled` (the client sent a cancel event). For an\n`incomplete` Response, one of `max_output_tokens` or `content_filter`\n(the server-side safety filter activated and cut off the response).\n" + error: + type: object + properties: + type: + type: string + description: The type of error. + code: + type: string + description: 'Error code, if any.' + description: "A description of the error that caused the response to fail, \npopulated when the `status` is `failed`.\n" + description: Additional details about the status. + output: + type: array + items: + $ref: '#/components/schemas/RealtimeConversationItem' + description: The list of output items generated by the response. + metadata: + $ref: '#/components/schemas/Metadata' + usage: + type: object + properties: + total_tokens: + type: integer + description: "The total number of tokens in the Response including input and output\ntext and audio tokens.\n" + input_tokens: + type: integer + description: "The number of input tokens used in the Response, including text and\naudio tokens.\n" + output_tokens: + type: integer + description: "The number of output tokens sent in the Response, including text and\naudio tokens.\n" + input_token_details: + type: object + properties: + cached_tokens: + type: integer + description: The number of cached tokens used in the Response. + text_tokens: + type: integer + description: The number of text tokens used in the Response. + audio_tokens: + type: integer + description: The number of audio tokens used in the Response. + description: Details about the input tokens used in the Response. + output_token_details: + type: object + properties: + text_tokens: + type: integer + description: The number of text tokens used in the Response. + audio_tokens: + type: integer + description: The number of audio tokens used in the Response. + description: Details about the output tokens used in the Response. + description: "Usage statistics for the Response, this will correspond to billing. A\nRealtime API session will maintain a conversation context and append new\nItems to the Conversation, thus output from previous turns (text and\naudio tokens) will become the input for later turns.\n" + conversation_id: + type: string + description: "Which conversation the response is added to, determined by the `conversation`\nfield in the `response.create` event. If `auto`, the response will be added to\nthe default conversation and the value of `conversation_id` will be an id like\n`conv_1234`. If `none`, the response will not be added to any conversation and\nthe value of `conversation_id` will be `null`. If responses are being triggered\nby server VAD, the response will be added to the default conversation, thus\nthe `conversation_id` will be an id like `conv_1234`.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + modalities: + type: array + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model used to respond. If there are multiple modalities,\nthe model will pick one, for example if `modalities` is `[\"text\", \"audio\"]`, the model\ncould be responding in either text or audio.\n" + output_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.\n" + max_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls, that was used in this response.\n" + description: The response resource. + RealtimeResponseCreateParams: + type: object + properties: + modalities: + type: array + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model\ncalls. This field allows the client to guide the model on desired\nresponses. The model can be instructed on response content and format,\n(e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good\nresponses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion\ninto your voice\", \"laugh frequently\"). The instructions are not guaranteed\nto be followed by the model, but they provide guidance to the model on the\ndesired behavior.\n\nNote that the server sets default instructions which will be used if this\nfield is not set and are visible in the `session.created` event at the\nstart of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + output_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function, like `{\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}`.\n" + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.\n" + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + conversation: + oneOf: + - type: string + - enum: + - auto + - none + type: string + default: auto + description: "Controls which conversation the response is added to. Currently supports\n`auto` and `none`, with `auto` as the default value. The `auto` value\nmeans that the contents of the response will be added to the default\nconversation. Set this to `none` to create an out-of-band response which\nwill not add items to default conversation.\n" + metadata: + $ref: '#/components/schemas/Metadata' + input: + type: array + items: + $ref: '#/components/schemas/RealtimeConversationItemWithReference' + description: "Input items to include in the prompt for the model. Using this field\ncreates a new context for this Response instead of using the default\nconversation. An empty array `[]` will clear the context for this Response.\nNote that this can include references to items from the default conversation.\n" + description: Create a new Realtime response with these parameters + RealtimeServerEvent: + anyOf: + - $ref: '#/components/schemas/RealtimeServerEventConversationCreated' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemCreated' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemDeleted' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionCompleted' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionDelta' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemInputAudioTranscriptionFailed' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemRetrieved' + - $ref: '#/components/schemas/RealtimeServerEventConversationItemTruncated' + - $ref: '#/components/schemas/RealtimeServerEventError' + - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCleared' + - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferCommitted' + - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStarted' + - $ref: '#/components/schemas/RealtimeServerEventInputAudioBufferSpeechStopped' + - $ref: '#/components/schemas/RealtimeServerEventRateLimitsUpdated' + - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDelta' + - $ref: '#/components/schemas/RealtimeServerEventResponseAudioDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDelta' + - $ref: '#/components/schemas/RealtimeServerEventResponseAudioTranscriptDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartAdded' + - $ref: '#/components/schemas/RealtimeServerEventResponseContentPartDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseCreated' + - $ref: '#/components/schemas/RealtimeServerEventResponseDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDelta' + - $ref: '#/components/schemas/RealtimeServerEventResponseFunctionCallArgumentsDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemAdded' + - $ref: '#/components/schemas/RealtimeServerEventResponseOutputItemDone' + - $ref: '#/components/schemas/RealtimeServerEventResponseTextDelta' + - $ref: '#/components/schemas/RealtimeServerEventResponseTextDone' + - $ref: '#/components/schemas/RealtimeServerEventSessionCreated' + - $ref: '#/components/schemas/RealtimeServerEventSessionUpdated' + - $ref: '#/components/schemas/RealtimeServerEventTranscriptionSessionUpdated' + - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStarted' + - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferStopped' + - $ref: '#/components/schemas/RealtimeServerEventOutputAudioBufferCleared' + description: "A realtime server event.\n" + discriminator: + propertyName: type + RealtimeServerEventConversationCreated: + required: + - event_id + - type + - conversation + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.created + type: string + description: 'The event type, must be `conversation.created`.' + x-stainless-const: true + conversation: + type: object + properties: + id: + type: string + description: The unique ID of the conversation. + object: + type: string + description: 'The object type, must be `realtime.conversation`.' + description: The conversation resource. + description: "Returned when a conversation is created. Emitted right after session creation.\n" + x-oaiMeta: + name: conversation.created + group: realtime + example: "{\n \"event_id\": \"event_9101\",\n \"type\": \"conversation.created\",\n \"conversation\": {\n \"id\": \"conv_001\",\n \"object\": \"realtime.conversation\"\n }\n}\n" + RealtimeServerEventConversationItemCreated: + required: + - event_id + - type + - previous_item_id + - item + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.created + type: string + description: 'The event type, must be `conversation.item.created`.' + x-stainless-const: true + previous_item_id: + type: string + description: "The ID of the preceding item in the Conversation context, allows the\nclient to understand the order of the conversation.\n" + item: + $ref: '#/components/schemas/RealtimeConversationItem' + description: "Returned when a conversation item is created. There are several scenarios that produce this event: - The server is generating a Response, which if successful will produce \n either one or two Items, which will be of type `message` \n (role `assistant`) or type `function_call`.\n - The input audio buffer has been committed, either by the client or the \n server (in `server_vad` mode). The server will take the content of the \n input audio buffer and add it to a new user message Item.\n - The client has sent a `conversation.item.create` event to add a new Item \n to the Conversation.\n" + x-oaiMeta: + name: conversation.item.created + group: realtime + example: "{\n \"event_id\": \"event_1920\",\n \"type\": \"conversation.item.created\",\n \"previous_item_id\": \"msg_002\",\n \"item\": {\n \"id\": \"msg_003\",\n \"object\": \"realtime.item\",\n \"type\": \"message\",\n \"status\": \"completed\",\n \"role\": \"user\",\n \"content\": []\n }\n}\n" + RealtimeServerEventConversationItemDeleted: + required: + - event_id + - type + - item_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.deleted + type: string + description: 'The event type, must be `conversation.item.deleted`.' + x-stainless-const: true + item_id: + type: string + description: The ID of the item that was deleted. + description: "Returned when an item in the conversation is deleted by the client with a\n`conversation.item.delete` event. This event is used to synchronize the\nserver's understanding of the conversation history with the client's view.\n" + x-oaiMeta: + name: conversation.item.deleted + group: realtime + example: "{\n \"event_id\": \"event_2728\",\n \"type\": \"conversation.item.deleted\",\n \"item_id\": \"msg_005\"\n}\n" + RealtimeServerEventConversationItemInputAudioTranscriptionCompleted: + required: + - event_id + - type + - item_id + - content_index + - transcript + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.input_audio_transcription.completed + type: string + description: "The event type, must be\n`conversation.item.input_audio_transcription.completed`.\n" + x-stainless-const: true + item_id: + type: string + description: The ID of the user message item containing the audio. + content_index: + type: integer + description: The index of the content part containing the audio. + transcript: + type: string + description: The transcribed text. + logprobs: + type: array + items: + $ref: '#/components/schemas/LogProbProperties' + description: The log probabilities of the transcription. + nullable: true + description: "This event is the output of audio transcription for user audio written to the\nuser audio buffer. Transcription begins when the input audio buffer is\ncommitted by the client or server (in `server_vad` mode). Transcription runs\nasynchronously with Response creation, so this event may come before or after\nthe Response events.\n\nRealtime API models accept audio natively, and thus input transcription is a\nseparate process run on a separate ASR (Automatic Speech Recognition) model,\ncurrently always `whisper-1`. Thus the transcript may diverge somewhat from\nthe model's interpretation, and should be treated as a rough guide.\n" + x-oaiMeta: + name: conversation.item.input_audio_transcription.completed + group: realtime + example: "{\n \"event_id\": \"event_2122\",\n \"type\": \"conversation.item.input_audio_transcription.completed\",\n \"item_id\": \"msg_003\",\n \"content_index\": 0,\n \"transcript\": \"Hello, how are you?\"\n}\n" + RealtimeServerEventConversationItemInputAudioTranscriptionDelta: + required: + - event_id + - type + - item_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.input_audio_transcription.delta + type: string + description: "The event type, must be\n`conversation.item.input_audio_transcription.delta`." + x-stainless-const: true + item_id: + type: string + description: The ID of the item. + content_index: + type: integer + description: The index of the content part in the item's content array. + delta: + type: string + description: The text delta. + logprobs: + type: array + items: + $ref: '#/components/schemas/LogProbProperties' + description: The log probabilities of the transcription. + nullable: true + description: "Returned when the text value of an input audio transcription content part is updated.\n" + x-oaiMeta: + name: conversation.item.input_audio_transcription.delta + group: realtime + example: "{\n \"type\": \"conversation.item.input_audio_transcription.delta\",\n \"event_id\": \"event_001\",\n \"item_id\": \"item_001\",\n \"content_index\": 0,\n \"delta\": \"Hello\"\n}\n" + RealtimeServerEventConversationItemInputAudioTranscriptionFailed: + required: + - event_id + - type + - item_id + - content_index + - error + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.input_audio_transcription.failed + type: string + description: "The event type, must be\n`conversation.item.input_audio_transcription.failed`.\n" + x-stainless-const: true + item_id: + type: string + description: The ID of the user message item. + content_index: + type: integer + description: The index of the content part containing the audio. + error: + type: object + properties: + type: + type: string + description: The type of error. + code: + type: string + description: 'Error code, if any.' + message: + type: string + description: A human-readable error message. + param: + type: string + description: 'Parameter related to the error, if any.' + description: Details of the transcription error. + description: "Returned when input audio transcription is configured, and a transcription\nrequest for a user message failed. These events are separate from other\n`error` events so that the client can identify the related Item.\n" + x-oaiMeta: + name: conversation.item.input_audio_transcription.failed + group: realtime + example: "{\n \"event_id\": \"event_2324\",\n \"type\": \"conversation.item.input_audio_transcription.failed\",\n \"item_id\": \"msg_003\",\n \"content_index\": 0,\n \"error\": {\n \"type\": \"transcription_error\",\n \"code\": \"audio_unintelligible\",\n \"message\": \"The audio could not be transcribed.\",\n \"param\": null\n }\n}\n" + RealtimeServerEventConversationItemRetrieved: + required: + - event_id + - type + - item + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.retrieved + type: string + description: 'The event type, must be `conversation.item.retrieved`.' + x-stainless-const: true + item: + $ref: '#/components/schemas/RealtimeConversationItem' + description: "Returned when a conversation item is retrieved with `conversation.item.retrieve`.\n" + x-oaiMeta: + name: conversation.item.retrieved + group: realtime + example: "{\n \"event_id\": \"event_1920\",\n \"type\": \"conversation.item.created\",\n \"previous_item_id\": \"msg_002\",\n \"item\": {\n \"id\": \"msg_003\",\n \"object\": \"realtime.item\",\n \"type\": \"message\",\n \"status\": \"completed\",\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_audio\",\n \"transcript\": \"hello how are you\",\n \"audio\": \"base64encodedaudio==\"\n }\n ]\n }\n}\n" + RealtimeServerEventConversationItemTruncated: + required: + - event_id + - type + - item_id + - content_index + - audio_end_ms + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - conversation.item.truncated + type: string + description: 'The event type, must be `conversation.item.truncated`.' + x-stainless-const: true + item_id: + type: string + description: The ID of the assistant message item that was truncated. + content_index: + type: integer + description: The index of the content part that was truncated. + audio_end_ms: + type: integer + description: "The duration up to which the audio was truncated, in milliseconds.\n" + description: "Returned when an earlier assistant audio message item is truncated by the\nclient with a `conversation.item.truncate` event. This event is used to\nsynchronize the server's understanding of the audio with the client's playback.\n\nThis action will truncate the audio and remove the server-side text transcript\nto ensure there is no text in the context that hasn't been heard by the user.\n" + x-oaiMeta: + name: conversation.item.truncated + group: realtime + example: "{\n \"event_id\": \"event_2526\",\n \"type\": \"conversation.item.truncated\",\n \"item_id\": \"msg_004\",\n \"content_index\": 0,\n \"audio_end_ms\": 1500\n}\n" + RealtimeServerEventError: + required: + - event_id + - type + - error + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - error + type: string + description: 'The event type, must be `error`.' + x-stainless-const: true + error: + required: + - type + - message + type: object + properties: + type: + type: string + description: "The type of error (e.g., \"invalid_request_error\", \"server_error\").\n" + code: + type: string + description: 'Error code, if any.' + nullable: true + message: + type: string + description: A human-readable error message. + param: + type: string + description: 'Parameter related to the error, if any.' + nullable: true + event_id: + type: string + description: "The event_id of the client event that caused the error, if applicable.\n" + nullable: true + description: Details of the error. + description: "Returned when an error occurs, which could be a client problem or a server\nproblem. Most errors are recoverable and the session will stay open, we\nrecommend to implementors to monitor and log error messages by default.\n" + x-oaiMeta: + name: error + group: realtime + example: "{\n \"event_id\": \"event_890\",\n \"type\": \"error\",\n \"error\": {\n \"type\": \"invalid_request_error\",\n \"code\": \"invalid_event\",\n \"message\": \"The 'type' field is missing.\",\n \"param\": null,\n \"event_id\": \"event_567\"\n }\n}\n" + RealtimeServerEventInputAudioBufferCleared: + required: + - event_id + - type + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - input_audio_buffer.cleared + type: string + description: 'The event type, must be `input_audio_buffer.cleared`.' + x-stainless-const: true + description: "Returned when the input audio buffer is cleared by the client with a \n`input_audio_buffer.clear` event.\n" + x-oaiMeta: + name: input_audio_buffer.cleared + group: realtime + example: "{\n \"event_id\": \"event_1314\",\n \"type\": \"input_audio_buffer.cleared\"\n}\n" + RealtimeServerEventInputAudioBufferCommitted: + required: + - event_id + - type + - previous_item_id + - item_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - input_audio_buffer.committed + type: string + description: 'The event type, must be `input_audio_buffer.committed`.' + x-stainless-const: true + previous_item_id: + type: string + description: "The ID of the preceding item after which the new item will be inserted.\n" + item_id: + type: string + description: The ID of the user message item that will be created. + description: "Returned when an input audio buffer is committed, either by the client or\nautomatically in server VAD mode. The `item_id` property is the ID of the user\nmessage item that will be created, thus a `conversation.item.created` event\nwill also be sent to the client.\n" + x-oaiMeta: + name: input_audio_buffer.committed + group: realtime + example: "{\n \"event_id\": \"event_1121\",\n \"type\": \"input_audio_buffer.committed\",\n \"previous_item_id\": \"msg_001\",\n \"item_id\": \"msg_002\"\n}\n" + RealtimeServerEventInputAudioBufferSpeechStarted: + required: + - event_id + - type + - audio_start_ms + - item_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - input_audio_buffer.speech_started + type: string + description: 'The event type, must be `input_audio_buffer.speech_started`.' + x-stainless-const: true + audio_start_ms: + type: integer + description: "Milliseconds from the start of all audio written to the buffer during the\nsession when speech was first detected. This will correspond to the\nbeginning of audio sent to the model, and thus includes the\n`prefix_padding_ms` configured in the Session.\n" + item_id: + type: string + description: "The ID of the user message item that will be created when speech stops.\n" + description: "Sent by the server when in `server_vad` mode to indicate that speech has been\ndetected in the audio buffer. This can happen any time audio is added to the\nbuffer (unless speech is already detected). The client may want to use this\nevent to interrupt audio playback or provide visual feedback to the user.\n\nThe client should expect to receive a `input_audio_buffer.speech_stopped` event\nwhen speech stops. The `item_id` property is the ID of the user message item\nthat will be created when speech stops and will also be included in the\n`input_audio_buffer.speech_stopped` event (unless the client manually commits\nthe audio buffer during VAD activation).\n" + x-oaiMeta: + name: input_audio_buffer.speech_started + group: realtime + example: "{\n \"event_id\": \"event_1516\",\n \"type\": \"input_audio_buffer.speech_started\",\n \"audio_start_ms\": 1000,\n \"item_id\": \"msg_003\"\n}\n" + RealtimeServerEventInputAudioBufferSpeechStopped: + required: + - event_id + - type + - audio_end_ms + - item_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - input_audio_buffer.speech_stopped + type: string + description: 'The event type, must be `input_audio_buffer.speech_stopped`.' + x-stainless-const: true + audio_end_ms: + type: integer + description: "Milliseconds since the session started when speech stopped. This will\ncorrespond to the end of audio sent to the model, and thus includes the\n`min_silence_duration_ms` configured in the Session.\n" + item_id: + type: string + description: The ID of the user message item that will be created. + description: "Returned in `server_vad` mode when the server detects the end of speech in\nthe audio buffer. The server will also send an `conversation.item.created`\nevent with the user message item that is created from the audio buffer.\n" + x-oaiMeta: + name: input_audio_buffer.speech_stopped + group: realtime + example: "{\n \"event_id\": \"event_1718\",\n \"type\": \"input_audio_buffer.speech_stopped\",\n \"audio_end_ms\": 2000,\n \"item_id\": \"msg_003\"\n}\n" + RealtimeServerEventOutputAudioBufferCleared: + required: + - event_id + - type + - response_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - output_audio_buffer.cleared + type: string + description: 'The event type, must be `output_audio_buffer.cleared`.' + x-stainless-const: true + response_id: + type: string + description: The unique ID of the response that produced the audio. + description: "**WebRTC Only:** Emitted when the output audio buffer is cleared. This happens either in VAD\nmode when the user has interrupted (`input_audio_buffer.speech_started`),\nor when the client has emitted the `output_audio_buffer.clear` event to manually\ncut off the current audio response.\n[Learn more](/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc).\n" + x-oaiMeta: + name: output_audio_buffer.cleared + group: realtime + example: "{\n \"event_id\": \"event_abc123\",\n \"type\": \"output_audio_buffer.cleared\",\n \"response_id\": \"resp_abc123\"\n}\n" + RealtimeServerEventOutputAudioBufferStarted: + required: + - event_id + - type + - response_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - output_audio_buffer.started + type: string + description: 'The event type, must be `output_audio_buffer.started`.' + x-stainless-const: true + response_id: + type: string + description: The unique ID of the response that produced the audio. + description: "**WebRTC Only:** Emitted when the server begins streaming audio to the client. This event is\nemitted after an audio content part has been added (`response.content_part.added`)\nto the response.\n[Learn more](/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc).\n" + x-oaiMeta: + name: output_audio_buffer.started + group: realtime + example: "{\n \"event_id\": \"event_abc123\",\n \"type\": \"output_audio_buffer.started\",\n \"response_id\": \"resp_abc123\"\n}\n" + RealtimeServerEventOutputAudioBufferStopped: + required: + - event_id + - type + - response_id + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - output_audio_buffer.stopped + type: string + description: 'The event type, must be `output_audio_buffer.stopped`.' + x-stainless-const: true + response_id: + type: string + description: The unique ID of the response that produced the audio. + description: "**WebRTC Only:** Emitted when the output audio buffer has been completely drained on the server,\nand no more audio is forthcoming. This event is emitted after the full response\ndata has been sent to the client (`response.done`).\n[Learn more](/docs/guides/realtime-model-capabilities#client-and-server-events-for-audio-in-webrtc).\n" + x-oaiMeta: + name: output_audio_buffer.stopped + group: realtime + example: "{\n \"event_id\": \"event_abc123\",\n \"type\": \"output_audio_buffer.stopped\",\n \"response_id\": \"resp_abc123\"\n}\n" + RealtimeServerEventRateLimitsUpdated: + required: + - event_id + - type + - rate_limits + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - rate_limits.updated + type: string + description: 'The event type, must be `rate_limits.updated`.' + x-stainless-const: true + rate_limits: + type: array + items: + type: object + properties: + name: + enum: + - requests + - tokens + type: string + description: "The name of the rate limit (`requests`, `tokens`).\n" + limit: + type: integer + description: The maximum allowed value for the rate limit. + remaining: + type: integer + description: The remaining value before the limit is reached. + reset_seconds: + type: number + description: Seconds until the rate limit resets. + description: List of rate limit information. + description: "Emitted at the beginning of a Response to indicate the updated rate limits.\nWhen a Response is created some tokens will be \"reserved\" for the output\ntokens, the rate limits shown here reflect that reservation, which is then\nadjusted accordingly once the Response is completed.\n" + x-oaiMeta: + name: rate_limits.updated + group: realtime + example: "{\n \"event_id\": \"event_5758\",\n \"type\": \"rate_limits.updated\",\n \"rate_limits\": [\n {\n \"name\": \"requests\",\n \"limit\": 1000,\n \"remaining\": 999,\n \"reset_seconds\": 60\n },\n {\n \"name\": \"tokens\",\n \"limit\": 50000,\n \"remaining\": 49950,\n \"reset_seconds\": 60\n }\n ]\n}\n" + RealtimeServerEventResponseAudioDelta: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - delta + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.audio.delta + type: string + description: 'The event type, must be `response.audio.delta`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + delta: + type: string + description: Base64-encoded audio data delta. + description: Returned when the model-generated audio is updated. + x-oaiMeta: + name: response.audio.delta + group: realtime + example: "{\n \"event_id\": \"event_4950\",\n \"type\": \"response.audio.delta\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_008\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"Base64EncodedAudioDelta\"\n}\n" + RealtimeServerEventResponseAudioDone: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.audio.done + type: string + description: 'The event type, must be `response.audio.done`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + description: "Returned when the model-generated audio is done. Also emitted when a Response\nis interrupted, incomplete, or cancelled.\n" + x-oaiMeta: + name: response.audio.done + group: realtime + example: "{\n \"event_id\": \"event_5152\",\n \"type\": \"response.audio.done\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_008\",\n \"output_index\": 0,\n \"content_index\": 0\n}\n" + RealtimeServerEventResponseAudioTranscriptDelta: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - delta + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.audio_transcript.delta + type: string + description: 'The event type, must be `response.audio_transcript.delta`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + delta: + type: string + description: The transcript delta. + description: "Returned when the model-generated transcription of audio output is updated.\n" + x-oaiMeta: + name: response.audio_transcript.delta + group: realtime + example: "{\n \"event_id\": \"event_4546\",\n \"type\": \"response.audio_transcript.delta\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_008\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"Hello, how can I a\"\n}\n" + RealtimeServerEventResponseAudioTranscriptDone: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - transcript + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.audio_transcript.done + type: string + description: 'The event type, must be `response.audio_transcript.done`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + transcript: + type: string + description: The final transcript of the audio. + description: "Returned when the model-generated transcription of audio output is done\nstreaming. Also emitted when a Response is interrupted, incomplete, or\ncancelled.\n" + x-oaiMeta: + name: response.audio_transcript.done + group: realtime + example: "{\n \"event_id\": \"event_4748\",\n \"type\": \"response.audio_transcript.done\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_008\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"transcript\": \"Hello, how can I assist you today?\"\n}\n" + RealtimeServerEventResponseContentPartAdded: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - part + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.content_part.added + type: string + description: 'The event type, must be `response.content_part.added`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item to which the content part was added. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + part: + type: object + properties: + type: + enum: + - audio + - text + type: string + description: 'The content type ("text", "audio").' + text: + type: string + description: The text content (if type is "text"). + audio: + type: string + description: Base64-encoded audio data (if type is "audio"). + transcript: + type: string + description: The transcript of the audio (if type is "audio"). + description: The content part that was added. + description: "Returned when a new content part is added to an assistant message item during\nresponse generation.\n" + x-oaiMeta: + name: response.content_part.added + group: realtime + example: "{\n \"event_id\": \"event_3738\",\n \"type\": \"response.content_part.added\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_007\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"text\",\n \"text\": \"\"\n }\n}\n" + RealtimeServerEventResponseContentPartDone: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - part + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.content_part.done + type: string + description: 'The event type, must be `response.content_part.done`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + part: + type: object + properties: + type: + enum: + - audio + - text + type: string + description: 'The content type ("text", "audio").' + text: + type: string + description: The text content (if type is "text"). + audio: + type: string + description: Base64-encoded audio data (if type is "audio"). + transcript: + type: string + description: The transcript of the audio (if type is "audio"). + description: The content part that is done. + description: "Returned when a content part is done streaming in an assistant message item.\nAlso emitted when a Response is interrupted, incomplete, or cancelled.\n" + x-oaiMeta: + name: response.content_part.done + group: realtime + example: "{\n \"event_id\": \"event_3940\",\n \"type\": \"response.content_part.done\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_007\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"text\",\n \"text\": \"Sure, I can help with that.\"\n }\n}\n" + RealtimeServerEventResponseCreated: + required: + - event_id + - type + - response + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.created + type: string + description: 'The event type, must be `response.created`.' + x-stainless-const: true + response: + $ref: '#/components/schemas/RealtimeResponse' + description: "Returned when a new Response is created. The first event of response creation,\nwhere the response is in an initial state of `in_progress`.\n" + x-oaiMeta: + name: response.created + group: realtime + example: "{\n \"event_id\": \"event_2930\",\n \"type\": \"response.created\",\n \"response\": {\n \"id\": \"resp_001\",\n \"object\": \"realtime.response\",\n \"status\": \"in_progress\",\n \"status_details\": null,\n \"output\": [],\n \"usage\": null\n }\n}\n" + RealtimeServerEventResponseDone: + required: + - event_id + - type + - response + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.done + type: string + description: 'The event type, must be `response.done`.' + x-stainless-const: true + response: + $ref: '#/components/schemas/RealtimeResponse' + description: "Returned when a Response is done streaming. Always emitted, no matter the\nfinal state. The Response object included in the `response.done` event will\ninclude all output Items in the Response but will omit the raw audio data.\n" + x-oaiMeta: + name: response.done + group: realtime + example: "{\n \"event_id\": \"event_3132\",\n \"type\": \"response.done\",\n \"response\": {\n \"id\": \"resp_001\",\n \"object\": \"realtime.response\",\n \"status\": \"completed\",\n \"status_details\": null,\n \"output\": [\n {\n \"id\": \"msg_006\",\n \"object\": \"realtime.item\",\n \"type\": \"message\",\n \"status\": \"completed\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"Sure, how can I assist you today?\"\n }\n ]\n }\n ],\n \"usage\": {\n \"total_tokens\":275,\n \"input_tokens\":127,\n \"output_tokens\":148,\n \"input_token_details\": {\n \"cached_tokens\":384,\n \"text_tokens\":119,\n \"audio_tokens\":8,\n \"cached_tokens_details\": {\n \"text_tokens\": 128,\n \"audio_tokens\": 256\n }\n },\n \"output_token_details\": {\n \"text_tokens\":36,\n \"audio_tokens\":112\n }\n }\n }\n}\n" + RealtimeServerEventResponseFunctionCallArgumentsDelta: + required: + - event_id + - type + - response_id + - item_id + - output_index + - call_id + - delta + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.function_call_arguments.delta + type: string + description: "The event type, must be `response.function_call_arguments.delta`.\n" + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the function call item. + output_index: + type: integer + description: The index of the output item in the response. + call_id: + type: string + description: The ID of the function call. + delta: + type: string + description: The arguments delta as a JSON string. + description: "Returned when the model-generated function call arguments are updated.\n" + x-oaiMeta: + name: response.function_call_arguments.delta + group: realtime + example: "{\n \"event_id\": \"event_5354\",\n \"type\": \"response.function_call_arguments.delta\",\n \"response_id\": \"resp_002\",\n \"item_id\": \"fc_001\",\n \"output_index\": 0,\n \"call_id\": \"call_001\",\n \"delta\": \"{\\\"location\\\": \\\"San\\\"\"\n}\n" + RealtimeServerEventResponseFunctionCallArgumentsDone: + required: + - event_id + - type + - response_id + - item_id + - output_index + - call_id + - arguments + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.function_call_arguments.done + type: string + description: "The event type, must be `response.function_call_arguments.done`.\n" + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the function call item. + output_index: + type: integer + description: The index of the output item in the response. + call_id: + type: string + description: The ID of the function call. + arguments: + type: string + description: The final arguments as a JSON string. + description: "Returned when the model-generated function call arguments are done streaming.\nAlso emitted when a Response is interrupted, incomplete, or cancelled.\n" + x-oaiMeta: + name: response.function_call_arguments.done + group: realtime + example: "{\n \"event_id\": \"event_5556\",\n \"type\": \"response.function_call_arguments.done\",\n \"response_id\": \"resp_002\",\n \"item_id\": \"fc_001\",\n \"output_index\": 0,\n \"call_id\": \"call_001\",\n \"arguments\": \"{\\\"location\\\": \\\"San Francisco\\\"}\"\n}\n" + RealtimeServerEventResponseOutputItemAdded: + required: + - event_id + - type + - response_id + - output_index + - item + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.output_item.added + type: string + description: 'The event type, must be `response.output_item.added`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the Response to which the item belongs. + output_index: + type: integer + description: The index of the output item in the Response. + item: + $ref: '#/components/schemas/RealtimeConversationItem' + description: Returned when a new Item is created during Response generation. + x-oaiMeta: + name: response.output_item.added + group: realtime + example: "{\n \"event_id\": \"event_3334\",\n \"type\": \"response.output_item.added\",\n \"response_id\": \"resp_001\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_007\",\n \"object\": \"realtime.item\",\n \"type\": \"message\",\n \"status\": \"in_progress\",\n \"role\": \"assistant\",\n \"content\": []\n }\n}\n" + RealtimeServerEventResponseOutputItemDone: + required: + - event_id + - type + - response_id + - output_index + - item + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.output_item.done + type: string + description: 'The event type, must be `response.output_item.done`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the Response to which the item belongs. + output_index: + type: integer + description: The index of the output item in the Response. + item: + $ref: '#/components/schemas/RealtimeConversationItem' + description: "Returned when an Item is done streaming. Also emitted when a Response is\ninterrupted, incomplete, or cancelled.\n" + x-oaiMeta: + name: response.output_item.done + group: realtime + example: "{\n \"event_id\": \"event_3536\",\n \"type\": \"response.output_item.done\",\n \"response_id\": \"resp_001\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_007\",\n \"object\": \"realtime.item\",\n \"type\": \"message\",\n \"status\": \"completed\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"Sure, I can help with that.\"\n }\n ]\n }\n}\n" + RealtimeServerEventResponseTextDelta: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - delta + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.text.delta + type: string + description: 'The event type, must be `response.text.delta`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + delta: + type: string + description: The text delta. + description: Returned when the text value of a "text" content part is updated. + x-oaiMeta: + name: response.text.delta + group: realtime + example: "{\n \"event_id\": \"event_4142\",\n \"type\": \"response.text.delta\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_007\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"Sure, I can h\"\n}\n" + RealtimeServerEventResponseTextDone: + required: + - event_id + - type + - response_id + - item_id + - output_index + - content_index + - text + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - response.text.done + type: string + description: 'The event type, must be `response.text.done`.' + x-stainless-const: true + response_id: + type: string + description: The ID of the response. + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item in the response. + content_index: + type: integer + description: The index of the content part in the item's content array. + text: + type: string + description: The final text content. + description: "Returned when the text value of a \"text\" content part is done streaming. Also\nemitted when a Response is interrupted, incomplete, or cancelled.\n" + x-oaiMeta: + name: response.text.done + group: realtime + example: "{\n \"event_id\": \"event_4344\",\n \"type\": \"response.text.done\",\n \"response_id\": \"resp_001\",\n \"item_id\": \"msg_007\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"text\": \"Sure, I can help with that.\"\n}\n" + RealtimeServerEventSessionCreated: + required: + - event_id + - type + - session + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - session.created + type: string + description: 'The event type, must be `session.created`.' + x-stainless-const: true + session: + $ref: '#/components/schemas/RealtimeSession' + description: "Returned when a Session is created. Emitted automatically when a new\nconnection is established as the first server event. This event will contain\nthe default Session configuration.\n" + x-oaiMeta: + name: session.created + group: realtime + example: "{\n \"event_id\": \"event_1234\",\n \"type\": \"session.created\",\n \"session\": {\n \"id\": \"sess_001\",\n \"object\": \"realtime.session\",\n \"model\": \"gpt-4o-realtime-preview\",\n \"modalities\": [\"text\", \"audio\"],\n \"instructions\": \"...model instructions here...\",\n \"voice\": \"sage\",\n \"input_audio_format\": \"pcm16\",\n \"output_audio_format\": \"pcm16\",\n \"input_audio_transcription\": null,\n \"turn_detection\": {\n \"type\": \"server_vad\",\n \"threshold\": 0.5,\n \"prefix_padding_ms\": 300,\n \"silence_duration_ms\": 200\n },\n \"tools\": [],\n \"tool_choice\": \"auto\",\n \"temperature\": 0.8,\n \"max_response_output_tokens\": \"inf\"\n }\n}\n" + RealtimeServerEventSessionUpdated: + required: + - event_id + - type + - session + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - session.updated + type: string + description: 'The event type, must be `session.updated`.' + x-stainless-const: true + session: + $ref: '#/components/schemas/RealtimeSession' + description: "Returned when a session is updated with a `session.update` event, unless\nthere is an error.\n" + x-oaiMeta: + name: session.updated + group: realtime + example: "{\n \"event_id\": \"event_5678\",\n \"type\": \"session.updated\",\n \"session\": {\n \"id\": \"sess_001\",\n \"object\": \"realtime.session\",\n \"model\": \"gpt-4o-realtime-preview\",\n \"modalities\": [\"text\"],\n \"instructions\": \"New instructions\",\n \"voice\": \"sage\",\n \"input_audio_format\": \"pcm16\",\n \"output_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"whisper-1\"\n },\n \"turn_detection\": null,\n \"tools\": [],\n \"tool_choice\": \"none\",\n \"temperature\": 0.7,\n \"max_response_output_tokens\": 200\n }\n}\n" + RealtimeServerEventTranscriptionSessionUpdated: + required: + - event_id + - type + - session + type: object + properties: + event_id: + type: string + description: The unique ID of the server event. + type: + enum: + - transcription_session.updated + type: string + description: 'The event type, must be `transcription_session.updated`.' + x-stainless-const: true + session: + $ref: '#/components/schemas/RealtimeTranscriptionSessionCreateResponse' + description: "Returned when a transcription session is updated with a `transcription_session.update` event, unless\nthere is an error.\n" + x-oaiMeta: + name: transcription_session.updated + group: realtime + example: "{\n \"event_id\": \"event_5678\",\n \"type\": \"transcription_session.updated\",\n \"session\": {\n \"id\": \"sess_001\",\n \"object\": \"realtime.transcription_session\",\n \"input_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"gpt-4o-transcribe\",\n \"prompt\": \"\",\n \"language\": \"\"\n },\n \"turn_detection\": {\n \"type\": \"server_vad\",\n \"threshold\": 0.5,\n \"prefix_padding_ms\": 300,\n \"silence_duration_ms\": 500,\n \"create_response\": true,\n // \"interrupt_response\": false -- this will NOT be returned\n },\n \"input_audio_noise_reduction\": {\n \"type\": \"near_field\"\n },\n \"include\": [\n \"item.input_audio_transcription.avg_logprob\",\n ],\n }\n}\n" + RealtimeSession: + type: object + properties: + id: + type: string + description: "Unique identifier for the session that looks like `sess_1234567890abcdef`.\n" + modalities: + items: + enum: + - text + - audio + type: string + default: + - text + - audio + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + model: + enum: + - gpt-4o-realtime-preview + - gpt-4o-realtime-preview-2024-10-01 + - gpt-4o-realtime-preview-2024-12-17 + - gpt-4o-mini-realtime-preview + - gpt-4o-mini-realtime-preview-2024-12-17 + type: string + description: "The Realtime model used for this session.\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\n\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + input_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,\nsingle channel (mono), and little-endian byte order.\n" + default: pcm16 + output_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, output audio is sampled at a rate of 24kHz.\n" + default: pcm16 + input_audio_transcription: + type: object + properties: + model: + type: string + description: "The model to use for transcription, current options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format\nwill improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio\nsegment.\nFor `whisper-1`, the [prompt is a list of keywords](/docs/guides/speech-to-text#prompting).\nFor `gpt-4o-transcribe` models, the prompt is a free text string, for example \"expect words related to technology\".\n" + description: "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + turn_detection: + type: object + properties: + type: + enum: + - server_vad + - semantic_vad + type: string + description: "Type of turn detection.\n" + default: server_vad + eagerness: + enum: + - low + - medium + - high + - auto + type: string + description: "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`.\n" + default: auto + threshold: + type: number + description: "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + create_response: + type: boolean + description: "Whether or not to automatically generate a response when a VAD stop event occurs.\n" + default: true + interrupt_response: + type: boolean + description: "Whether or not to automatically interrupt any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs.\n" + default: true + description: "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\nSemantic VAD is more advanced and uses a turn detection model (in conjuction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n" + input_audio_noise_reduction: + type: object + properties: + type: + enum: + - near_field + - far_field + type: string + description: "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n" + description: "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + default: + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function.\n" + default: auto + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a temperature of 0.8 is highly recommended for best performance.\n" + default: 0.8 + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + description: Realtime session object configuration. + RealtimeSessionCreateRequest: + type: object + properties: + modalities: + items: + enum: + - text + - audio + type: string + default: + - text + - audio + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + model: + enum: + - gpt-4o-realtime-preview + - gpt-4o-realtime-preview-2024-10-01 + - gpt-4o-realtime-preview-2024-12-17 + - gpt-4o-mini-realtime-preview + - gpt-4o-mini-realtime-preview-2024-12-17 + type: string + description: "The Realtime model used for this session.\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model calls. This field allows the client to guide the model on desired responses. The model can be instructed on response content and format, (e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good responses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion into your voice\", \"laugh frequently\"). The instructions are not guaranteed to be followed by the model, but they provide guidance to the model on the desired behavior.\n\nNote that the server sets default instructions which will be used if this field is not set and are visible in the `session.created` event at the start of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + input_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,\nsingle channel (mono), and little-endian byte order.\n" + default: pcm16 + output_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, output audio is sampled at a rate of 24kHz.\n" + default: pcm16 + input_audio_transcription: + type: object + properties: + model: + type: string + description: "The model to use for transcription, current options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format\nwill improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio\nsegment.\nFor `whisper-1`, the [prompt is a list of keywords](/docs/guides/speech-to-text#prompting).\nFor `gpt-4o-transcribe` models, the prompt is a free text string, for example \"expect words related to technology\".\n" + description: "Configuration for input audio transcription, defaults to off and can be set to `null` to turn off once on. Input audio transcription is not native to the model, since the model consumes audio directly. Transcription runs asynchronously through [the /audio/transcriptions endpoint](https://platform.openai.com/docs/api-reference/audio/createTranscription) and should be treated as guidance of input audio content rather than precisely what the model heard. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + turn_detection: + type: object + properties: + type: + enum: + - server_vad + - semantic_vad + type: string + description: "Type of turn detection.\n" + default: server_vad + eagerness: + enum: + - low + - medium + - high + - auto + type: string + description: "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`.\n" + default: auto + threshold: + type: number + description: "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + create_response: + type: boolean + description: "Whether or not to automatically generate a response when a VAD stop event occurs.\n" + default: true + interrupt_response: + type: boolean + description: "Whether or not to automatically interrupt any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs.\n" + default: true + description: "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\nSemantic VAD is more advanced and uses a turn detection model (in conjuction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n" + input_audio_noise_reduction: + type: object + properties: + type: + enum: + - near_field + - far_field + type: string + description: "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n" + description: "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + default: + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function.\n" + default: auto + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. For audio models a temperature of 0.8 is highly recommended for best performance.\n" + default: 0.8 + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + description: Realtime session object configuration. + RealtimeSessionCreateResponse: + required: + - client_secret + type: object + properties: + client_secret: + required: + - value + - expires_at + type: object + properties: + value: + type: string + description: "Ephemeral key usable in client environments to authenticate connections\nto the Realtime API. Use this in client-side environments rather than\na standard API token, which should only be used server-side.\n" + expires_at: + type: integer + description: "Timestamp for when the token expires. Currently, all tokens expire\nafter one minute.\n" + description: Ephemeral key returned by the API. + modalities: + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + instructions: + type: string + description: "The default system instructions (i.e. system message) prepended to model\ncalls. This field allows the client to guide the model on desired\nresponses. The model can be instructed on response content and format,\n(e.g. \"be extremely succinct\", \"act friendly\", \"here are examples of good\nresponses\") and on audio behavior (e.g. \"talk quickly\", \"inject emotion\ninto your voice\", \"laugh frequently\"). The instructions are not guaranteed\nto be followed by the model, but they provide guidance to the model on the\ndesired behavior.\n\nNote that the server sets default instructions which will be used if this\nfield is not set and are visible in the `session.created` event at the\nstart of the session.\n" + voice: + $ref: '#/components/schemas/VoiceIdsShared' + input_audio_format: + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + output_audio_format: + type: string + description: "The format of output audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + input_audio_transcription: + type: object + properties: + model: + type: string + description: "The model to use for transcription, `whisper-1` is the only currently\nsupported model.\n" + description: "Configuration for input audio transcription, defaults to off and can be\nset to `null` to turn off once on. Input audio transcription is not native\nto the model, since the model consumes audio directly. Transcription runs\nasynchronously through Whisper and should be treated as rough guidance\nrather than the representation understood by the model.\n" + turn_detection: + type: object + properties: + type: + type: string + description: "Type of turn detection, only `server_vad` is currently supported.\n" + threshold: + type: number + description: "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + description: "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech.\n" + tools: + type: array + items: + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool, i.e. `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function. + description: + type: string + description: "The description of the function, including guidance on when and how\nto call it, and guidance about what to tell the user when calling\n(if anything).\n" + parameters: + type: object + description: Parameters of the function in JSON Schema. + description: Tools (functions) available to the model. + tool_choice: + type: string + description: "How the model chooses tools. Options are `auto`, `none`, `required`, or\nspecify a function.\n" + temperature: + type: number + description: "Sampling temperature for the model, limited to [0.6, 1.2]. Defaults to 0.8.\n" + max_response_output_tokens: + oneOf: + - type: integer + - enum: + - inf + type: string + x-stainless-const: true + description: "Maximum number of output tokens for a single assistant response,\ninclusive of tool calls. Provide an integer between 1 and 4096 to\nlimit output tokens, or `inf` for the maximum available tokens for a\ngiven model. Defaults to `inf`.\n" + description: "A new Realtime session configuration, with an ephermeral key. Default TTL\nfor keys is one minute.\n" + x-oaiMeta: + name: The session object + group: realtime + example: "{\n \"id\": \"sess_001\",\n \"object\": \"realtime.session\",\n \"model\": \"gpt-4o-realtime-preview\",\n \"modalities\": [\"audio\", \"text\"],\n \"instructions\": \"You are a friendly assistant.\",\n \"voice\": \"alloy\",\n \"input_audio_format\": \"pcm16\",\n \"output_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"whisper-1\"\n },\n \"turn_detection\": null,\n \"tools\": [],\n \"tool_choice\": \"none\",\n \"temperature\": 0.7,\n \"max_response_output_tokens\": 200,\n \"client_secret\": {\n \"value\": \"ek_abc123\", \n \"expires_at\": 1234567890\n }\n}\n" + RealtimeTranscriptionSessionCreateRequest: + type: object + properties: + modalities: + items: + enum: + - text + - audio + type: string + default: + - text + - audio + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + input_audio_format: + enum: + - pcm16 + - g711_ulaw + - g711_alaw + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\nFor `pcm16`, input audio must be 16-bit PCM at a 24kHz sample rate,\nsingle channel (mono), and little-endian byte order.\n" + default: pcm16 + input_audio_transcription: + type: object + properties: + model: + enum: + - gpt-4o-transcribe + - gpt-4o-mini-transcribe + - whisper-1 + type: string + description: "The model to use for transcription, current options are `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, and `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format\nwill improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio\nsegment.\nFor `whisper-1`, the [prompt is a list of keywords](/docs/guides/speech-to-text#prompting).\nFor `gpt-4o-transcribe` models, the prompt is a free text string, for example \"expect words related to technology\".\n" + description: "Configuration for input audio transcription. The client can optionally set the language and prompt for transcription, these offer additional guidance to the transcription service.\n" + turn_detection: + type: object + properties: + type: + enum: + - server_vad + - semantic_vad + type: string + description: "Type of turn detection.\n" + default: server_vad + eagerness: + enum: + - low + - medium + - high + - auto + type: string + description: "Used only for `semantic_vad` mode. The eagerness of the model to respond. `low` will wait longer for the user to continue speaking, `high` will respond more quickly. `auto` is the default and is equivalent to `medium`.\n" + default: auto + threshold: + type: number + description: "Used only for `server_vad` mode. Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Used only for `server_vad` mode. Amount of audio to include before the VAD detected speech (in\nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Used only for `server_vad` mode. Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + create_response: + type: boolean + description: "Whether or not to automatically generate a response when a VAD stop event occurs. Not available for transcription sessions.\n" + default: true + interrupt_response: + type: boolean + description: "Whether or not to automatically interrupt any ongoing response with output to the default\nconversation (i.e. `conversation` of `auto`) when a VAD start event occurs. Not available for transcription sessions.\n" + default: true + description: "Configuration for turn detection, ether Server VAD or Semantic VAD. This can be set to `null` to turn off, in which case the client must manually trigger model response.\nServer VAD means that the model will detect the start and end of speech based on audio volume and respond at the end of user speech.\nSemantic VAD is more advanced and uses a turn detection model (in conjuction with VAD) to semantically estimate whether the user has finished speaking, then dynamically sets a timeout based on this probability. For example, if user audio trails off with \"uhhm\", the model will score a low probability of turn end and wait longer for the user to continue speaking. This can be useful for more natural conversations, but may have a higher latency.\n" + input_audio_noise_reduction: + type: object + properties: + type: + enum: + - near_field + - far_field + type: string + description: "Type of noise reduction. `near_field` is for close-talking microphones such as headphones, `far_field` is for far-field microphones such as laptop or conference room microphones.\n" + description: "Configuration for input audio noise reduction. This can be set to `null` to turn off.\nNoise reduction filters audio added to the input audio buffer before it is sent to VAD and the model.\nFiltering the audio can improve VAD and turn detection accuracy (reducing false positives) and model performance by improving perception of the input audio.\n" + default: + include: + type: array + items: + type: string + description: "The set of items to include in the transcription. Current available items are:\n- `item.input_audio_transcription.logprobs`\n" + description: Realtime transcription session object configuration. + RealtimeTranscriptionSessionCreateResponse: + required: + - client_secret + type: object + properties: + client_secret: + required: + - value + - expires_at + type: object + properties: + value: + type: string + description: "Ephemeral key usable in client environments to authenticate connections\nto the Realtime API. Use this in client-side environments rather than\na standard API token, which should only be used server-side.\n" + expires_at: + type: integer + description: "Timestamp for when the token expires. Currently, all tokens expire\nafter one minute.\n" + description: "Ephemeral key returned by the API. Only present when the session is\ncreated on the server via REST API.\n" + modalities: + items: + enum: + - text + - audio + type: string + description: "The set of modalities the model can respond with. To disable audio,\nset this to [\"text\"].\n" + input_audio_format: + type: string + description: "The format of input audio. Options are `pcm16`, `g711_ulaw`, or `g711_alaw`.\n" + input_audio_transcription: + type: object + properties: + model: + enum: + - gpt-4o-transcribe + - gpt-4o-mini-transcribe + - whisper-1 + type: string + description: "The model to use for transcription. Can be `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, or `whisper-1`.\n" + language: + type: string + description: "The language of the input audio. Supplying the input language in\n[ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g. `en`) format\nwill improve accuracy and latency.\n" + prompt: + type: string + description: "An optional text to guide the model's style or continue a previous audio\nsegment. The [prompt](/docs/guides/speech-to-text#prompting) should match\nthe audio language.\n" + description: "Configuration of the transcription model.\n" + turn_detection: + type: object + properties: + type: + type: string + description: "Type of turn detection, only `server_vad` is currently supported.\n" + threshold: + type: number + description: "Activation threshold for VAD (0.0 to 1.0), this defaults to 0.5. A\nhigher threshold will require louder audio to activate the model, and\nthus might perform better in noisy environments.\n" + prefix_padding_ms: + type: integer + description: "Amount of audio to include before the VAD detected speech (in \nmilliseconds). Defaults to 300ms.\n" + silence_duration_ms: + type: integer + description: "Duration of silence to detect speech stop (in milliseconds). Defaults\nto 500ms. With shorter values the model will respond more quickly,\nbut may jump in on short pauses from the user.\n" + description: "Configuration for turn detection. Can be set to `null` to turn off. Server\nVAD means that the model will detect the start and end of speech based on\naudio volume and respond at the end of user speech.\n" + description: "A new Realtime transcription session configuration.\n\nWhen a session is created on the server via REST API, the session object\nalso contains an ephemeral key. Default TTL for keys is one minute. This\nproperty is not present when a session is updated via the WebSocket API.\n" + x-oaiMeta: + name: The transcription session object + group: realtime + example: "{\n \"id\": \"sess_BBwZc7cFV3XizEyKGDCGL\",\n \"object\": \"realtime.transcription_session\",\n \"expires_at\": 1742188264,\n \"modalities\": [\"audio\", \"text\"],\n \"turn_detection\": {\n \"type\": \"server_vad\",\n \"threshold\": 0.5,\n \"prefix_padding_ms\": 300,\n \"silence_duration_ms\": 200\n },\n \"input_audio_format\": \"pcm16\",\n \"input_audio_transcription\": {\n \"model\": \"gpt-4o-transcribe\",\n \"language\": null,\n \"prompt\": \"\"\n },\n \"client_secret\": null\n}\n" + Reasoning: + title: Reasoning + type: object + properties: + effort: + $ref: '#/components/schemas/ReasoningEffort' + description: + enum: + - auto + - concise + - detailed + type: string + description: "A summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n" + nullable: true + generate_summary: + enum: + - auto + - concise + - detailed + type: string + description: "**Deprecated:** use `summary` instead.\n\nA summary of the reasoning performed by the model. This can be\nuseful for debugging and understanding the model's reasoning process.\nOne of `auto`, `concise`, or `detailed`.\n" + nullable: true + deprecated: true + description: "**o-series models only**\n\nConfiguration options for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\n" + ReasoningEffort: + enum: + - low + - medium + - high + type: string + description: "**o-series models only** \n\nConstrains effort on reasoning for \n[reasoning models](https://platform.openai.com/docs/guides/reasoning).\nCurrently supported values are `low`, `medium`, and `high`. Reducing\nreasoning effort can result in faster responses and fewer tokens used\non reasoning in a response.\n" + default: medium + nullable: true + ReasoningItem: + title: Reasoning + required: + - id + - summary + - type + type: object + properties: + type: + enum: + - reasoning + type: string + description: "The type of the object. Always `reasoning`.\n" + x-stainless-const: true + id: + type: string + description: "The unique identifier of the reasoning content.\n" + description: + type: array + items: + required: + - type + - text + type: object + properties: + type: + enum: + - summary_text + type: string + description: "The type of the object. Always `summary_text`.\n" + x-stainless-const: true + text: + type: string + description: "A short summary of the reasoning used by the model when generating\nthe response.\n" + description: "Reasoning text contents.\n" + status: + enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API.\n" + description: "A description of the chain of thought used by a reasoning model while generating\na response.\n" + Response: + allOf: + - $ref: '#/components/schemas/ModelResponseProperties' + - $ref: '#/components/schemas/ResponseProperties' + - required: + - id + - object + - created_at + - error + - incomplete_details + - instructions + - model + - tools + - output + - parallel_tool_calls + - metadata + - tool_choice + - temperature + - top_p + type: object + properties: + id: + type: string + description: "Unique identifier for this Response.\n" + object: + enum: + - response + type: string + description: "The object type of this resource - always set to `response`.\n" + x-stainless-const: true + status: + enum: + - completed + - failed + - in_progress + - incomplete + type: string + description: "The status of the response generation. One of `completed`, `failed`,\n`in_progress`, or `incomplete`.\n" + created_at: + type: number + description: "Unix timestamp (in seconds) of when this Response was created.\n" + error: + $ref: '#/components/schemas/ResponseError' + incomplete_details: + type: object + properties: + reason: + enum: + - max_output_tokens + - content_filter + type: string + description: The reason why the response is incomplete. + description: "Details about why the response is incomplete.\n" + nullable: true + output: + type: array + items: + $ref: '#/components/schemas/OutputItem' + description: "An array of content items generated by the model.\n\n- The length and order of items in the `output` array is dependent on the model's response.\n- Rather than accessing the first item in the `output` array and assuming it's an `assistant` message with the content generated by\n the model, you might consider using the `output_text` property where\n supported in SDKs.\n" + output_text: + type: string + description: "SDK-only convenience property that contains the aggregated text output\nfrom all `output_text` items in the `output` array, if any are present.\nSupported in the Python and JavaScript SDKs.\n" + nullable: true + x-oaiSupportedSDKs: + - python + - javascript + usage: + $ref: '#/components/schemas/ResponseUsage' + parallel_tool_calls: + type: boolean + description: "Whether to allow the model to run tool calls in parallel.\n" + default: true + x-oaiMeta: + name: The response object + group: responses + example: "{ \"id\": \"resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41\",\n \"object\": \"response\",\n \"created_at\": 1741476777,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [\n {\n \"type\": \"message\",\n \"id\": \"msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41\",\n \"status\": \"completed\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"The image depicts a scenic landscape with a wooden boardwalk or pathway leading through lush, green grass under a blue sky with some clouds. The setting suggests a peaceful natural area, possibly a park or nature reserve. There are trees and shrubs in the background.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1.0,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1.0,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 328,\n \"input_tokens_details\": {\n \"cached_tokens\": 0\n },\n \"output_tokens\": 52,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 380\n },\n \"user\": null,\n \"metadata\": {}\n}\n" + ResponseAudioDeltaEvent: + required: + - type + - delta + type: object + properties: + type: + enum: + - response.audio.delta + type: string + description: "The type of the event. Always `response.audio.delta`.\n" + x-stainless-const: true + delta: + type: string + description: "A chunk of Base64 encoded response audio bytes.\n" + description: Emitted when there is a partial audio response. + x-oaiMeta: + name: response.audio.delta + group: responses + example: "{\n \"type\": \"response.audio.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \"base64encoded...\"\n}\n" + ResponseAudioDoneEvent: + required: + - type + - response_id + type: object + properties: + type: + enum: + - response.audio.done + type: string + description: "The type of the event. Always `response.audio.done`.\n" + x-stainless-const: true + description: Emitted when the audio response is complete. + x-oaiMeta: + name: response.audio.done + group: responses + example: "{\n \"type\": \"response.audio.done\",\n \"response_id\": \"resp-123\"\n}\n" + ResponseAudioTranscriptDeltaEvent: + required: + - type + - response_id + - delta + type: object + properties: + type: + enum: + - response.audio.transcript.delta + type: string + description: "The type of the event. Always `response.audio.transcript.delta`.\n" + x-stainless-const: true + delta: + type: string + description: "The partial transcript of the audio response.\n" + description: Emitted when there is a partial transcript of audio. + x-oaiMeta: + name: response.audio.transcript.delta + group: responses + example: "{\n \"type\": \"response.audio.transcript.delta\",\n \"response_id\": \"resp_123\",\n \"delta\": \" ... partial transcript ... \"\n}\n" + ResponseAudioTranscriptDoneEvent: + required: + - type + - response_id + type: object + properties: + type: + enum: + - response.audio.transcript.done + type: string + description: "The type of the event. Always `response.audio.transcript.done`.\n" + x-stainless-const: true + description: Emitted when the full audio transcript is completed. + x-oaiMeta: + name: response.audio.transcript.done + group: responses + example: "{\n \"type\": \"response.audio.transcript.done\",\n \"response_id\": \"resp_123\"\n}\n" + ResponseCodeInterpreterCallCodeDeltaEvent: + required: + - type + - response_id + - output_index + - delta + type: object + properties: + type: + enum: + - response.code_interpreter_call.code.delta + type: string + description: "The type of the event. Always `response.code_interpreter_call.code.delta`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + delta: + type: string + description: "The partial code snippet added by the code interpreter.\n" + description: Emitted when a partial code snippet is added by the code interpreter. + x-oaiMeta: + name: response.code_interpreter_call.code.delta + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.code.delta\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"delta\": \"partial code\"\n}\n" + ResponseCodeInterpreterCallCodeDoneEvent: + required: + - type + - response_id + - output_index + - code + type: object + properties: + type: + enum: + - response.code_interpreter_call.code.done + type: string + description: "The type of the event. Always `response.code_interpreter_call.code.done`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code: + type: string + description: "The final code snippet output by the code interpreter.\n" + description: Emitted when code snippet output is finalized by the code interpreter. + x-oaiMeta: + name: response.code_interpreter_call.code.done + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.code.done\",\n \"response_id\": \"resp-123\",\n \"output_index\": 3,\n \"code\": \"console.log('done');\"\n}\n" + ResponseCodeInterpreterCallCompletedEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.completed + type: string + description: "The type of the event. Always `response.code_interpreter_call.completed`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: Emitted when the code interpreter call is completed. + x-oaiMeta: + name: response.code_interpreter_call.completed + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.completed\",\n \"response_id\": \"resp-123\",\n \"output_index\": 5,\n \"code_interpreter_call\": {}\n}\n" + ResponseCodeInterpreterCallInProgressEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.in_progress + type: string + description: "The type of the event. Always `response.code_interpreter_call.in_progress`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: Emitted when a code interpreter call is in progress. + x-oaiMeta: + name: response.code_interpreter_call.in_progress + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.in.progress\",\n \"response_id\": \"resp-123\",\n \"output_index\": 0,\n \"code_interpreter_call\": {}\n}\n" + ResponseCodeInterpreterCallInterpretingEvent: + required: + - type + - response_id + - output_index + - code_interpreter_call + type: object + properties: + type: + enum: + - response.code_interpreter_call.interpreting + type: string + description: "The type of the event. Always `response.code_interpreter_call.interpreting`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the code interpreter call is in progress.\n" + code_interpreter_call: + $ref: '#/components/schemas/CodeInterpreterToolCall' + description: "Emitted when the code interpreter is actively interpreting the code\nsnippet." + x-oaiMeta: + name: response.code_interpreter_call.interpreting + group: responses + example: "{\n \"type\": \"response.code_interpreter_call.interpreting\",\n \"response_id\": \"resp-123\",\n \"output_index\": 4,\n \"code_interpreter_call\": {}\n}\n" + ResponseCompletedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.completed + type: string + description: "The type of the event. Always `response.completed`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/Response' + description: Emitted when the model response is complete. + x-oaiMeta: + name: response.completed + group: responses + example: "{ \"type\": \"response.completed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"completed\",\n \"error\": null,\n \"incomplete_details\": null,\n \"input\": [],\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [\n {\n \"id\": \"msg_123\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n ],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": {\n \"input_tokens\": 0,\n \"output_tokens\": 0,\n \"output_tokens_details\": {\n \"reasoning_tokens\": 0\n },\n \"total_tokens\": 0\n },\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseContentPartAddedEvent: + required: + - type + - item_id + - output_index + - content_index + - part + type: object + properties: + type: + enum: + - response.content_part.added + type: string + description: "The type of the event. Always `response.content_part.added`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the content part was added to.\n" + output_index: + type: integer + description: "The index of the output item that the content part was added to.\n" + content_index: + type: integer + description: "The index of the content part that was added.\n" + part: + $ref: '#/components/schemas/OutputContent' + description: Emitted when a new content part is added. + x-oaiMeta: + name: response.content_part.added + group: responses + example: "{\n \"type\": \"response.content_part.added\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"\",\n \"annotations\": []\n }\n}\n" + ResponseContentPartDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - part + type: object + properties: + type: + enum: + - response.content_part.done + type: string + description: "The type of the event. Always `response.content_part.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the content part was added to.\n" + output_index: + type: integer + description: "The index of the output item that the content part was added to.\n" + content_index: + type: integer + description: "The index of the content part that is done.\n" + part: + $ref: '#/components/schemas/OutputContent' + description: Emitted when a content part is done. + x-oaiMeta: + name: response.content_part.done + group: responses + example: "{ \"type\": \"response.content_part.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"part\": {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n}\n" + ResponseCreatedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.created + type: string + description: "The type of the event. Always `response.created`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/Response' + description: "An event that is emitted when a response is created.\n" + x-oaiMeta: + name: response.created + group: responses + example: "{\n \"type\": \"response.created\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseError: + required: + - code + - message + type: object + properties: + code: + $ref: '#/components/schemas/ResponseErrorCode' + message: + type: string + description: "A human-readable description of the error.\n" + description: "An error object returned when the model fails to generate a Response.\n" + nullable: true + ResponseErrorCode: + enum: + - server_error + - rate_limit_exceeded + - invalid_prompt + - vector_store_timeout + - invalid_image + - invalid_image_format + - invalid_base64_image + - invalid_image_url + - image_too_large + - image_too_small + - image_parse_error + - image_content_policy_violation + - invalid_image_mode + - image_file_too_large + - unsupported_image_media_type + - empty_image_file + - failed_to_download_image + - image_file_not_found + type: string + description: "The error code for the response.\n" + ResponseErrorEvent: + required: + - type + - code + - message + - param + type: object + properties: + type: + enum: + - error + type: string + description: "The type of the event. Always `error`.\n" + x-stainless-const: true + code: + type: string + description: "The error code.\n" + nullable: true + message: + type: string + description: "The error message.\n" + param: + type: string + description: "The error parameter.\n" + nullable: true + description: Emitted when an error occurs. + x-oaiMeta: + name: error + group: responses + example: "{\n \"type\": \"error\",\n \"code\": \"ERR_SOMETHING\",\n \"message\": \"Something went wrong\",\n \"param\": null\n}\n" + ResponseFailedEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.failed + type: string + description: "The type of the event. Always `response.failed`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/Response' + description: "An event that is emitted when a response fails.\n" + x-oaiMeta: + name: response.failed + group: responses + example: "{\n \"type\": \"response.failed\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"failed\",\n \"error\": {\n \"code\": \"server_error\",\n \"message\": \"The model failed to generate a response.\"\n },\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseFileSearchCallCompletedEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.completed + type: string + description: "The type of the event. Always `response.file_search_call.completed`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is initiated.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search call is completed (results found). + x-oaiMeta: + name: response.file_search_call.completed + group: responses + example: "{\n \"type\": \"response.file_search_call.completed\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFileSearchCallInProgressEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.in_progress + type: string + description: "The type of the event. Always `response.file_search_call.in_progress`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is initiated.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search call is initiated. + x-oaiMeta: + name: response.file_search_call.in_progress + group: responses + example: "{\n \"type\": \"response.file_search_call.in_progress\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFileSearchCallSearchingEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.file_search_call.searching + type: string + description: "The type of the event. Always `response.file_search_call.searching`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the file search call is searching.\n" + item_id: + type: string + description: "The ID of the output item that the file search call is initiated.\n" + description: Emitted when a file search is currently searching. + x-oaiMeta: + name: response.file_search_call.searching + group: responses + example: "{\n \"type\": \"response.file_search_call.searching\",\n \"output_index\": 0,\n \"item_id\": \"fs_123\",\n}\n" + ResponseFormatJsonObject: + title: JSON object + required: + - type + type: object + properties: + type: + enum: + - json_object + type: string + description: The type of response format being defined. Always `json_object`. + x-stainless-const: true + description: "JSON object response format. An older method of generating JSON responses.\nUsing `json_schema` is recommended for models that support it. Note that the\nmodel will not generate JSON without a system or user message instructing it\nto do so.\n" + ResponseFormatJsonSchema: + title: JSON schema + required: + - type + - json_schema + type: object + properties: + type: + enum: + - json_schema + type: string + description: The type of response format being defined. Always `json_schema`. + x-stainless-const: true + json_schema: + title: JSON schema + required: + - name + type: object + properties: + description: + type: string + description: "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + name: + type: string + description: "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + schema: + $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema' + strict: + type: boolean + description: "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n" + default: false + nullable: true + description: "Structured Outputs configuration options, including a JSON Schema.\n" + description: "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n" + ResponseFormatJsonSchemaSchema: + title: JSON schema + type: object + description: "The schema for the response format, described as a JSON Schema object.\nLearn how to build JSON schemas [here](https://json-schema.org/).\n" + ResponseFormatText: + title: Text + required: + - type + type: object + properties: + type: + enum: + - text + type: string + description: The type of response format being defined. Always `text`. + x-stainless-const: true + description: "Default response format. Used to generate text responses.\n" + ResponseFunctionCallArgumentsDeltaEvent: + required: + - type + - item_id + - output_index + - delta + type: object + properties: + type: + enum: + - response.function_call_arguments.delta + type: string + description: "The type of the event. Always `response.function_call_arguments.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the function-call arguments delta is added to.\n" + output_index: + type: integer + description: "The index of the output item that the function-call arguments delta is added to.\n" + delta: + type: string + description: "The function-call arguments delta that is added.\n" + description: Emitted when there is a partial function-call arguments delta. + x-oaiMeta: + name: response.function_call_arguments.delta + group: responses + example: "{\n \"type\": \"response.function_call_arguments.delta\",\n \"item_id\": \"item-abc\",\n \"output_index\": 0,\n \"delta\": \"{ \\\"arg\\\":\"\n}\n" + ResponseFunctionCallArgumentsDoneEvent: + required: + - type + - item_id + - output_index + - arguments + type: object + properties: + type: + enum: + - response.function_call_arguments.done + type: string + x-stainless-const: true + item_id: + type: string + description: The ID of the item. + output_index: + type: integer + description: The index of the output item. + arguments: + type: string + description: The function-call arguments. + description: Emitted when function-call arguments are finalized. + x-oaiMeta: + name: response.function_call_arguments.done + group: responses + example: "{\n \"type\": \"response.function_call_arguments.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"arguments\": \"{ \\\"arg\\\": 123 }\"\n}\n" + ResponseInProgressEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.in_progress + type: string + description: "The type of the event. Always `response.in_progress`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/Response' + description: Emitted when the response is in progress. + x-oaiMeta: + name: response.in_progress + group: responses + example: "{\n \"type\": \"response.in_progress\",\n \"response\": {\n \"id\": \"resp_67ccfcdd16748190a91872c75d38539e09e4d4aac714747c\",\n \"object\": \"response\",\n \"created_at\": 1741487325,\n \"status\": \"in_progress\",\n \"error\": null,\n \"incomplete_details\": null,\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-2024-08-06\",\n \"output\": [],\n \"parallel_tool_calls\": true,\n \"previous_response_id\": null,\n \"reasoning\": {\n \"effort\": null,\n \"summary\": null\n },\n \"store\": true,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseIncompleteEvent: + required: + - type + - response + type: object + properties: + type: + enum: + - response.incomplete + type: string + description: "The type of the event. Always `response.incomplete`.\n" + x-stainless-const: true + response: + $ref: '#/components/schemas/Response' + description: "An event that is emitted when a response finishes as incomplete.\n" + x-oaiMeta: + name: response.incomplete + group: responses + example: "{\n \"type\": \"response.incomplete\",\n \"response\": {\n \"id\": \"resp_123\",\n \"object\": \"response\",\n \"created_at\": 1740855869,\n \"status\": \"incomplete\",\n \"error\": null, \n \"incomplete_details\": {\n \"reason\": \"max_tokens\"\n },\n \"instructions\": null,\n \"max_output_tokens\": null,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"output\": [],\n \"previous_response_id\": null,\n \"reasoning_effort\": null,\n \"store\": false,\n \"temperature\": 1,\n \"text\": {\n \"format\": {\n \"type\": \"text\"\n }\n },\n \"tool_choice\": \"auto\",\n \"tools\": [],\n \"top_p\": 1,\n \"truncation\": \"disabled\",\n \"usage\": null,\n \"user\": null,\n \"metadata\": {}\n }\n}\n" + ResponseItemList: + required: + - object + - data + - has_more + - first_id + - last_id + type: object + properties: + object: + enum: + - list + type: string + description: 'The type of object returned, must be `list`.' + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/ItemResource' + description: A list of items used to generate this response. + has_more: + type: boolean + description: Whether there are more items available. + first_id: + type: string + description: The ID of the first item in the list. + last_id: + type: string + description: The ID of the last item in the list. + description: A list of Response items. + x-oaiMeta: + name: The input item list + group: responses + example: "{ \"object\": \"list\",\n \"data\": [\n {\n \"id\": \"msg_abc123\",\n \"type\": \"message\",\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"input_text\",\n \"text\": \"Tell me a three sentence bedtime story about a unicorn.\"\n }\n ]\n }\n ],\n \"first_id\": \"msg_abc123\",\n \"last_id\": \"msg_abc123\",\n \"has_more\": false\n}\n" + ResponseModalities: + type: array + items: + enum: + - text + - audio + type: string + description: "Output types that you would like the model to generate.\nMost models are capable of generating text, which is the default:\n\n`[\"text\"]`\n\nThe `gpt-4o-audio-preview` model can also be used to\n[generate audio](/docs/guides/audio). To request that this model generate\nboth text and audio responses, you can use:\n\n`[\"text\", \"audio\"]`\n" + nullable: true + ResponseOutputItemAddedEvent: + required: + - type + - output_index + - item + type: object + properties: + type: + enum: + - response.output_item.added + type: string + description: "The type of the event. Always `response.output_item.added`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that was added.\n" + item: + $ref: '#/components/schemas/OutputItem' + description: Emitted when a new output item is added. + x-oaiMeta: + name: response.output_item.added + group: responses + example: "{\n \"type\": \"response.output_item.added\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"in_progress\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": []\n }\n}\n" + ResponseOutputItemDoneEvent: + required: + - type + - output_index + - item + type: object + properties: + type: + enum: + - response.output_item.done + type: string + description: "The type of the event. Always `response.output_item.done`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that was marked done.\n" + item: + $ref: '#/components/schemas/OutputItem' + description: Emitted when an output item is marked done. + x-oaiMeta: + name: response.output_item.done + group: responses + example: "{ \"type\": \"response.output_item.done\",\n \"output_index\": 0,\n \"item\": {\n \"id\": \"msg_123\",\n \"status\": \"completed\",\n \"type\": \"message\",\n \"role\": \"assistant\",\n \"content\": [\n {\n \"type\": \"output_text\",\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\",\n \"annotations\": []\n }\n ]\n }\n}\n" + ResponseProperties: + type: object + properties: + previous_response_id: + type: string + description: "The unique ID of the previous response to the model. Use this to\ncreate multi-turn conversations. Learn more about \n[conversation state](/docs/guides/conversation-state).\n" + nullable: true + model: + $ref: '#/components/schemas/ModelIdsResponses' + reasoning: + $ref: '#/components/schemas/Reasoning' + max_output_tokens: + type: integer + description: "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and [reasoning tokens](/docs/guides/reasoning).\n" + nullable: true + instructions: + type: string + description: "Inserts a system (or developer) message as the first item in the model's context.\n\nWhen using along with `previous_response_id`, the instructions from a previous\nresponse will not be carried over to the next response. This makes it simple\nto swap out system (or developer) messages in new responses.\n" + nullable: true + text: + type: object + properties: + format: + $ref: '#/components/schemas/TextResponseFormatConfiguration' + description: "Configuration options for a text response from the model. Can be plain\ntext or structured JSON data. Learn more:\n- [Text inputs and outputs](/docs/guides/text)\n- [Structured Outputs](/docs/guides/structured-outputs)\n" + tools: + type: array + items: + $ref: '#/components/schemas/Tool' + description: "An array of tools the model may call while generating a response. You\ncan specify which tool to use by setting the `tool_choice` parameter.\n\nThe two categories of tools you can provide the model are:\n\n- **Built-in tools**: Tools that are provided by OpenAI that extend the model's capabilities, like [web search](/docs/guides/tools-web-search)\n or [file search](/docs/guides/tools-file-search). Learn more about\n [built-in tools](/docs/guides/tools).\n- **Function calls (custom tools)**: Functions that are defined by you, enabling the model to call your own code. Learn more about\n [function calling](/docs/guides/function-calling).\n" + tool_choice: + oneOf: + - $ref: '#/components/schemas/ToolChoiceOptions' + - $ref: '#/components/schemas/ToolChoiceTypes' + - $ref: '#/components/schemas/ToolChoiceFunction' + description: "How the model should select which tool (or tools) to use when generating\na response. See the `tools` parameter to see how to specify which tools\nthe model can call.\n" + truncation: + enum: + - auto + - disabled + type: string + description: "The truncation strategy to use for the model response.\n- `auto`: If the context of this response and previous ones exceeds the model's context window size, the model will truncate the \n response to fit the context window by dropping input items in the\n middle of the conversation. \n- `disabled` (default): If a model response will exceed the context window size for a model, the request will fail with a 400 error.\n" + default: disabled + nullable: true + ResponseReasoningSummaryPartAddedEvent: + required: + - type + - item_id + - output_index + - summary_index + - part + type: object + properties: + type: + enum: + - response.reasoning_summary_part.added + type: string + description: "The type of the event. Always `response.reasoning_summary_part.added`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the item this summary part is associated with.\n" + output_index: + type: integer + description: "The index of the output item this summary part is associated with.\n" + summary_index: + type: integer + description: "The index of the summary part within the reasoning summary.\n" + part: + required: + - type + - text + type: object + properties: + type: + enum: + - summary_text + type: string + description: The type of the summary part. Always `summary_text`. + x-stainless-const: true + text: + type: string + description: The text of the summary part. + description: "The summary part that was added.\n" + description: Emitted when a new reasoning summary part is added. + x-oaiMeta: + name: response.reasoning_summary_part.added + group: responses + example: "{\n \"type\": \"response.reasoning_summary_part.added\",\n \"item_id\": \"rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476\",\n \"output_index\": 0,\n \"summary_index\": 0,\n \"part\": {\n \"type\": \"summary_text\",\n \"text\": \"\"\n }\n}\n" + ResponseReasoningSummaryPartDoneEvent: + required: + - type + - item_id + - output_index + - summary_index + - part + type: object + properties: + type: + enum: + - response.reasoning_summary_part.done + type: string + description: "The type of the event. Always `response.reasoning_summary_part.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the item this summary part is associated with.\n" + output_index: + type: integer + description: "The index of the output item this summary part is associated with.\n" + summary_index: + type: integer + description: "The index of the summary part within the reasoning summary.\n" + part: + required: + - type + - text + type: object + properties: + type: + enum: + - summary_text + type: string + description: The type of the summary part. Always `summary_text`. + x-stainless-const: true + text: + type: string + description: The text of the summary part. + description: "The completed summary part.\n" + description: Emitted when a reasoning summary part is completed. + x-oaiMeta: + name: response.reasoning_summary_part.done + group: responses + example: "{ \"type\": \"response.reasoning_summary_part.done\",\n \"item_id\": \"rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476\",\n \"output_index\": 0,\n \"summary_index\": 0,\n \"part\": {\n \"type\": \"summary_text\",\n \"text\": \"**Responding to a greeting**\\n\\nThe user just said, \\\"Hello!\\\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \\\"Hello! How can I assist you today?\\\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!\"\n }\n}\n" + ResponseReasoningSummaryTextDeltaEvent: + required: + - type + - item_id + - output_index + - summary_index + - delta + type: object + properties: + type: + enum: + - response.reasoning_summary_text.delta + type: string + description: "The type of the event. Always `response.reasoning_summary_text.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the item this summary text delta is associated with.\n" + output_index: + type: integer + description: "The index of the output item this summary text delta is associated with.\n" + summary_index: + type: integer + description: "The index of the summary part within the reasoning summary.\n" + delta: + type: string + description: "The text delta that was added to the summary.\n" + description: Emitted when a delta is added to a reasoning summary text. + x-oaiMeta: + name: response.reasoning_summary_text.delta + group: responses + example: "{\n \"type\": \"response.reasoning_summary_text.delta\",\n \"item_id\": \"rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476\",\n \"output_index\": 0,\n \"summary_index\": 0,\n \"delta\": \"**Respond\"\n}\n" + ResponseReasoningSummaryTextDoneEvent: + required: + - type + - item_id + - output_index + - summary_index + - text + type: object + properties: + type: + enum: + - response.reasoning_summary_text.done + type: string + description: "The type of the event. Always `response.reasoning_summary_text.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the item this summary text is associated with.\n" + output_index: + type: integer + description: "The index of the output item this summary text is associated with.\n" + summary_index: + type: integer + description: "The index of the summary part within the reasoning summary.\n" + text: + type: string + description: "The full text of the completed reasoning summary.\n" + description: Emitted when a reasoning summary text is completed. + x-oaiMeta: + name: response.reasoning_summary_text.done + group: responses + example: "{ \"type\": \"response.reasoning_summary_text.done\",\n \"item_id\": \"rs_6806bfca0b2481918a5748308061a2600d3ce51bdffd5476\",\n \"output_index\": 0,\n \"summary_index\": 0,\n \"text\": \"**Responding to a greeting**\\n\\nThe user just said, \\\"Hello!\\\" So, it seems I need to engage. I'll greet them back and offer help since they're looking to chat. I could say something like, \\\"Hello! How can I assist you today?\\\" That feels friendly and open. They didn't ask a specific question, so this approach will work well for starting a conversation. Let's see where it goes from there!\"\n}\n" + ResponseRefusalDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - delta + type: object + properties: + type: + enum: + - response.refusal.delta + type: string + description: "The type of the event. Always `response.refusal.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the refusal text is added to.\n" + output_index: + type: integer + description: "The index of the output item that the refusal text is added to.\n" + content_index: + type: integer + description: "The index of the content part that the refusal text is added to.\n" + delta: + type: string + description: "The refusal text that is added.\n" + description: Emitted when there is a partial refusal text. + x-oaiMeta: + name: response.refusal.delta + group: responses + example: "{\n \"type\": \"response.refusal.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"refusal text so far\"\n}\n" + ResponseRefusalDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - refusal + type: object + properties: + type: + enum: + - response.refusal.done + type: string + description: "The type of the event. Always `response.refusal.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the refusal text is finalized.\n" + output_index: + type: integer + description: "The index of the output item that the refusal text is finalized.\n" + content_index: + type: integer + description: "The index of the content part that the refusal text is finalized.\n" + refusal: + type: string + description: "The refusal text that is finalized.\n" + description: Emitted when refusal text is finalized. + x-oaiMeta: + name: response.refusal.done + group: responses + example: "{\n \"type\": \"response.refusal.done\",\n \"item_id\": \"item-abc\",\n \"output_index\": 1,\n \"content_index\": 2,\n \"refusal\": \"final refusal text\"\n}\n" + ResponseStreamEvent: + anyOf: + - $ref: '#/components/schemas/ResponseAudioDeltaEvent' + - $ref: '#/components/schemas/ResponseAudioDoneEvent' + - $ref: '#/components/schemas/ResponseAudioTranscriptDeltaEvent' + - $ref: '#/components/schemas/ResponseAudioTranscriptDoneEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDeltaEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCodeDoneEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallCompletedEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallInProgressEvent' + - $ref: '#/components/schemas/ResponseCodeInterpreterCallInterpretingEvent' + - $ref: '#/components/schemas/ResponseCompletedEvent' + - $ref: '#/components/schemas/ResponseContentPartAddedEvent' + - $ref: '#/components/schemas/ResponseContentPartDoneEvent' + - $ref: '#/components/schemas/ResponseCreatedEvent' + - $ref: '#/components/schemas/ResponseErrorEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallCompletedEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallInProgressEvent' + - $ref: '#/components/schemas/ResponseFileSearchCallSearchingEvent' + - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDeltaEvent' + - $ref: '#/components/schemas/ResponseFunctionCallArgumentsDoneEvent' + - $ref: '#/components/schemas/ResponseInProgressEvent' + - $ref: '#/components/schemas/ResponseFailedEvent' + - $ref: '#/components/schemas/ResponseIncompleteEvent' + - $ref: '#/components/schemas/ResponseOutputItemAddedEvent' + - $ref: '#/components/schemas/ResponseOutputItemDoneEvent' + - $ref: '#/components/schemas/ResponseReasoningSummaryPartAddedEvent' + - $ref: '#/components/schemas/ResponseReasoningSummaryPartDoneEvent' + - $ref: '#/components/schemas/ResponseReasoningSummaryTextDeltaEvent' + - $ref: '#/components/schemas/ResponseReasoningSummaryTextDoneEvent' + - $ref: '#/components/schemas/ResponseRefusalDeltaEvent' + - $ref: '#/components/schemas/ResponseRefusalDoneEvent' + - $ref: '#/components/schemas/ResponseTextAnnotationDeltaEvent' + - $ref: '#/components/schemas/ResponseTextDeltaEvent' + - $ref: '#/components/schemas/ResponseTextDoneEvent' + - $ref: '#/components/schemas/ResponseWebSearchCallCompletedEvent' + - $ref: '#/components/schemas/ResponseWebSearchCallInProgressEvent' + - $ref: '#/components/schemas/ResponseWebSearchCallSearchingEvent' + discriminator: + propertyName: type + ResponseTextAnnotationDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - annotation_index + - annotation + type: object + properties: + type: + enum: + - response.output_text.annotation.added + type: string + description: "The type of the event. Always `response.output_text.annotation.added`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text annotation was added to.\n" + output_index: + type: integer + description: "The index of the output item that the text annotation was added to.\n" + content_index: + type: integer + description: "The index of the content part that the text annotation was added to.\n" + annotation_index: + type: integer + description: "The index of the annotation that was added.\n" + annotation: + $ref: '#/components/schemas/Annotation' + description: Emitted when a text annotation is added. + x-oaiMeta: + name: response.output_text.annotation.added + group: responses + example: "{\n \"type\": \"response.output_text.annotation.added\",\n \"item_id\": \"msg_abc123\",\n \"output_index\": 1,\n \"content_index\": 0,\n \"annotation_index\": 0,\n \"annotation\": {\n \"type\": \"file_citation\",\n \"index\": 390,\n \"file_id\": \"file-4wDz5b167pAf72nx1h9eiN\",\n \"filename\": \"dragons.pdf\"\n }\n}\n" + ResponseTextDeltaEvent: + required: + - type + - item_id + - output_index + - content_index + - delta + type: object + properties: + type: + enum: + - response.output_text.delta + type: string + description: "The type of the event. Always `response.output_text.delta`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text delta was added to.\n" + output_index: + type: integer + description: "The index of the output item that the text delta was added to.\n" + content_index: + type: integer + description: "The index of the content part that the text delta was added to.\n" + delta: + type: string + description: "The text delta that was added.\n" + description: Emitted when there is an additional text delta. + x-oaiMeta: + name: response.output_text.delta + group: responses + example: "{\n \"type\": \"response.output_text.delta\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"delta\": \"In\"\n}\n" + ResponseTextDoneEvent: + required: + - type + - item_id + - output_index + - content_index + - text + type: object + properties: + type: + enum: + - response.output_text.done + type: string + description: "The type of the event. Always `response.output_text.done`.\n" + x-stainless-const: true + item_id: + type: string + description: "The ID of the output item that the text content is finalized.\n" + output_index: + type: integer + description: "The index of the output item that the text content is finalized.\n" + content_index: + type: integer + description: "The index of the content part that the text content is finalized.\n" + text: + type: string + description: "The text content that is finalized.\n" + description: Emitted when text content is finalized. + x-oaiMeta: + name: response.output_text.done + group: responses + example: "{ \"type\": \"response.output_text.done\",\n \"item_id\": \"msg_123\",\n \"output_index\": 0,\n \"content_index\": 0,\n \"text\": \"In a shimmering forest under a sky full of stars, a lonely unicorn named Lila discovered a hidden pond that glowed with moonlight. Every night, she would leave sparkling, magical flowers by the water's edge, hoping to share her beauty with others. One enchanting evening, she woke to find a group of friendly animals gathered around, eager to be friends and share in her magic.\"\n}\n" + ResponseUsage: + required: + - input_tokens + - input_tokens_details + - output_tokens + - output_tokens_details + - total_tokens + type: object + properties: + input_tokens: + type: integer + description: The number of input tokens. + input_tokens_details: + required: + - cached_tokens + type: object + properties: + cached_tokens: + type: integer + description: "The number of tokens that were retrieved from the cache. \n[More on prompt caching](/docs/guides/prompt-caching).\n" + description: A detailed breakdown of the input tokens. + output_tokens: + type: integer + description: The number of output tokens. + output_tokens_details: + required: + - reasoning_tokens + type: object + properties: + reasoning_tokens: + type: integer + description: The number of reasoning tokens. + description: A detailed breakdown of the output tokens. + total_tokens: + type: integer + description: The total number of tokens used. + description: "Represents token usage details including input tokens, output tokens,\na breakdown of output tokens, and the total tokens used.\n" + ResponseWebSearchCallCompletedEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.web_search_call.completed + type: string + description: "The type of the event. Always `response.web_search_call.completed`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the web search call is associated with.\n" + item_id: + type: string + description: "Unique ID for the output item associated with the web search call.\n" + description: Emitted when a web search call is completed. + x-oaiMeta: + name: response.web_search_call.completed + group: responses + example: "{\n \"type\": \"response.web_search_call.completed\",\n \"output_index\": 0,\n \"item_id\": \"ws_123\",\n}\n" + ResponseWebSearchCallInProgressEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.web_search_call.in_progress + type: string + description: "The type of the event. Always `response.web_search_call.in_progress`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the web search call is associated with.\n" + item_id: + type: string + description: "Unique ID for the output item associated with the web search call.\n" + description: Emitted when a web search call is initiated. + x-oaiMeta: + name: response.web_search_call.in_progress + group: responses + example: "{\n \"type\": \"response.web_search_call.in_progress\",\n \"output_index\": 0,\n \"item_id\": \"ws_123\",\n}\n" + ResponseWebSearchCallSearchingEvent: + required: + - type + - output_index + - item_id + type: object + properties: + type: + enum: + - response.web_search_call.searching + type: string + description: "The type of the event. Always `response.web_search_call.searching`.\n" + x-stainless-const: true + output_index: + type: integer + description: "The index of the output item that the web search call is associated with.\n" + item_id: + type: string + description: "Unique ID for the output item associated with the web search call.\n" + description: Emitted when a web search call is executing. + x-oaiMeta: + name: response.web_search_call.searching + group: responses + example: "{\n \"type\": \"response.web_search_call.searching\",\n \"output_index\": 0,\n \"item_id\": \"ws_123\",\n}\n" + RunCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: "Usage statistics related to the run. This value will be `null` if\nthe run is not in a terminal state (i.e. `in_progress`, `queued`, etc.)." + nullable: true + RunObject: + title: A run on a thread + required: + - id + - object + - created_at + - thread_id + - assistant_id + - status + - required_action + - last_error + - expires_at + - started_at + - cancelled_at + - failed_at + - completed_at + - model + - instructions + - tools + - metadata + - usage + - incomplete_details + - max_prompt_tokens + - max_completion_tokens + - truncation_strategy + - tool_choice + - parallel_tool_calls + - response_format + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread.run + type: string + description: 'The object type, which is always `thread.run`.' + x-stainless-const: true + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was created. + thread_id: + type: string + description: "The ID of the [thread](/docs/api-reference/threads) that was\nexecuted on as a part of this run." + assistant_id: + type: string + description: "The ID of the [assistant](/docs/api-reference/assistants) used for\nexecution of this run." + status: + enum: + - queued + - in_progress + - requires_action + - cancelling + - cancelled + - failed + - completed + - incomplete + - expired + type: string + description: "The status of the run, which can be either `queued`, `in_progress`,\n`requires_action`, `cancelling`, `cancelled`, `failed`, `completed`,\n`incomplete`, or `expired`." + required_action: + required: + - type + - submit_tool_outputs + type: object + properties: + type: + enum: + - submit_tool_outputs + type: string + description: 'For now, this is always `submit_tool_outputs`.' + x-stainless-const: true + submit_tool_outputs: + required: + - tool_calls + type: object + properties: + tool_calls: + type: array + items: + $ref: '#/components/schemas/RunToolCallObject' + description: A list of the relevant tool calls. + description: Details on the tool outputs needed for this run to continue. + description: "Details on the action required to continue the run. Will be `null`\nif no action is required." + nullable: true + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + - invalid_prompt + type: string + description: 'One of `server_error`, `rate_limit_exceeded`, or `invalid_prompt`.' + message: + type: string + description: A human-readable description of the error. + description: "The last error associated with this run. Will be `null` if there\nare no errors." + nullable: true + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the run will expire. + nullable: true + started_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was started. + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run was completed. + nullable: true + incomplete_details: + type: object + properties: + reason: + enum: + - max_completion_tokens + - max_prompt_tokens + type: string + description: "The reason why the run is incomplete. This will point to which\nspecific token limit was reached over the course of the run." + description: "Details on why the run is incomplete. Will be `null` if the run is\nnot incomplete." + nullable: true + model: + type: string + description: "The model that the [assistant](/docs/api-reference/assistants) used\nfor this run." + instructions: + type: string + description: "The instructions that the\n[assistant](/docs/api-reference/assistants) used for this run." + tools: + maxItems: 20 + type: array + items: + oneOf: + - $ref: '#/components/schemas/AssistantToolsCode' + - $ref: '#/components/schemas/AssistantToolsFileSearch' + - $ref: '#/components/schemas/AssistantToolsFunction' + description: "The list of tools that the\n[assistant](/docs/api-reference/assistants) used for this run." + metadata: + $ref: '#/components/schemas/Metadata' + usage: + $ref: '#/components/schemas/RunCompletionUsage' + temperature: + type: number + description: "The sampling temperature used for this run. If not set, defaults to\n1." + nullable: true + top_p: + type: number + description: "The nucleus sampling value used for this run. If not set, defaults\nto 1." + nullable: true + max_prompt_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of prompt tokens specified to have been used over the course of the run.\n" + nullable: true + max_completion_tokens: + minimum: 256.0 + type: integer + description: "The maximum number of completion tokens specified to have been used over the course of the run.\n" + nullable: true + truncation_strategy: + allOf: + - $ref: '#/components/schemas/TruncationObject' + - nullable: true + tool_choice: + allOf: + - $ref: '#/components/schemas/AssistantsApiToolChoiceOption' + - nullable: true + parallel_tool_calls: + $ref: '#/components/schemas/ParallelToolCalls' + response_format: + $ref: '#/components/schemas/AssistantsApiResponseFormatOption' + description: 'Represents an execution run on a [thread](/docs/api-reference/threads).' + x-oaiMeta: + name: The run object + beta: true + example: "{\n \"id\": \"run_abc123\",\n \"object\": \"thread.run\",\n \"created_at\": 1698107661,\n \"assistant_id\": \"asst_abc123\",\n \"thread_id\": \"thread_abc123\",\n \"status\": \"completed\",\n \"started_at\": 1699073476,\n \"expires_at\": null,\n \"cancelled_at\": null,\n \"failed_at\": null,\n \"completed_at\": 1699073498,\n \"last_error\": null,\n \"model\": \"gpt-4o\",\n \"instructions\": null,\n \"tools\": [{\"type\": \"file_search\"}, {\"type\": \"code_interpreter\"}],\n \"metadata\": {},\n \"incomplete_details\": null,\n \"usage\": {\n \"prompt_tokens\": 123,\n \"completion_tokens\": 456,\n \"total_tokens\": 579\n },\n \"temperature\": 1.0,\n \"top_p\": 1.0,\n \"max_prompt_tokens\": 1000,\n \"max_completion_tokens\": 1000,\n \"truncation_strategy\": {\n \"type\": \"auto\",\n \"last_messages\": null\n },\n \"response_format\": \"auto\",\n \"tool_choice\": \"auto\",\n \"parallel_tool_calls\": true\n}\n" + RunStepCompletionUsage: + required: + - prompt_tokens + - completion_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: Number of completion tokens used over the course of the run step. + prompt_tokens: + type: integer + description: Number of prompt tokens used over the course of the run step. + total_tokens: + type: integer + description: Total number of tokens used (prompt + completion). + description: "Usage statistics related to the run step. This value will be `null`\nwhile the run step's status is `in_progress`." + nullable: true + RunStepDeltaObject: + title: Run step delta object + required: + - id + - object + - delta + type: object + properties: + id: + type: string + description: "The identifier of the run step, which can be referenced in API\nendpoints." + object: + enum: + - thread.run.step.delta + type: string + description: 'The object type, which is always `thread.run.step.delta`.' + x-stainless-const: true + delta: + type: object + properties: + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/RunStepDeltaStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsObject' + description: The details of the run step. + description: The delta containing the fields that have changed on the run step. + description: "Represents a run step delta i.e. any changed fields on a run step during streaming.\n" + x-oaiMeta: + name: The run step delta object + beta: true + example: "{\n \"id\": \"step_123\",\n \"object\": \"thread.run.step.delta\",\n \"delta\": {\n \"step_details\": {\n \"type\": \"tool_calls\",\n \"tool_calls\": [\n {\n \"index\": 0,\n \"id\": \"call_123\",\n \"type\": \"code_interpreter\",\n \"code_interpreter\": { \"input\": \"\", \"outputs\": [] }\n }\n ]\n }\n }\n}\n" + RunStepDeltaStepDetailsMessageCreationObject: + title: Message creation + required: + - type + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation`. + x-stainless-const: true + message_creation: + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + RunStepDeltaStepDetailsToolCallsCodeObject: + title: Code interpreter tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: "The type of tool call. This is always going to be\n`code_interpreter` for this type of tool call." + x-stainless-const: true + code_interpreter: + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeOutputImageObject' + description: "The outputs from the Code Interpreter tool call. Code Interpreter\ncan output one or more items, including text (`logs`) or images\n(`image`). Each of these are represented by a different object\ntype." + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + RunStepDeltaStepDetailsToolCallsCodeOutputImageObject: + title: Code interpreter image output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - image + type: string + description: Always `image`. + x-stainless-const: true + image: + type: object + properties: + file_id: + type: string + description: 'The [file](/docs/api-reference/files) ID of the image.' + RunStepDeltaStepDetailsToolCallsCodeOutputLogsObject: + title: Code interpreter log output + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the output in the outputs array. + type: + enum: + - logs + type: string + description: Always `logs`. + x-stainless-const: true + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + RunStepDeltaStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - index + - type + - file_search + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: "The type of tool call. This is always going to be `file_search` for\nthis type of tool call." + x-stainless-const: true + file_search: + type: object + description: 'For now, this is always going to be an empty object.' + x-oaiTypeLabel: map + RunStepDeltaStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - index + - type + type: object + properties: + index: + type: integer + description: The index of the tool call in the tool calls array. + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: "The type of tool call. This is always going to be `function` for\nthis type of tool call." + x-stainless-const: true + function: + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: "The output of the function. This will be `null` if the outputs have\nnot been [submitted](/docs/api-reference/runs/submitToolOutputs)\nyet." + nullable: true + description: The definition of the function that was called. + RunStepDeltaStepDetailsToolCallsObject: + title: Tool calls + required: + - type + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-stainless-const: true + tool_calls: + type: array + items: + oneOf: + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/RunStepDeltaStepDetailsToolCallsFunctionObject' + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.\n" + description: Details of the tool call. + RunStepDetailsMessageCreationObject: + title: Message creation + required: + - type + - message_creation + type: object + properties: + type: + enum: + - message_creation + type: string + description: Always `message_creation`. + x-stainless-const: true + message_creation: + required: + - message_id + type: object + properties: + message_id: + type: string + description: The ID of the message that was created by this run step. + description: Details of the message creation by the run step. + RunStepDetailsToolCallsCodeObject: + title: Code Interpreter tool call + required: + - id + - type + - code_interpreter + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + enum: + - code_interpreter + type: string + description: "The type of tool call. This is always going to be\n`code_interpreter` for this type of tool call." + x-stainless-const: true + code_interpreter: + required: + - input + - outputs + type: object + properties: + input: + type: string + description: The input to the Code Interpreter tool call. + outputs: + type: array + items: + type: object + oneOf: + - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputLogsObject' + - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeOutputImageObject' + description: "The outputs from the Code Interpreter tool call. Code Interpreter\ncan output one or more items, including text (`logs`) or images\n(`image`). Each of these are represented by a different object\ntype." + description: The Code Interpreter tool call definition. + description: Details of the Code Interpreter tool call the run step was involved in. + RunStepDetailsToolCallsCodeOutputImageObject: + title: Code Interpreter image output + required: + - type + - image + type: object + properties: + type: + enum: + - image + type: string + description: Always `image`. + x-stainless-const: true + image: + required: + - file_id + type: object + properties: + file_id: + type: string + description: 'The [file](/docs/api-reference/files) ID of the image.' + RunStepDetailsToolCallsCodeOutputLogsObject: + title: Code Interpreter log output + required: + - type + - logs + type: object + properties: + type: + enum: + - logs + type: string + description: Always `logs`. + x-stainless-const: true + logs: + type: string + description: The text output from the Code Interpreter tool call. + description: Text output from the Code Interpreter tool call as part of a run step. + RunStepDetailsToolCallsFileSearchObject: + title: File search tool call + required: + - id + - type + - file_search + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - file_search + type: string + description: "The type of tool call. This is always going to be `file_search` for\nthis type of tool call." + x-stainless-const: true + file_search: + type: object + properties: + ranking_options: + $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchRankingOptionsObject' + results: + type: array + items: + $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchResultObject' + description: The results of the file search. + description: 'For now, this is always going to be an empty object.' + x-oaiTypeLabel: map + RunStepDetailsToolCallsFileSearchRankingOptionsObject: + title: File search tool call ranking options + required: + - ranker + - score_threshold + type: object + properties: + ranker: + $ref: '#/components/schemas/FileSearchRanker' + score_threshold: + maximum: 1.0 + minimum: 0.0 + type: number + description: "The score threshold for the file search. All values must be a\nfloating point number between 0 and 1." + description: The ranking options for the file search. + RunStepDetailsToolCallsFileSearchResultObject: + title: File search tool call result + required: + - file_id + - file_name + - score + type: object + properties: + file_id: + type: string + description: The ID of the file that result was found in. + file_name: + type: string + description: The name of the file that result was found in. + score: + maximum: 1.0 + minimum: 0.0 + type: number + description: "The score of the result. All values must be a floating point number\nbetween 0 and 1." + content: + type: array + items: + type: object + properties: + type: + enum: + - text + type: string + description: The type of the content. + x-stainless-const: true + text: + type: string + description: The text content of the file. + description: "The content of the result that was found. The content is only\nincluded if requested via the include query parameter." + description: A result instance of the file search. + x-oaiTypeLabel: map + RunStepDetailsToolCallsFunctionObject: + title: Function tool call + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call object. + type: + enum: + - function + type: string + description: "The type of tool call. This is always going to be `function` for\nthis type of tool call." + x-stainless-const: true + function: + required: + - name + - arguments + - output + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments passed to the function. + output: + type: string + description: "The output of the function. This will be `null` if the outputs have\nnot been [submitted](/docs/api-reference/runs/submitToolOutputs)\nyet." + nullable: true + description: The definition of the function that was called. + RunStepDetailsToolCallsObject: + title: Tool calls + required: + - type + - tool_calls + type: object + properties: + type: + enum: + - tool_calls + type: string + description: Always `tool_calls`. + x-stainless-const: true + tool_calls: + type: array + items: + oneOf: + - $ref: '#/components/schemas/RunStepDetailsToolCallsCodeObject' + - $ref: '#/components/schemas/RunStepDetailsToolCallsFileSearchObject' + - $ref: '#/components/schemas/RunStepDetailsToolCallsFunctionObject' + description: "An array of tool calls the run step was involved in. These can be associated with one of three types of tools: `code_interpreter`, `file_search`, or `function`.\n" + description: Details of the tool call. + RunStepObject: + title: Run steps + required: + - id + - object + - created_at + - assistant_id + - thread_id + - run_id + - type + - status + - step_details + - last_error + - expired_at + - cancelled_at + - failed_at + - completed_at + - metadata + - usage + type: object + properties: + id: + type: string + description: "The identifier of the run step, which can be referenced in API\nendpoints." + object: + enum: + - thread.run.step + type: string + description: 'The object type, which is always `thread.run.step`.' + x-stainless-const: true + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was created. + assistant_id: + type: string + description: "The ID of the [assistant](/docs/api-reference/assistants)\nassociated with the run step." + thread_id: + type: string + description: 'The ID of the [thread](/docs/api-reference/threads) that was run.' + run_id: + type: string + description: "The ID of the [run](/docs/api-reference/runs) that this run step is\na part of." + type: + enum: + - message_creation + - tool_calls + type: string + description: "The type of run step, which can be either `message_creation` or\n`tool_calls`." + status: + enum: + - in_progress + - cancelled + - failed + - completed + - expired + type: string + description: "The status of the run step, which can be either `in_progress`,\n`cancelled`, `failed`, `completed`, or `expired`." + step_details: + type: object + oneOf: + - $ref: '#/components/schemas/RunStepDetailsMessageCreationObject' + - $ref: '#/components/schemas/RunStepDetailsToolCallsObject' + description: The details of the run step. + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - rate_limit_exceeded + type: string + description: One of `server_error` or `rate_limit_exceeded`. + message: + type: string + description: A human-readable description of the error. + description: "The last error associated with this run step. Will be `null` if\nthere are no errors." + nullable: true + expired_at: + type: integer + description: "The Unix timestamp (in seconds) for when the run step expired. A\nstep is considered expired if the parent run is expired." + nullable: true + cancelled_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step was cancelled. + nullable: true + failed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step failed. + nullable: true + completed_at: + type: integer + description: The Unix timestamp (in seconds) for when the run step completed. + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + usage: + $ref: '#/components/schemas/RunStepCompletionUsage' + description: "Represents a step in execution of a run.\n" + x-oaiMeta: + name: The run step object + beta: true + example: "{\n \"id\": \"step_abc123\",\n \"object\": \"thread.run.step\",\n \"created_at\": 1699063291,\n \"run_id\": \"run_abc123\",\n \"assistant_id\": \"asst_abc123\",\n \"thread_id\": \"thread_abc123\",\n \"type\": \"message_creation\",\n \"status\": \"completed\",\n \"cancelled_at\": null,\n \"completed_at\": 1699063291,\n \"expired_at\": null,\n \"failed_at\": null,\n \"last_error\": null,\n \"step_details\": {\n \"type\": \"message_creation\",\n \"message_creation\": {\n \"message_id\": \"msg_abc123\"\n }\n },\n \"usage\": {\n \"prompt_tokens\": 123,\n \"completion_tokens\": 456,\n \"total_tokens\": 579\n }\n}\n" + RunStepStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.created + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nis created." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.in_progress + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nmoves to an `in_progress` state." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.delta + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepDeltaObject' + description: 'Occurs when parts of a [run step](/docs/api-reference/run-steps/step-object) are being streamed.' + x-oaiMeta: + dataDescription: '`data` is a [run step delta](/docs/api-reference/assistants-streaming/run-step-delta-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.completed + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nis completed." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.failed + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nfails." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.cancelled + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nis cancelled." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.step.expired + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunStepObject' + description: "Occurs when a [run step](/docs/api-reference/run-steps/step-object)\nexpires." + x-oaiMeta: + dataDescription: '`data` is a [run step](/docs/api-reference/run-steps/step-object)' + RunStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.created + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: 'Occurs when a new [run](/docs/api-reference/runs/object) is created.' + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.queued + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: "Occurs when a [run](/docs/api-reference/runs/object) moves to a\n`queued` status." + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.in_progress + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: "Occurs when a [run](/docs/api-reference/runs/object) moves to an\n`in_progress` status." + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.requires_action + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: "Occurs when a [run](/docs/api-reference/runs/object) moves to a\n`requires_action` status." + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.completed + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: 'Occurs when a [run](/docs/api-reference/runs/object) is completed.' + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.incomplete + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: "Occurs when a [run](/docs/api-reference/runs/object) ends with\nstatus `incomplete`." + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.failed + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: 'Occurs when a [run](/docs/api-reference/runs/object) fails.' + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelling + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: "Occurs when a [run](/docs/api-reference/runs/object) moves to a\n`cancelling` status." + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.cancelled + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: 'Occurs when a [run](/docs/api-reference/runs/object) is cancelled.' + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + - required: + - event + - data + type: object + properties: + event: + enum: + - thread.run.expired + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/RunObject' + description: 'Occurs when a [run](/docs/api-reference/runs/object) expires.' + x-oaiMeta: + dataDescription: '`data` is a [run](/docs/api-reference/runs/object)' + RunToolCallObject: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: "The ID of the tool call. This ID must be referenced when you submit\nthe tool outputs in using the [Submit tool outputs to\nrun](/docs/api-reference/runs/submitToolOutputs) endpoint." + type: + enum: + - function + type: string + description: "The type of tool call the output is required for. For now, this is\nalways `function`." + x-stainless-const: true + function: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function. + arguments: + type: string + description: The arguments that the model expects you to pass to the function. + description: The function definition. + description: Tool call objects + Screenshot: + title: Screenshot + required: + - type + type: object + properties: + type: + enum: + - screenshot + type: string + description: "Specifies the event type. For a screenshot action, this property is \nalways set to `screenshot`.\n" + default: screenshot + x-stainless-const: true + description: "A screenshot action.\n" + Scroll: + title: Scroll + required: + - type + - x + - y + - scroll_x + - scroll_y + type: object + properties: + type: + enum: + - scroll + type: string + description: "Specifies the event type. For a scroll action, this property is \nalways set to `scroll`.\n" + default: scroll + x-stainless-const: true + x: + type: integer + description: "The x-coordinate where the scroll occurred.\n" + y: + type: integer + description: "The y-coordinate where the scroll occurred.\n" + scroll_x: + type: integer + description: "The horizontal scroll distance.\n" + scroll_y: + type: integer + description: "The vertical scroll distance.\n" + description: "A scroll action.\n" + ServiceTier: + enum: + - auto + - default + - flex + type: string + description: "Specifies the latency tier to use for processing the request. This parameter is relevant for customers subscribed to the scale tier service: - If set to 'auto', and the Project is Scale tier enabled, the system\n will utilize scale tier credits until they are exhausted.\n - If set to 'auto', and the Project is not Scale tier enabled, the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee.\n - If set to 'default', the request will be processed using the default service tier with a lower uptime SLA and no latency guarentee.\n - If set to 'flex', the request will be processed with the Flex Processing service tier. [Learn more](/docs/guides/flex-processing).\n - When not set, the default behavior is 'auto'.\n\n When this parameter is set, the response body will include the `service_tier` utilized.\n" + default: auto + nullable: true + StaticChunkingStrategy: + required: + - max_chunk_size_tokens + - chunk_overlap_tokens + type: object + properties: + max_chunk_size_tokens: + maximum: 4096.0 + minimum: 100.0 + type: integer + description: "The maximum number of tokens in each chunk. The default value is\n`800`. The minimum value is `100` and the maximum value is `4096`." + chunk_overlap_tokens: + type: integer + description: "The number of tokens that overlap between chunks. The default value is `400`.\n\nNote that the overlap must not exceed half of `max_chunk_size_tokens`.\n" + additionalProperties: false + StaticChunkingStrategyRequestParam: + title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-stainless-const: true + static: + $ref: '#/components/schemas/StaticChunkingStrategy' + additionalProperties: false + description: "Customize your own chunking strategy by setting chunk size and\nchunk overlap." + StaticChunkingStrategyResponseParam: + title: Static Chunking Strategy + required: + - type + - static + type: object + properties: + type: + enum: + - static + type: string + description: Always `static`. + x-stainless-const: true + static: + $ref: '#/components/schemas/StaticChunkingStrategy' + additionalProperties: false + StopConfiguration: + oneOf: + - type: string + default: <|endoftext|> + nullable: true + example: "\n" + - maxItems: 4 + minItems: 1 + type: array + items: + type: string + example: '["\n"]' + description: "Not supported with latest reasoning models `o3` and `o4-mini`.\n\nUp to 4 sequences where the API will stop generating further tokens. The\nreturned text will not contain the stop sequence.\n" + default: + nullable: true + SubmitToolOutputsRunRequest: + required: + - tool_outputs + type: object + properties: + tool_outputs: + type: array + items: + type: object + properties: + tool_call_id: + type: string + description: "The ID of the tool call in the `required_action` object within the\nrun object the output is being submitted for." + output: + type: string + description: The output of the tool call to be submitted to continue the run. + description: A list of tools for which the outputs are being submitted. + stream: + type: boolean + description: "If `true`, returns a stream of events that happen during the Run as server-sent events, terminating when the Run enters a terminal state with a `data: [DONE]` message.\n" + nullable: true + additionalProperties: false + TextResponseFormatConfiguration: + oneOf: + - $ref: '#/components/schemas/ResponseFormatText' + - $ref: '#/components/schemas/TextResponseFormatJsonSchema' + - $ref: '#/components/schemas/ResponseFormatJsonObject' + description: "An object specifying the format that the model must output.\n\nConfiguring `{ \"type\": \"json_schema\" }` enables Structured Outputs,\nwhich ensures the model will match your supplied JSON schema. Learn more in the\n[Structured Outputs guide](/docs/guides/structured-outputs).\n\nThe default format is `{ \"type\": \"text\" }` with no additional options.\n\n**Not recommended for gpt-4o and newer models:**\n\nSetting to `{ \"type\": \"json_object\" }` enables the older JSON mode, which\nensures the message the model generates is valid JSON. Using `json_schema`\nis preferred for models that support it.\n" + TextResponseFormatJsonSchema: + title: JSON schema + required: + - type + - schema + - name + type: object + properties: + type: + enum: + - json_schema + type: string + description: The type of response format being defined. Always `json_schema`. + x-stainless-const: true + description: + type: string + description: "A description of what the response format is for, used by the model to\ndetermine how to respond in the format.\n" + name: + type: string + description: "The name of the response format. Must be a-z, A-Z, 0-9, or contain\nunderscores and dashes, with a maximum length of 64.\n" + schema: + $ref: '#/components/schemas/ResponseFormatJsonSchemaSchema' + strict: + type: boolean + description: "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined\nin the `schema` field. Only a subset of JSON Schema is supported when\n`strict` is `true`. To learn more, read the [Structured Outputs\nguide](/docs/guides/structured-outputs).\n" + default: false + nullable: true + description: "JSON Schema response format. Used to generate structured JSON responses.\nLearn more about [Structured Outputs](/docs/guides/structured-outputs).\n" + ThreadObject: + title: Thread + required: + - id + - object + - created_at + - tool_resources + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - thread + type: string + description: 'The object type, which is always `thread`.' + x-stainless-const: true + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the thread was created. + tool_resources: + type: object + properties: + code_interpreter: + type: object + properties: + file_ids: + maxItems: 20 + type: array + items: + type: string + description: "A list of [file](/docs/api-reference/files) IDs made available to the `code_interpreter` tool. There can be a maximum of 20 files associated with the tool.\n" + file_search: + type: object + properties: + vector_store_ids: + maxItems: 1 + type: array + items: + type: string + description: "The [vector store](/docs/api-reference/vector-stores/object) attached to this thread. There can be a maximum of 1 vector store attached to the thread.\n" + description: "A set of resources that are made available to the assistant's tools in this thread. The resources are specific to the type of tool. For example, the `code_interpreter` tool requires a list of file IDs, while the `file_search` tool requires a list of vector store IDs.\n" + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + description: "Represents a thread that contains\n[messages](/docs/api-reference/messages)." + x-oaiMeta: + name: The thread object + beta: true + example: "{\n \"id\": \"thread_abc123\",\n \"object\": \"thread\",\n \"created_at\": 1698107661,\n \"metadata\": {}\n}\n" + ThreadStreamEvent: + oneOf: + - required: + - event + - data + type: object + properties: + enabled: + type: boolean + description: Whether to enable input audio transcription. + event: + enum: + - thread.created + type: string + x-stainless-const: true + data: + $ref: '#/components/schemas/ThreadObject' + description: "Occurs when a new [thread](/docs/api-reference/threads/object) is\ncreated." + x-oaiMeta: + dataDescription: '`data` is a [thread](/docs/api-reference/threads/object)' + ToggleCertificatesRequest: + required: + - certificate_ids + type: object + properties: + certificate_ids: + maxItems: 10 + minItems: 1 + type: array + items: + type: string + example: cert_abc + ToolChoiceFunction: + title: Function tool + required: + - type + - name + type: object + properties: + type: + enum: + - function + type: string + description: 'For function calling, the type is always `function`.' + x-stainless-const: true + name: + type: string + description: The name of the function to call. + description: "Use this option to force the model to call a specific function.\n" + ToolChoiceOptions: + title: Tool choice mode + enum: + - none + - auto + - required + type: string + description: "Controls which (if any) tool is called by the model.\n\n`none` means the model will not call any tool and instead generates a message.\n\n`auto` means the model can pick between generating a message or calling one or\nmore tools.\n\n`required` means the model must call one or more tools.\n" + ToolChoiceTypes: + title: Hosted tool + required: + - type + type: object + properties: + type: + enum: + - file_search + - web_search_preview + - computer_use_preview + - web_search_preview_2025_03_11 + type: string + description: "The type of hosted tool the model should to use. Learn more about\n[built-in tools](/docs/guides/tools).\n\nAllowed values are:\n- `file_search`\n- `web_search_preview`\n- `computer_use_preview`\n" + description: "Indicates that the model should use a built-in tool to generate a response.\n[Learn more about built-in tools](/docs/guides/tools).\n" + TranscriptTextDeltaEvent: + required: + - type + - delta + type: object + properties: + type: + enum: + - transcript.text.delta + type: string + description: "The type of the event. Always `transcript.text.delta`.\n" + x-stainless-const: true + delta: + type: string + description: "The text delta that was additionally transcribed.\n" + logprobs: + type: array + items: + type: object + properties: + token: + type: string + description: "The token that was used to generate the log probability.\n" + logprob: + type: number + description: "The log probability of the token.\n" + bytes: + type: array + items: + type: byte + description: "The bytes that were used to generate the log probability.\n" + description: "The log probabilities of the delta. Only included if you [create a transcription](/docs/api-reference/audio/create-transcription) with the `include[]` parameter set to `logprobs`.\n" + description: "Emitted when there is an additional text delta. This is also the\nfirst event emitted when the transcription starts. Only emitted when you\n[create a transcription](/docs/api-reference/audio/create-transcription)\nwith the `Stream` parameter set to `true`." + x-oaiMeta: + name: Stream Event (transcript.text.delta) + group: transcript + example: "{\n \"type\": \"transcript.text.delta\",\n \"delta\": \" wonderful\"\n}\n" + TranscriptTextDoneEvent: + required: + - type + - text + type: object + properties: + type: + enum: + - transcript.text.done + type: string + description: "The type of the event. Always `transcript.text.done`.\n" + x-stainless-const: true + text: + type: string + description: "The text that was transcribed.\n" + logprobs: + type: array + items: + type: object + properties: + token: + type: string + description: "The token that was used to generate the log probability.\n" + logprob: + type: number + description: "The log probability of the token.\n" + bytes: + type: array + items: + type: byte + description: "The bytes that were used to generate the log probability.\n" + description: "The log probabilities of the individual tokens in the transcription. Only included if you [create a transcription](/docs/api-reference/audio/create-transcription) with the `include[]` parameter set to `logprobs`.\n" + description: "Emitted when the transcription is complete. Contains the complete\ntranscription text. Only emitted when you [create a\ntranscription](/docs/api-reference/audio/create-transcription) with the\n`Stream` parameter set to `true`." + x-oaiMeta: + name: Stream Event (transcript.text.done) + group: transcript + example: "{ \"type\": \"transcript.text.done\",\n \"text\": \"I see skies of blue and clouds of white, the bright blessed days, the dark sacred nights, and I think to myself, what a wonderful world.\"\n}\n" + TranscriptionInclude: + enum: + - logprobs + type: string + TranscriptionSegment: + required: + - id + - seek + - start + - end + - text + - tokens + - temperature + - avg_logprob + - compression_ratio + - no_speech_prob + type: object + properties: + id: + type: integer + description: Unique identifier of the segment. + seek: + type: integer + description: Seek offset of the segment. + start: + type: number + description: Start time of the segment in seconds. + format: float + end: + type: number + description: End time of the segment in seconds. + format: float + text: + type: string + description: Text content of the segment. + tokens: + type: array + items: + type: integer + description: Array of token IDs for the text content. + temperature: + type: number + description: Temperature parameter used for generating the segment. + format: float + avg_logprob: + type: number + description: "Average logprob of the segment. If the value is lower than -1,\nconsider the logprobs failed." + format: float + compression_ratio: + type: number + description: "Compression ratio of the segment. If the value is greater than 2.4,\nconsider the compression failed." + format: float + no_speech_prob: + type: number + description: "Probability of no speech in the segment. If the value is higher\nthan 1.0 and the `avg_logprob` is below -1, consider this segment\nsilent." + format: float + TranscriptionWord: + required: + - word + - start + - end + type: object + properties: + word: + type: string + description: The text content of the word. + start: + type: number + description: Start time of the word in seconds. + format: float + end: + type: number + description: End time of the word in seconds. + format: float + TruncationObject: + title: Thread Truncation Controls + required: + - type + type: object + properties: + type: + enum: + - auto + - last_messages + type: string + description: "The truncation strategy to use for the thread. The default is\n`auto`. If set to `last_messages`, the thread will be truncated to\nthe n most recent messages in the thread. When set to `auto`,\nmessages in the middle of the thread will be dropped to fit the\ncontext length of the model, `max_prompt_tokens`." + last_messages: + minimum: 1.0 + type: integer + description: "The number of most recent messages from the thread when\nconstructing the context for the run." + nullable: true + description: "Controls for how a thread will be truncated prior to the run. Use\nthis to control the intial context window of the run." + Type: + title: Type + required: + - type + - text + type: object + properties: + type: + enum: + - type + type: string + description: "Specifies the event type. For a type action, this property is \nalways set to `type`.\n" + default: type + x-stainless-const: true + text: + type: string + description: "The text to type.\n" + description: "An action to type in text.\n" + UpdateVectorStoreFileAttributesRequest: + required: + - attributes + type: object + properties: + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + additionalProperties: false + x-oaiMeta: + name: Update vector store file attributes request + UpdateVectorStoreRequest: + type: object + properties: + name: + type: string + description: The name of the vector store. + nullable: true + expires_after: + allOf: + - $ref: '#/components/schemas/VectorStoreExpirationAfter' + - nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + additionalProperties: false + Upload: + title: Upload + required: + - bytes + - created_at + - expires_at + - filename + - id + - purpose + - status + type: object + properties: + id: + type: string + description: "The Upload unique identifier, which can be referenced in API\nendpoints." + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the Upload was created. + filename: + type: string + description: The name of the file to be uploaded. + bytes: + type: integer + description: The intended number of bytes to be uploaded. + purpose: + type: string + description: "The intended purpose of the file. [Please refer\nhere](/docs/api-reference/files/object#files/object-purpose) for\nacceptable values." + status: + enum: + - pending + - completed + - cancelled + - expired + type: string + description: The status of the Upload. + expires_at: + type: integer + description: The Unix timestamp (in seconds) for when the Upload will expire. + object: + enum: + - upload + type: string + description: 'The object type, which is always "upload".' + x-stainless-const: true + file: + allOf: + - $ref: '#/components/schemas/OpenAIFile' + - description: The ready File object after the Upload is completed. + nullable: true + description: "The Upload object can accept byte chunks in the form of Parts.\n" + x-oaiMeta: + name: The upload object + example: "{\n \"id\": \"upload_abc123\",\n \"object\": \"upload\",\n \"bytes\": 2147483648,\n \"created_at\": 1719184911,\n \"filename\": \"training_examples.jsonl\",\n \"purpose\": \"fine-tune\",\n \"status\": \"completed\",\n \"expires_at\": 1719127296,\n \"file\": {\n \"id\": \"file-xyz321\",\n \"object\": \"file\",\n \"bytes\": 2147483648,\n \"created_at\": 1719186911,\n \"filename\": \"training_examples.jsonl\",\n \"purpose\": \"fine-tune\",\n }\n}\n" + UploadCertificateRequest: + required: + - content + type: object + properties: + name: + type: string + description: An optional name for the certificate + content: + type: string + description: The certificate content in PEM format + UploadPart: + title: UploadPart + required: + - created_at + - id + - object + - upload_id + type: object + properties: + id: + type: string + description: "The upload Part unique identifier, which can be referenced in API\nendpoints." + created_at: + type: integer + description: The Unix timestamp (in seconds) for when the Part was created. + upload_id: + type: string + description: The ID of the Upload object that this Part was added to. + object: + enum: + - upload.part + type: string + description: 'The object type, which is always `upload.part`.' + x-stainless-const: true + description: "The upload Part represents a chunk of bytes we can add to an Upload object.\n" + x-oaiMeta: + name: The upload part object + example: "{\n \"id\": \"part_def456\",\n \"object\": \"upload.part\",\n \"created_at\": 1719186911,\n \"upload_id\": \"upload_abc123\"\n}\n" + UsageAudioSpeechesResult: + required: + - object + - characters + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.audio_speeches.result + type: string + x-stainless-const: true + characters: + type: integer + description: The number of characters processed. + num_model_requests: + type: integer + description: The count of requests made to the model. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + description: The aggregated audio speeches usage details of the specific time bucket. + x-oaiMeta: + name: Audio speeches usage object + example: "{\n \"object\": \"organization.usage.audio_speeches.result\",\n \"characters\": 45,\n \"num_model_requests\": 1,\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"tts-1\"\n}\n" + UsageAudioTranscriptionsResult: + required: + - object + - seconds + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.audio_transcriptions.result + type: string + x-stainless-const: true + seconds: + type: integer + description: The number of seconds processed. + num_model_requests: + type: integer + description: The count of requests made to the model. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + description: "The aggregated audio transcriptions usage details of the specific\ntime bucket." + x-oaiMeta: + name: Audio transcriptions usage object + example: "{\n \"object\": \"organization.usage.audio_transcriptions.result\",\n \"seconds\": 10,\n \"num_model_requests\": 1,\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"tts-1\"\n}\n" + UsageCodeInterpreterSessionsResult: + required: + - object + - sessions + type: object + properties: + object: + enum: + - organization.usage.code_interpreter_sessions.result + type: string + x-stainless-const: true + num_sessions: + type: integer + description: The number of code interpreter sessions. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + description: "The aggregated code interpreter sessions usage details of the\nspecific time bucket." + x-oaiMeta: + name: Code interpreter sessions usage object + example: "{\n \"object\": \"organization.usage.code_interpreter_sessions.result\",\n \"num_sessions\": 1,\n \"project_id\": \"proj_abc\"\n}\n" + UsageCompletionsResult: + required: + - object + - input_tokens + - output_tokens + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.completions.result + type: string + x-stainless-const: true + input_tokens: + type: integer + description: "The aggregated number of text input tokens used, including cached\ntokens. For customers subscribe to scale tier, this includes scale\ntier tokens." + input_cached_tokens: + type: integer + description: "The aggregated number of text input tokens that has been cached\nfrom previous requests. For customers subscribe to scale tier, this\nincludes scale tier tokens." + output_tokens: + type: integer + description: "The aggregated number of text output tokens used. For customers\nsubscribe to scale tier, this includes scale tier tokens." + input_audio_tokens: + type: integer + description: "The aggregated number of audio input tokens used, including cached\ntokens." + output_audio_tokens: + type: integer + description: The aggregated number of audio output tokens used. + num_model_requests: + type: integer + description: The count of requests made to the model. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + batch: + type: boolean + description: "When `group_by=batch`, this field tells whether the grouped usage\nresult is batch or not." + nullable: true + description: The aggregated completions usage details of the specific time bucket. + x-oaiMeta: + name: Completions usage object + example: "{\n \"object\": \"organization.usage.completions.result\",\n \"input_tokens\": 5000,\n \"output_tokens\": 1000,\n \"input_cached_tokens\": 4000,\n \"input_audio_tokens\": 300,\n \"output_audio_tokens\": 200,\n \"num_model_requests\": 5,\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"batch\": false\n}\n" + UsageEmbeddingsResult: + required: + - object + - input_tokens + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.embeddings.result + type: string + x-stainless-const: true + input_tokens: + type: integer + description: The aggregated number of input tokens used. + num_model_requests: + type: integer + description: The count of requests made to the model. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + description: The aggregated embeddings usage details of the specific time bucket. + x-oaiMeta: + name: Embeddings usage object + example: "{\n \"object\": \"organization.usage.embeddings.result\",\n \"input_tokens\": 20,\n \"num_model_requests\": 2,\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"text-embedding-ada-002-v2\"\n}\n" + UsageImagesResult: + required: + - object + - images + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.images.result + type: string + x-stainless-const: true + images: + type: integer + description: The number of images processed. + num_model_requests: + type: integer + description: The count of requests made to the model. + source: + type: string + description: "When `group_by=source`, this field provides the source of the\ngrouped usage result, possible values are `image.generation`,\n`image.edit`, `image.variation`." + nullable: true + size: + type: string + description: "When `group_by=size`, this field provides the image size of the\ngrouped usage result." + nullable: true + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + description: The aggregated images usage details of the specific time bucket. + x-oaiMeta: + name: Images usage object + example: "{\n \"object\": \"organization.usage.images.result\",\n \"images\": 2,\n \"num_model_requests\": 2,\n \"size\": \"1024x1024\",\n \"source\": \"image.generation\",\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"dall-e-3\"\n}\n" + UsageModerationsResult: + required: + - object + - input_tokens + - num_model_requests + type: object + properties: + object: + enum: + - organization.usage.moderations.result + type: string + x-stainless-const: true + input_tokens: + type: integer + description: The aggregated number of input tokens used. + num_model_requests: + type: integer + description: The count of requests made to the model. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + user_id: + type: string + description: "When `group_by=user_id`, this field provides the user ID of the\ngrouped usage result." + nullable: true + api_key_id: + type: string + description: "When `group_by=api_key_id`, this field provides the API key ID of\nthe grouped usage result." + nullable: true + model: + type: string + description: "When `group_by=model`, this field provides the model name of the\ngrouped usage result." + nullable: true + description: The aggregated moderations usage details of the specific time bucket. + x-oaiMeta: + name: Moderations usage object + example: "{\n \"object\": \"organization.usage.moderations.result\",\n \"input_tokens\": 20,\n \"num_model_requests\": 2,\n \"project_id\": \"proj_abc\",\n \"user_id\": \"user-abc\",\n \"api_key_id\": \"key_abc\",\n \"model\": \"text-moderation\"\n}\n" + UsageResponse: + required: + - object + - data + - has_more + - next_page + type: object + properties: + object: + enum: + - page + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/UsageTimeBucket' + has_more: + type: boolean + next_page: + type: string + UsageTimeBucket: + required: + - object + - start_time + - end_time + - result + type: object + properties: + object: + enum: + - bucket + type: string + x-stainless-const: true + start_time: + type: integer + end_time: + type: integer + result: + type: array + items: + oneOf: + - $ref: '#/components/schemas/UsageCompletionsResult' + - $ref: '#/components/schemas/UsageEmbeddingsResult' + - $ref: '#/components/schemas/UsageModerationsResult' + - $ref: '#/components/schemas/UsageImagesResult' + - $ref: '#/components/schemas/UsageAudioSpeechesResult' + - $ref: '#/components/schemas/UsageAudioTranscriptionsResult' + - $ref: '#/components/schemas/UsageVectorStoresResult' + - $ref: '#/components/schemas/UsageCodeInterpreterSessionsResult' + - $ref: '#/components/schemas/CostsResult' + UsageVectorStoresResult: + required: + - object + - usage_bytes + type: object + properties: + object: + enum: + - organization.usage.vector_stores.result + type: string + x-stainless-const: true + usage_bytes: + type: integer + description: The vector stores usage in bytes. + project_id: + type: string + description: "When `group_by=project_id`, this field provides the project ID of\nthe grouped usage result." + nullable: true + description: The aggregated vector stores usage details of the specific time bucket. + x-oaiMeta: + name: Vector stores usage object + example: "{\n \"object\": \"organization.usage.vector_stores.result\",\n \"usage_bytes\": 1024,\n \"project_id\": \"proj_abc\"\n}\n" + User: + required: + - object + - id + - name + - email + - role + - added_at + type: object + properties: + object: + enum: + - organization.user + type: string + description: 'The object type, which is always `organization.user`' + x-stainless-const: true + id: + type: string + description: 'The identifier, which can be referenced in API endpoints' + name: + type: string + description: The name of the user + email: + type: string + description: The email address of the user + role: + enum: + - owner + - reader + type: string + description: '`owner` or `reader`' + added_at: + type: integer + description: The Unix timestamp (in seconds) of when the user was added. + description: Represents an individual `user` within an organization. + x-oaiMeta: + name: The user object + example: "{\n \"object\": \"organization.user\",\n \"id\": \"user_abc\",\n \"name\": \"First Last\",\n \"email\": \"user@example.com\",\n \"role\": \"owner\",\n \"added_at\": 1711471533\n}\n" + UserDeleteResponse: + required: + - object + - id + - deleted + type: object + properties: + object: + enum: + - organization.user.deleted + type: string + x-stainless-const: true + id: + type: string + deleted: + type: boolean + UserListResponse: + required: + - object + - data + - first_id + - last_id + - has_more + type: object + properties: + object: + enum: + - list + type: string + x-stainless-const: true + data: + type: array + items: + $ref: '#/components/schemas/User' + first_id: + type: string + last_id: + type: string + has_more: + type: boolean + UserRoleUpdateRequest: + required: + - role + type: object + properties: + role: + enum: + - owner + - reader + type: string + description: '`owner` or `reader`' + VectorStoreExpirationAfter: + title: Vector store expiration policy + required: + - anchor + - days + type: object + properties: + anchor: + enum: + - last_active_at + type: string + description: "Anchor timestamp after which the expiration policy applies.\nSupported anchors: `last_active_at`." + x-stainless-const: true + days: + maximum: 365.0 + minimum: 1.0 + type: integer + description: "The number of days after the anchor time that the vector store will\nexpire." + description: The expiration policy for a vector store. + VectorStoreFileAttributes: + maxProperties: 16 + type: object + additionalProperties: + oneOf: + - maxLength: 512 + type: string + - type: number + - type: boolean + description: "Set of 16 key-value pairs that can be attached to an object. This can be\nuseful for storing additional information about the object in a structured\nformat, and querying for objects via API or the dashboard. Keys are strings\nwith a maximum length of 64 characters. Values are strings with a maximum\nlength of 512 characters, booleans, or numbers.\n" + nullable: true + x-oaiTypeLabel: map + VectorStoreFileBatchObject: + title: Vector store file batch + required: + - id + - object + - created_at + - vector_store_id + - status + - file_counts + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.files_batch + type: string + description: 'The object type, which is always `vector_store.file_batch`.' + x-stainless-const: true + created_at: + type: integer + description: "The Unix timestamp (in seconds) for when the vector store files\nbatch was created." + vector_store_id: + type: string + description: "The ID of the [vector\nstore](/docs/api-reference/vector-stores/object) that the\n[File](/docs/api-reference/files) is attached to." + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: "The status of the vector store files batch, which can be either\n`in_progress`, `completed`, `cancelled` or `failed`." + file_counts: + required: + - in_progress + - completed + - cancelled + - failed + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that where cancelled. + total: + type: integer + description: The total number of files. + description: A batch of files attached to a vector store. + x-oaiMeta: + name: The vector store files batch object + beta: true + example: "{\n \"id\": \"vsfb_123\",\n \"object\": \"vector_store.files_batch\",\n \"created_at\": 1698107661,\n \"vector_store_id\": \"vs_abc123\",\n \"status\": \"completed\",\n \"file_counts\": {\n \"in_progress\": 0,\n \"completed\": 100,\n \"failed\": 0,\n \"cancelled\": 0,\n \"total\": 100\n }\n}\n" + VectorStoreFileContentResponse: + required: + - object + - data + - has_more + - next_page + type: object + properties: + object: + enum: + - vector_store.file_content.page + type: string + description: 'The object type, which is always `vector_store.file_content.page`' + x-stainless-const: true + data: + type: array + items: + type: object + properties: + type: + type: string + description: The content type (currently only `"text"`) + text: + type: string + description: The text content + description: Parsed content of the file. + has_more: + type: boolean + description: Indicates if there are more content pages to fetch. + next_page: + type: string + description: 'The token for the next page, if any.' + nullable: true + description: Represents the parsed content of a vector store file. + VectorStoreFileObject: + title: Vector store files + required: + - id + - object + - usage_bytes + - created_at + - vector_store_id + - status + - last_error + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store.file + type: string + description: 'The object type, which is always `vector_store.file`.' + x-stainless-const: true + usage_bytes: + type: integer + description: "The total vector store usage in bytes. Note that this may be\ndifferent from the original file size." + created_at: + type: integer + description: "The Unix timestamp (in seconds) for when the vector store file was\ncreated." + vector_store_id: + type: string + description: "The ID of the [vector\nstore](/docs/api-reference/vector-stores/object) that the\n[File](/docs/api-reference/files) is attached to." + status: + enum: + - in_progress + - completed + - cancelled + - failed + type: string + description: "The status of the vector store file, which can be either\n`in_progress`, `completed`, `cancelled`, or `failed`. The status\n`completed` indicates that the vector store file is ready for use." + last_error: + required: + - code + - message + type: object + properties: + code: + enum: + - server_error + - unsupported_file + - invalid_file + type: string + description: One of `server_error` or `rate_limit_exceeded`. + message: + type: string + description: A human-readable description of the error. + description: "The last error associated with this vector store file. Will be\n`null` if there are no errors." + nullable: true + chunking_strategy: + type: object + oneOf: + - $ref: '#/components/schemas/StaticChunkingStrategyResponseParam' + - $ref: '#/components/schemas/OtherChunkingStrategyResponseParam' + description: The strategy used to chunk the file. + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + description: A list of files attached to a vector store. + x-oaiMeta: + name: The vector store file object + beta: true + example: "{\n \"id\": \"file-abc123\",\n \"object\": \"vector_store.file\",\n \"usage_bytes\": 1234,\n \"created_at\": 1698107661,\n \"vector_store_id\": \"vs_abc123\",\n \"status\": \"completed\",\n \"last_error\": null,\n \"chunking_strategy\": {\n \"type\": \"static\",\n \"static\": {\n \"max_chunk_size_tokens\": 800,\n \"chunk_overlap_tokens\": 400\n }\n }\n}\n" + VectorStoreObject: + title: Vector store + required: + - id + - object + - usage_bytes + - created_at + - status + - last_active_at + - name + - file_counts + - metadata + type: object + properties: + id: + type: string + description: 'The identifier, which can be referenced in API endpoints.' + object: + enum: + - vector_store + type: string + description: 'The object type, which is always `vector_store`.' + x-stainless-const: true + created_at: + type: integer + description: "The Unix timestamp (in seconds) for when the vector store was\ncreated." + name: + type: string + description: The name of the vector store. + usage_bytes: + type: integer + description: The total number of bytes used by the files in the vector store. + file_counts: + required: + - in_progress + - completed + - failed + - cancelled + - total + type: object + properties: + in_progress: + type: integer + description: The number of files that are currently being processed. + completed: + type: integer + description: The number of files that have been successfully processed. + failed: + type: integer + description: The number of files that have failed to process. + cancelled: + type: integer + description: The number of files that were cancelled. + total: + type: integer + description: The total number of files. + status: + enum: + - expired + - in_progress + - completed + type: string + description: "The status of the vector store, which can be either `expired`,\n`in_progress`, or `completed`. A status of `completed` indicates\nthat the vector store is ready for use." + expires_after: + $ref: '#/components/schemas/VectorStoreExpirationAfter' + expires_at: + type: integer + description: "The Unix timestamp (in seconds) for when the vector store will\nexpire." + nullable: true + last_active_at: + type: integer + description: "The Unix timestamp (in seconds) for when the vector store was last\nactive." + nullable: true + metadata: + $ref: '#/components/schemas/Metadata' + description: "A vector store is a collection of processed files can be used by\nthe `file_search` tool." + x-oaiMeta: + name: The vector store object + example: "{\n \"id\": \"vs_123\",\n \"object\": \"vector_store\",\n \"created_at\": 1698107661,\n \"usage_bytes\": 123456,\n \"last_active_at\": 1698107661,\n \"name\": \"my_vector_store\",\n \"status\": \"completed\",\n \"file_counts\": {\n \"in_progress\": 0,\n \"completed\": 100,\n \"cancelled\": 0,\n \"failed\": 0,\n \"total\": 100\n },\n \"last_used_at\": 1698107661\n}\n" + VectorStoreSearchRequest: + required: + - query + type: object + properties: + query: + oneOf: + - type: string + - type: array + items: + minItems: 1 + type: string + description: A list of queries to search for. + description: A query string for a search + rewrite_query: + type: boolean + description: Whether to rewrite the natural language query for vector search. + default: false + max_num_results: + maximum: 50.0 + minimum: 1.0 + type: integer + description: "The maximum number of results to return. This number should be\nbetween 1 and 50 inclusive." + default: 10 + filters: + oneOf: + - $ref: '#/components/schemas/ComparisonFilter' + - $ref: '#/components/schemas/CompoundFilter' + description: A filter to apply based on file attributes. + ranking_options: + type: object + properties: + ranker: + enum: + - auto + - default-2024-11-15 + type: string + default: auto + score_threshold: + maximum: 1.0 + minimum: 0.0 + type: number + default: 0 + additionalProperties: false + description: Ranking options for search. + additionalProperties: false + x-oaiMeta: + name: Vector store search request + VectorStoreSearchResultContentObject: + required: + - type + - text + type: object + properties: + type: + enum: + - text + type: string + description: The type of content. + text: + type: string + description: The text content returned from search. + additionalProperties: false + x-oaiMeta: + name: Vector store search result content object + VectorStoreSearchResultItem: + required: + - file_id + - filename + - score + - attributes + - content + type: object + properties: + file_id: + type: string + description: The ID of the vector store file. + filename: + type: string + description: The name of the vector store file. + score: + maximum: 1.0 + minimum: 0.0 + type: number + description: The similarity score for the result. + attributes: + $ref: '#/components/schemas/VectorStoreFileAttributes' + content: + type: array + items: + $ref: '#/components/schemas/VectorStoreSearchResultContentObject' + description: Content chunks from the file. + additionalProperties: false + x-oaiMeta: + name: Vector store search result item + VectorStoreSearchResultsPage: + required: + - object + - search_query + - data + - has_more + - next_page + type: object + properties: + object: + enum: + - vector_store.search_results.page + type: string + description: 'The object type, which is always `vector_store.search_results.page`' + x-stainless-const: true + search_query: + type: array + items: + minItems: 1 + type: string + description: The query used for this search. + data: + type: array + items: + $ref: '#/components/schemas/VectorStoreSearchResultItem' + description: The list of search result items. + has_more: + type: boolean + description: Indicates if there are more results to fetch. + next_page: + type: string + description: 'The token for the next page, if any.' + nullable: true + additionalProperties: false + x-oaiMeta: + name: Vector store search results page + VoiceIdsShared: + anyOf: + - type: string + - enum: + - alloy + - ash + - ballad + - coral + - echo + - fable + - onyx + - nova + - sage + - shimmer + - verse + type: string + example: ash + Wait: + title: Wait + required: + - type + type: object + properties: + type: + enum: + - wait + type: string + description: "Specifies the event type. For a wait action, this property is \nalways set to `wait`.\n" + default: wait + x-stainless-const: true + description: "A wait action.\n" + WebSearchContextSize: + enum: + - low + - medium + - high + type: string + description: "High level guidance for the amount of context window space to use for the\nsearch. One of `low`, `medium`, or `high`. `medium` is the default.\n" + default: medium + WebSearchLocation: + title: Web search location + type: object + properties: + country: + type: string + description: "The two-letter\n[ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1) of the user,\ne.g. `US`.\n" + region: + type: string + description: "Free text input for the region of the user, e.g. `California`.\n" + city: + type: string + description: "Free text input for the city of the user, e.g. `San Francisco`.\n" + timezone: + type: string + description: "The [IANA timezone](https://timeapi.io/documentation/iana-timezones)\nof the user, e.g. `America/Los_Angeles`.\n" + description: Approximate location parameters for the search. + WebSearchToolCall: + title: Web search tool call + required: + - id + - type + - status + type: object + properties: + id: + type: string + description: "The unique ID of the web search tool call.\n" + type: + enum: + - web_search_call + type: string + description: "The type of the web search tool call. Always `web_search_call`.\n" + x-stainless-const: true + status: + enum: + - in_progress + - searching + - completed + - failed + type: string + description: "The status of the web search tool call.\n" + description: "The results of a web search tool call. See the \n[web search guide](/docs/guides/tools-web-search) for more information.\n" + InputTextContent: + title: Input text + required: + - type + - text + type: object + properties: + type: + enum: + - input_text + type: string + description: The type of the input item. Always `input_text`. + default: input_text + x-stainless-const: true + text: + type: string + description: The text input to the model. + description: A text input to the model. + InputImageContent: + title: Input image + required: + - type + - detail + type: object + properties: + type: + enum: + - input_image + type: string + description: The type of the input item. Always `input_image`. + default: input_image + x-stainless-const: true + image_url: + anyOf: + - type: string + description: "The URL of the image to be sent to the model. A fully qualified URL\nor base64 encoded image in a data URL." + - type: 'null' + file_id: + anyOf: + - type: string + description: The ID of the file to be sent to the model. + - type: 'null' + detail: + enum: + - low + - high + - auto + type: string + description: "The detail level of the image to be sent to the model. One of\n`high`, `low`, or `auto`. Defaults to `auto`." + description: "An image input to the model. Learn about [image\ninputs](/docs/guides/vision)." + InputFileContent: + title: Input file + required: + - type + type: object + properties: + type: + enum: + - input_file + type: string + description: The type of the input item. Always `input_file`. + default: input_file + x-stainless-const: true + file_id: + anyOf: + - type: string + description: The ID of the file to be sent to the model. + - type: 'null' + filename: + type: string + description: The name of the file to be sent to the model. + file_data: + type: string + description: "The content of the file to be sent to the model.\n" + description: A file input to the model. + RankingOptions: + type: object + properties: + ranker: + enum: + - auto + - default-2024-11-15 + type: string + description: The ranker to use for the file search. + score_threshold: + type: number + description: "The score threshold for the file search, a number between 0 and 1.\nNumbers closer to 1 will attempt to return only the most relevant\nresults, but may return fewer results." + Filters: + anyOf: + - $ref: '#/components/schemas/ComparisonFilter' + - $ref: '#/components/schemas/CompoundFilter' + FileSearchTool: + title: File search + required: + - type + - vector_store_ids + type: object + properties: + type: + enum: + - file_search + type: string + description: The type of the file search tool. Always `file_search`. + default: file_search + x-stainless-const: true + vector_store_ids: + type: array + items: + type: string + description: The IDs of the vector stores to search. + max_num_results: + type: integer + description: "The maximum number of results to return. This number should be\nbetween 1 and 50 inclusive." + ranking_options: + $ref: '#/components/schemas/RankingOptions' + filters: + anyOf: + - $ref: '#/components/schemas/Filters' + - type: 'null' + description: "A tool that searches for relevant content from uploaded files.\nLearn more about the [file search\ntool](https://platform.openai.com/docs/guides/tools-file-search)." + FunctionTool: + title: Function + required: + - type + - name + - strict + - parameters + type: object + properties: + type: + enum: + - function + type: string + description: The type of the function tool. Always `function`. + default: function + x-stainless-const: true + name: + type: string + description: The name of the function to call. + description: + anyOf: + - type: string + description: "A description of the function. Used by the model to determine\nwhether or not to call the function." + - type: 'null' + parameters: + anyOf: + - type: object + additionalProperties: { } + description: A JSON schema object describing the parameters of the function. + - type: 'null' + strict: + anyOf: + - type: boolean + description: Whether to enforce strict parameter validation. Default `true`. + - type: 'null' + description: "Defines a function in your own code the model can choose to call.\nLearn more about [function\ncalling](https://platform.openai.com/docs/guides/function-calling)." + ApproximateLocation: + required: + - type + type: object + properties: + type: + enum: + - approximate + type: string + description: The type of location approximation. Always `approximate`. + default: approximate + x-stainless-const: true + country: + anyOf: + - type: string + description: "The two-letter [ISO country\ncode](https://en.wikipedia.org/wiki/ISO_3166-1) of the user,\ne.g. `US`." + - type: 'null' + region: + anyOf: + - type: string + description: 'Free text input for the region of the user, e.g. `California`.' + - type: 'null' + city: + anyOf: + - type: string + description: 'Free text input for the city of the user, e.g. `San Francisco`.' + - type: 'null' + timezone: + anyOf: + - type: string + description: "The [IANA\ntimezone](https://timeapi.io/documentation/iana-timezones) of\nthe user, e.g. `America/Los_Angeles`." + - type: 'null' + WebSearchPreviewTool: + title: Web search preview + required: + - type + type: object + properties: + type: + enum: + - web_search_preview + - web_search_preview_2025_03_11 + type: string + description: "The type of the web search tool. One of `web_search_preview` or\n`web_search_preview_2025_03_11`." + default: web_search_preview + x-stainless-const: true + user_location: + anyOf: + - $ref: '#/components/schemas/ApproximateLocation' + - type: 'null' + search_context_size: + enum: + - low + - medium + - high + type: string + description: "High level guidance for the amount of context window space to use\nfor the search. One of `low`, `medium`, or `high`. `medium` is the\ndefault." + description: "This tool searches the web for relevant results to use in a\nresponse. Learn more about the [web search\ntool](https://platform.openai.com/docs/guides/tools-web-search)." + ComputerUsePreviewTool: + title: Computer use preview + required: + - type + - environment + - display_width + - display_height + type: object + properties: + type: + enum: + - computer_use_preview + type: string + description: The type of the computer use tool. Always `computer_use_preview`. + default: computer_use_preview + x-stainless-const: true + environment: + enum: + - windows + - mac + - linux + - ubuntu + - browser + type: string + description: The type of computer environment to control. + display_width: + type: integer + description: The width of the computer display. + display_height: + type: integer + description: The height of the computer display. + description: "A tool that controls a virtual computer. Learn more about the\n[computer\ntool](https://platform.openai.com/docs/guides/tools-computer-use)." + Tool: + oneOf: + - $ref: '#/components/schemas/FileSearchTool' + - $ref: '#/components/schemas/FunctionTool' + - $ref: '#/components/schemas/WebSearchPreviewTool' + - $ref: '#/components/schemas/ComputerUsePreviewTool' + discriminator: + propertyName: type + FileCitationBody: + title: File citation + required: + - type + - file_id + - index + type: object + properties: + type: + enum: + - file_citation + type: string + description: The type of the file citation. Always `file_citation`. + default: file_citation + x-stainless-const: true + file_id: + type: string + description: The ID of the file. + index: + type: integer + description: The index of the file in the list of files. + description: A citation to a file. + UrlCitationBody: + title: URL citation + required: + - type + - url + - start_index + - end_index + - title + type: object + properties: + type: + enum: + - url_citation + type: string + description: The type of the URL citation. Always `url_citation`. + default: url_citation + x-stainless-const: true + url: + type: string + description: The URL of the web resource. + start_index: + type: integer + description: The index of the first character of the URL citation in the message. + end_index: + type: integer + description: The index of the last character of the URL citation in the message. + title: + type: string + description: The title of the web resource. + description: A citation for a web resource used to generate a model response. + Annotation: + oneOf: + - $ref: '#/components/schemas/FileCitationBody' + - $ref: '#/components/schemas/UrlCitationBody' + - $ref: '#/components/schemas/FilePath' + discriminator: + propertyName: type + OutputTextContent: + title: Output text + required: + - type + - text + - annotations + type: object + properties: + type: + enum: + - output_text + type: string + description: The type of the output text. Always `output_text`. + default: output_text + x-stainless-const: true + text: + type: string + description: The text output from the model. + annotations: + type: array + items: + $ref: '#/components/schemas/Annotation' + description: The annotations of the text output. + description: A text output from the model. + RefusalContent: + title: Refusal + required: + - type + - refusal + type: object + properties: + type: + enum: + - refusal + type: string + description: The type of the refusal. Always `refusal`. + default: refusal + x-stainless-const: true + refusal: + type: string + description: The refusal explanationfrom the model. + description: A refusal from the model. + ComputerCallSafetyCheckParam: + required: + - id + type: object + properties: + id: + type: string + description: The ID of the pending safety check. + code: + anyOf: + - type: string + description: The type of the pending safety check. + - type: 'null' + message: + anyOf: + - type: string + description: Details about the pending safety check. + - type: 'null' + description: A pending safety check for the computer call. + ComputerCallOutputItemParam: + title: Computer tool call output + required: + - call_id + - type + - output + type: object + properties: + id: + anyOf: + - type: string + description: The ID of the computer tool call output. + - type: 'null' + call_id: + maxLength: 64 + minLength: 1 + type: string + description: The ID of the computer tool call that produced the output. + type: + enum: + - computer_call_output + type: string + description: "The type of the computer tool call output. Always\n`computer_call_output`." + default: computer_call_output + x-stainless-const: true + output: + $ref: '#/components/schemas/ComputerScreenshotImage' + acknowledged_safety_checks: + anyOf: + - type: array + items: + $ref: '#/components/schemas/ComputerCallSafetyCheckParam' + description: "The safety checks reported by the API that have been acknowledged\nby the developer." + - type: 'null' + status: + anyOf: + - enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the message input. One of `in_progress`, `completed`,\nor `incomplete`. Populated when input items are returned via\nAPI." + - type: 'null' + description: The output of a computer tool call. + FunctionCallOutputItemParam: + title: Function tool call output + required: + - call_id + - type + - output + type: object + properties: + id: + anyOf: + - type: string + description: "The unique ID of the function tool call output. Populated when this\nitem is returned via API." + - type: 'null' + call_id: + maxLength: 64 + minLength: 1 + type: string + description: The unique ID of the function tool call generated by the model. + type: + enum: + - function_call_output + type: string + description: "The type of the function tool call output. Always\n`function_call_output`." + default: function_call_output + x-stainless-const: true + output: + maxLength: 10485760 + type: string + description: A JSON string of the output of the function tool call. + status: + anyOf: + - enum: + - in_progress + - completed + - incomplete + type: string + description: "The status of the item. One of `in_progress`, `completed`, or\n`incomplete`. Populated when items are returned via API." + - type: 'null' + description: The output of a function tool call. + ItemReferenceParam: + title: Item reference + required: + - id + type: object + properties: + type: + anyOf: + - enum: + - item_reference + type: string + description: The type of item to reference. Always `item_reference`. + default: item_reference + x-stainless-const: true + - type: 'null' + id: + type: string + description: The ID of the item to reference. + description: An internal identifier for an item to reference. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-Basic-API.openapi.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-Basic-API.openapi.yaml new file mode 100644 index 000000000..d71973b4b --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/apim/universal-llm-api/Universal-LLM-Basic-API.openapi.yaml @@ -0,0 +1,1685 @@ +openapi: 3.0.1 +info: + title: Inference-api + version: '1.0' +servers: + - url: https://apim-7c3dvrbie5sui.azure-api.net/inf-2/models +paths: + /chat/completions: + post: + summary: Gets chat completions for the provided chat messages. + description: "Gets chat completions for the provided chat messages.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data. The method makes a REST API call to the `/chat/completions` route\non the given endpoint." + operationId: getChatCompletions + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + default: 2024-05-01-preview + - name: extra-parameters + in: header + description: "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`." + schema: + $ref: '#/components/schemas/ExtraParameters' + requestBody: + description: The parameters of the chat completions request. + content: + application/json: + schema: + $ref: '#/components/schemas/ChatCompletionsOptions' + example: + model: gpt-4o + messages: + - role: system + content: You are a helpful assistant + - role: user + content: How are you? + max_tokens: 50 + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ChatCompletions' + example: + id: string + object: chat.completion + created: 0 + model: string + choices: + - index: 0 + finish_reason: { } + message: + role: { } + content: string + reasoning_content: string + tool_calls: + - id: string + type: function + function: + name: string + arguments: string + audio: + id: string + expires_at: 0 + data: string + format: { } + transcript: string + usage: + completion_tokens: 0 + prompt_tokens: 0 + total_tokens: 0 + completion_tokens_details: + audio_tokens: 0 + reasoning_tokens: 0 + total_tokens: 0 + prompt_tokens_details: + audio_tokens: 0 + cached_tokens: 0 + '400': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + '500': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + /embeddings: + post: + summary: Return the embedding vectors for given text prompts. + description: "Return the embedding vectors for given text prompts.\nThe method makes a REST API call to the `/embeddings` route on the given endpoint." + operationId: getEmbeddings + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + default: 2024-05-01-preview + - name: extra-parameters + in: header + description: "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`." + schema: + $ref: '#/components/schemas/ExtraParameters' + requestBody: + description: The parameters of the embeddings request. + content: + application/json: + schema: + $ref: '#/components/schemas/EmbeddingsOptions' + example: + model: text-embedding-ada-002 + input: How are you? + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/EmbeddingsResult' + example: + id: string + data: + - embedding: + - 0 + index: 0 + object: embedding + usage: + prompt_tokens: 0 + total_tokens: 0 + object: list + model: string + '400': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + '500': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + /images/embeddings: + post: + summary: Return the embedding vectors for given images. + description: "Return the embedding vectors for given images.\nThe method makes a REST API call to the `/images/embeddings` route on the given endpoint." + operationId: getImageEmbeddings + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + default: 2024-05-01-preview + - name: extra-parameters + in: header + description: "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`." + schema: + $ref: '#/components/schemas/ExtraParameters' + requestBody: + description: The parameters of the image embeddings request. + content: + application/json: + schema: + $ref: '#/components/schemas/ImageEmbeddingsOptions' + example: + input: + - image: string + text: string + dimensions: 0 + encoding_format: { } + input_type: { } + model: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/EmbeddingsResult' + example: + id: string + data: + - embedding: + - 0 + index: 0 + object: embedding + usage: + prompt_tokens: 0 + total_tokens: 0 + object: list + model: string + '400': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + '500': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + /images/generations: + post: + summary: Generates an image based on a text or image prompt. + description: "Generates an image based on a text or image prompt.\nThe method makes a REST API call to the `/images/generations` route on the given endpoint." + operationId: getImageGenerations + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + default: 2024-05-01-preview + - name: extra-parameters + in: header + description: "Controls what happens if extra parameters, undefined by the REST API,\nare passed in the JSON request payload.\nThis sets the HTTP request header `extra-parameters`." + schema: + $ref: '#/components/schemas/ExtraParameters' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ImageGenerationsOptions' + example: + prompt: string + negative_prompt: string + image_prompt: + image: string + strength: 1 + size: { } + output_format: png + quality: standard + n: 1 + model: string + seed: 0 + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ImageGenerationsResult' + example: + id: string + created: 0 + model: string + data: + - image: string + seed: 0 + usage: + image_generations: 0 + object: image.generation + '400': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + '500': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + /info: + get: + summary: Returns information about the AI model deployed. + description: "Returns information about the AI model deployed.\nThe method makes a REST API call to the `/info` route on the given endpoint.\nThis method will only work when using Serverless API, Managed Compute, or Model .\ninference endpoint. Azure OpenAI endpoints don't support i." + operationId: getModelInfo + parameters: + - name: api-version + in: query + description: The API version to use for this operation. + required: true + schema: + minLength: 1 + type: string + default: 2024-05-01-preview + - name: model + in: query + description: The model deployment name you want information from. + schema: + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + $ref: '#/components/schemas/ModelInfo' + example: + model_name: string + model_type: { } + model_provider_name: string + '400': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + '500': + description: An unexpected error response. + headers: + x-ms-error-code: + description: String error code indicating what went wrong. + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/Azure.Core.Foundations.ErrorResponse' + /deployments: + get: + summary: Deployments + description: Get deployments for Azure OpenAI resource for AI Foundry Integration + operationId: deployments + responses: + '200': + description: '' +components: + schemas: + AudioContentFormat: + anyOf: + - type: string + - enum: + - wav + - mp3 + type: string + description: A representation of the possible audio formats for audio. + Azure.Core.Foundations.Error: + required: + - code + - message + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + message: + type: string + description: A human-readable representation of the error. + target: + type: string + description: The target of the error. + details: + type: array + items: + $ref: '#/components/schemas/Azure.Core.Foundations.Error' + description: An array of details about specific errors that led to this reported error. + innererror: + allOf: + - $ref: '#/components/schemas/Azure.Core.Foundations.InnerError' + description: An object containing more specific information than the current object about the error. + description: The error object. + Azure.Core.Foundations.ErrorResponse: + required: + - error + type: object + properties: + error: + allOf: + - $ref: '#/components/schemas/Azure.Core.Foundations.Error' + description: The error object. + description: A response containing error details. + Azure.Core.Foundations.InnerError: + type: object + properties: + code: + type: string + description: One of a server-defined set of error codes. + innererror: + allOf: + - $ref: '#/components/schemas/Azure.Core.Foundations.InnerError' + description: Inner error. + description: An object containing more specific information about the error. As per Microsoft One API guidelines - https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#handling-errors. + ChatChoice: + required: + - index + - finish_reason + - message + type: object + properties: + index: + type: integer + description: The ordered index associated with this chat completions choice. + format: int32 + finish_reason: + allOf: + - $ref: '#/components/schemas/CompletionsFinishReason' + description: The reason that this chat completions choice completed its generated. + nullable: true + readOnly: true + message: + allOf: + - $ref: '#/components/schemas/ChatResponseMessage' + description: The chat message for a given chat completions prompt. + readOnly: true + description: "The representation of a single prompt completion as part of an overall chat completions request.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + ChatCompletions: + required: + - id + - object + - created + - model + - choices + - usage + type: object + properties: + id: + type: string + description: A unique identifier associated with this chat completions response. + object: + enum: + - chat.completion + type: string + description: 'The response object type, which is always `chat.completion`.' + created: + type: integer + description: "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970." + format: unixtime + readOnly: true + model: + type: string + description: The model used for the chat completion. + readOnly: true + choices: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatChoice' + description: "The collection of completions choices associated with this completions response.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + readOnly: true + usage: + allOf: + - $ref: '#/components/schemas/CompletionsUsage' + description: ' Usage information for tokens processed and generated as part of this completions operation.' + readOnly: true + description: "Representation of the response data from a chat completions request.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + ChatCompletionsAudio: + required: + - id + - expires_at + - data + - transcript + type: object + properties: + id: + type: string + description: " Unique identifier for the audio response. This value can be used in chat history messages instead of passing \n the full audio object." + readOnly: true + expires_at: + type: integer + description: "The Unix timestamp (in seconds) at which the audio piece expires and can't be any longer referenced by its ID in \nmulti-turn conversations." + format: unixtime + readOnly: true + data: + type: string + description: Base64 encoded audio data + readOnly: true + format: + allOf: + - $ref: '#/components/schemas/AudioContentFormat' + description: "The format of the audio content. If format is not provided, it will match the format used in the\ninput audio request." + readOnly: true + transcript: + type: string + description: The transcript of the audio file. + readOnly: true + description: A representation of the audio generated by the model. + ChatCompletionsModality: + anyOf: + - type: string + - enum: + - text + - audio + type: string + description: The modalities that the model is allowed to use for the chat completions response. + ChatCompletionsNamedToolChoice: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + allOf: + - $ref: '#/components/schemas/ChatCompletionsNamedToolChoiceFunction' + description: The function that should be called. + description: 'A tool selection of a specific, named function tool that will limit chat completions to using the named function.' + ChatCompletionsNamedToolChoiceFunction: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function that should be called. + description: 'A tool selection of a specific, named function tool that will limit chat completions to using the named function.' + ChatCompletionsOptions: + required: + - messages + type: object + properties: + messages: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatRequestMessage' + description: "The collection of context messages associated with this chat completions request.\nTypical usage begins with a chat message for the System role that provides instructions for\nthe behavior of the assistant, followed by alternating messages between the User and\nAssistant roles." + frequency_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "A value that influences the probability of generated tokens appearing based on their cumulative\nfrequency in generated text.\nPositive values will make tokens less likely to appear as their frequency increases and\ndecrease the likelihood of the model repeating the same statements verbatim.\nSupported range is [-2, 2]." + format: float + default: 0 + stream: + type: boolean + description: A value indicating whether chat completions should be streamed for this request. + presence_penalty: + maximum: 2.0 + minimum: -2.0 + type: number + description: "A value that influences the probability of generated tokens appearing based on their existing\npresence in generated text.\nPositive values will make tokens less likely to appear when they already exist and increase the\nmodel's likelihood to output new topics.\nSupported range is [-2, 2]." + format: float + default: 0 + temperature: + maximum: 1.0 + minimum: 0.0 + type: number + description: "The sampling temperature to use that controls the apparent creativity of generated completions.\nHigher values will make output more random while lower values will make results more focused\nand deterministic.\nIt is not recommended to modify temperature and top_p for the same completions request as the\ninteraction of these two settings is difficult to predict.\nSupported range is [0, 1]." + format: float + default: 0.7 + top_p: + maximum: 1.0 + minimum: 0.0 + type: number + description: "An alternative to sampling with temperature called nucleus sampling. This value causes the\nmodel to consider the results of tokens with the provided probability mass. As an example, a\nvalue of 0.15 will cause only the tokens comprising the top 15% of probability mass to be\nconsidered.\nIt is not recommended to modify temperature and top_p for the same completions request as the\ninteraction of these two settings is difficult to predict.\nSupported range is [0, 1]." + format: float + default: 1 + max_tokens: + minimum: 0.0 + type: integer + description: The maximum number of tokens to generate. + format: int32 + response_format: + allOf: + - $ref: '#/components/schemas/ChatCompletionsResponseFormat' + description: "An object specifying the format that the model must output.\n\nSetting to `{ \"type\": \"json_schema\", \"json_schema\": {...} }` enables Structured Outputs which ensures the model will match your supplied JSON schema.\n\nSetting to `{ \"type\": \"json_object\" }` enables JSON mode, which ensures the message the model generates is valid JSON.\n\n**Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly \"stuck\" request. Also note that the message content may be partially cut off if `finish_reason=\"length\"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length." + stop: + minItems: 1 + type: array + items: + type: string + description: A collection of textual sequences that will end completions generation. + tools: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/ChatCompletionsToolDefinition' + description: "A list of tools the model may request to call. Currently, only functions are supported as a tool. The model\nmay response with a function call request and provide the input arguments in JSON format for that function." + tool_choice: + anyOf: + - $ref: '#/components/schemas/ChatCompletionsToolChoicePreset' + - $ref: '#/components/schemas/ChatCompletionsNamedToolChoice' + description: 'If specified, the model will configure which of the provided tools it can use for the chat completions response.' + seed: + type: integer + description: "If specified, the system will make a best effort to sample deterministically such that repeated requests with the\nsame seed and parameters should return the same result. Determinism is not guaranteed." + format: int64 + model: + type: string + description: 'ID of the specific AI model to use, if more than one model is available on the endpoint.' + modalities: + type: array + items: + $ref: '#/components/schemas/ChatCompletionsModality' + description: "The modalities that the model is allowed to use for the chat completions response. The default modality\nis `text`. Indicating an unsupported modality combination results in an 422 error." + user_security_context: + allOf: + - $ref: '#/components/schemas/UserSecurityContext' + description: "User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. \nThese fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. \n[Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud." + additionalProperties: { } + description: "The configuration information for a chat completions request.\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + ChatCompletionsResponseFormat: + required: + - type + type: object + properties: + type: + type: string + description: The response format type to use for chat completions. + description: "Represents the format that the model must output. Use this to enable JSON mode instead of the default text mode.\nNote that to enable JSON mode, some AI models may also require you to instruct the model to produce JSON\nvia a system or user message." + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/ChatCompletionsResponseFormatText' + json_object: '#/components/schemas/ChatCompletionsResponseFormatJsonObject' + json_schema: '#/components/schemas/ChatCompletionsResponseFormatJsonSchema' + ChatCompletionsResponseFormatJsonObject: + required: + - type + type: object + allOf: + - $ref: '#/components/schemas/ChatCompletionsResponseFormat' + properties: + type: + enum: + - json_object + type: string + description: 'Response format type: always ''json_object'' for this object.' + description: "A response format for Chat Completions that restricts responses to emitting valid JSON objects.\nNote that to enable JSON mode, some AI models may also require you to instruct the model to produce JSON\nvia a system or user message." + ChatCompletionsResponseFormatJsonSchema: + required: + - type + - json_schema + type: object + allOf: + - $ref: '#/components/schemas/ChatCompletionsResponseFormat' + properties: + type: + enum: + - json_schema + type: string + description: 'The type of response format being defined: `json_schema`' + json_schema: + allOf: + - $ref: '#/components/schemas/ChatCompletionsResponseFormatJsonSchemaDefinition' + description: 'The definition of the required JSON schema in the response, and associated metadata.' + description: "A response format for Chat Completions that restricts responses to emitting valid JSON objects, with a\nJSON schema specified by the caller." + ChatCompletionsResponseFormatJsonSchemaDefinition: + required: + - name + - schema + type: object + properties: + name: + type: string + description: 'The name of the response format. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.' + schema: + type: object + additionalProperties: { } + description: The definition of the JSON schema + description: + type: string + description: 'A description of the response format, used by the AI model to determine how to generate responses in this format.' + strict: + type: boolean + description: "Whether to enable strict schema adherence when generating the output.\nIf set to true, the model will always follow the exact schema defined in the `schema` field. Only a subset of\nJSON Schema is supported when `strict` is `true`." + default: false + description: 'The definition of the required JSON schema in the response, and associated metadata.' + ChatCompletionsResponseFormatText: + required: + - type + type: object + allOf: + - $ref: '#/components/schemas/ChatCompletionsResponseFormat' + properties: + type: + enum: + - text + type: string + description: 'Response format type: always ''text'' for this object.' + description: A response format for Chat Completions that emits text responses. This is the default response format. + ChatCompletionsToolCall: + required: + - id + - type + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + type: + enum: + - function + type: string + description: 'The type of tool call. Currently, only `function` is supported.' + function: + allOf: + - $ref: '#/components/schemas/FunctionCall' + description: The details of the function call requested by the AI model. + description: A function tool call requested by the AI model. + ChatCompletionsToolChoicePreset: + anyOf: + - type: string + - enum: + - auto + - none + - required + type: string + description: Represents a generic policy for how a chat completions tool may be selected. + ChatCompletionsToolDefinition: + required: + - type + - function + type: object + properties: + type: + enum: + - function + type: string + description: 'The type of the tool. Currently, only `function` is supported.' + function: + allOf: + - $ref: '#/components/schemas/FunctionDefinition' + description: The function definition details for the function tool. + description: The definition of a chat completions tool that can call a function. + ChatMessageAudioContentItem: + required: + - type + - audio_url + type: object + allOf: + - $ref: '#/components/schemas/ChatMessageContentItem' + properties: + type: + enum: + - audio_url + type: string + description: 'The discriminated object type: always ''image_url'' for this type.' + audio_url: + allOf: + - $ref: '#/components/schemas/ChatMessageAudioUrl' + description: 'An internet location, which must be accessible to the model, from which the audio may be retrieved.' + description: A structured chat content item containing an audio reference. + ChatMessageAudioUrl: + required: + - url + type: object + properties: + url: + type: string + description: The URL of the audio. + description: An internet location from which the model may retrieve an audio. + ChatMessageContentItem: + required: + - type + type: object + properties: + type: + type: string + description: The discriminated object type. + description: An abstract representation of a structured content item within a chat message. + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/ChatMessageTextContentItem' + image_url: '#/components/schemas/ChatMessageImageContentItem' + audio_url: '#/components/schemas/ChatMessageAudioContentItem' + input_audio: '#/components/schemas/ChatMessageInputAudioContentItem' + ChatMessageImageContentItem: + required: + - type + - image_url + type: object + allOf: + - $ref: '#/components/schemas/ChatMessageContentItem' + properties: + type: + enum: + - image_url + type: string + description: 'The discriminated object type: always ''image_url'' for this type.' + image_url: + allOf: + - $ref: '#/components/schemas/ChatMessageImageUrl' + description: 'An internet location, which must be accessible to the model,from which the image may be retrieved.' + description: A structured chat content item containing an image reference. + ChatMessageImageDetailLevel: + anyOf: + - type: string + - enum: + - auto + - low + - high + type: string + description: A representation of the possible image detail levels for image-based chat completions message content. + ChatMessageImageUrl: + required: + - url + type: object + properties: + url: + type: string + description: The URL of the image. + detail: + allOf: + - $ref: '#/components/schemas/ChatMessageImageDetailLevel' + description: "The evaluation quality setting to use, which controls relative prioritization of speed, token consumption, and\naccuracy." + description: An internet location from which the model may retrieve an image. + ChatMessageInputAudio: + required: + - data + - format + type: object + properties: + data: + type: string + description: Base64 encoded audio data + format: + allOf: + - $ref: '#/components/schemas/AudioContentFormat' + description: The audio format of the audio content. + description: The details of an audio chat message content part. + ChatMessageInputAudioContentItem: + required: + - type + - format + type: object + allOf: + - $ref: '#/components/schemas/ChatMessageContentItem' + properties: + type: + enum: + - input_audio + type: string + description: 'The discriminated object type: always ''input_audio'' for this type.' + format: + allOf: + - $ref: '#/components/schemas/AudioContentFormat' + description: The audio format of the audio reference. + description: A structured chat content item containing an audio content. + ChatMessageTextContentItem: + required: + - type + - text + type: object + allOf: + - $ref: '#/components/schemas/ChatMessageContentItem' + properties: + type: + enum: + - text + type: string + description: 'The discriminated object type: always ''text'' for this type.' + text: + type: string + description: The content of the message. + description: A structured chat content item containing plain text. + ChatRequestAssistantMessage: + required: + - role + type: object + allOf: + - $ref: '#/components/schemas/ChatRequestMessage' + properties: + role: + enum: + - assistant + type: string + description: 'The chat role associated with this message, which is always ''assistant'' for assistant messages.' + content: + type: string + description: The content of the message. + tool_calls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionsToolCall' + description: "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured." + audio: + allOf: + - $ref: '#/components/schemas/ChatRequestAudioReference' + description: ' The audio generated by a previous response in a multi-turn conversation.' + description: A request chat message representing response or action from the assistant. + ChatRequestAudioReference: + required: + - id + type: object + properties: + id: + type: string + description: ' Unique identifier for the audio response. This value corresponds to the id of a previous audio completion.' + description: A reference to an audio response generated by the model. + ChatRequestMessage: + required: + - role + type: object + properties: + role: + allOf: + - $ref: '#/components/schemas/ChatRole' + description: The chat role associated with this message. + description: An abstract representation of a chat message as provided in a request. + discriminator: + propertyName: role + mapping: + system: '#/components/schemas/ChatRequestSystemMessage' + user: '#/components/schemas/ChatRequestUserMessage' + assistant: '#/components/schemas/ChatRequestAssistantMessage' + tool: '#/components/schemas/ChatRequestToolMessage' + ChatRequestSystemMessage: + required: + - role + - content + type: object + allOf: + - $ref: '#/components/schemas/ChatRequestMessage' + properties: + role: + enum: + - system + type: string + description: 'The chat role associated with this message, which is always ''system'' for system messages.' + content: + type: string + description: The contents of the system message. + description: "A request chat message containing system instructions that influence how the model will generate a chat completions\nresponse." + ChatRequestToolMessage: + required: + - role + - tool_call_id + type: object + allOf: + - $ref: '#/components/schemas/ChatRequestMessage' + properties: + role: + enum: + - tool + type: string + description: 'The chat role associated with this message, which is always ''tool'' for tool messages.' + content: + type: string + description: The content of the message. + tool_call_id: + type: string + description: The ID of the tool call resolved by the provided content. + description: A request chat message representing requested output from a configured tool. + ChatRequestUserMessage: + required: + - role + - content + type: object + allOf: + - $ref: '#/components/schemas/ChatRequestMessage' + properties: + role: + enum: + - user + type: string + description: 'The chat role associated with this message, which is always ''user'' for user messages.' + content: + anyOf: + - type: string + - type: array + items: + $ref: '#/components/schemas/ChatMessageContentItem' + description: 'The contents of the user message, with available input types varying by selected model.' + description: A request chat message representing user input to the assistant. + ChatResponseMessage: + required: + - role + - content + type: object + properties: + role: + allOf: + - $ref: '#/components/schemas/ChatRole' + description: The chat role associated with the message. + readOnly: true + content: + type: string + description: The content of the message. + nullable: true + readOnly: true + reasoning_content: + type: string + description: The reasoning content the model used for generating the response + readOnly: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/ChatCompletionsToolCall' + description: "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured." + readOnly: true + audio: + allOf: + - $ref: '#/components/schemas/ChatCompletionsAudio' + description: ' The audio generated by the model as a response to the messages if the model is configured to generate audio.' + readOnly: true + description: A representation of a chat message as received in a response. + ChatRole: + anyOf: + - type: string + - enum: + - system + - developer + - user + - assistant + - tool + type: string + description: A description of the intended purpose of a message within a chat completions interaction. + CompletionsFinishReason: + anyOf: + - type: string + - enum: + - stop + - length + - content_filter + - tool_calls + type: string + description: Representation of the manner in which a completions response concluded. + CompletionsUsage: + required: + - completion_tokens + - prompt_tokens + - total_tokens + type: object + properties: + completion_tokens: + type: integer + description: The number of tokens generated across all completions emissions. + format: int32 + readOnly: true + prompt_tokens: + type: integer + description: The number of tokens in the provided prompts for the completions request. + format: int32 + readOnly: true + total_tokens: + type: integer + description: The total number of tokens processed for the completions request and response. + format: int32 + readOnly: true + completion_tokens_details: + allOf: + - $ref: '#/components/schemas/CompletionsUsageDetails' + description: Breakdown of tokens used in a completion. + readOnly: true + prompt_tokens_details: + allOf: + - $ref: '#/components/schemas/PromptUsageDetails' + description: Breakdown of tokens used in the prompt/chat history. + readOnly: true + description: "Representation of the token counts processed for a completions request.\nCounts consider all tokens across prompts, choices, choice alternates, best_of generations, and\nother consumers." + CompletionsUsageDetails: + required: + - audio_tokens + - reasoning_tokens + - total_tokens + type: object + properties: + audio_tokens: + type: integer + description: The number of tokens corresponding to audio input. + format: int32 + readOnly: true + reasoning_tokens: + type: integer + description: The number of tokens corresponding to reasoning. + format: int32 + readOnly: true + total_tokens: + type: integer + description: The total number of tokens processed for the completions request and response. + format: int32 + readOnly: true + description: A breakdown of tokens used in a completion. + EmbeddingEncodingFormat: + anyOf: + - type: string + - enum: + - base64 + - binary + - float + - int8 + - ubinary + - uint8 + type: string + description: "Specifies the types of embeddings to generate. Compressed embeddings types like `uint8`, `int8`, `ubinary` and \n`binary`, may reduce storage costs without sacrificing the integrity of the data. Returns a 422 error if the\nmodel doesn't support the value or parameter. Read the model's documentation to know the values supported by\nthe your model." + EmbeddingInputType: + anyOf: + - type: string + - enum: + - text + - query + - document + type: string + description: Represents the input types used for embedding search. + EmbeddingItem: + required: + - embedding + - index + - object + type: object + properties: + embedding: + type: array + items: + type: number + format: float + description: "List of embedding values for the input prompt. These represent a measurement of the\nvector-based relatedness of the provided input. Or a base64 encoded string of the embedding vector." + readOnly: true + index: + type: integer + description: Index of the prompt to which the EmbeddingItem corresponds. + format: int32 + readOnly: true + object: + enum: + - embedding + type: string + description: The object type of this embeddings item. Will always be `embedding`. + description: Representation of a single embeddings relatedness comparison. + EmbeddingsOptions: + required: + - input + type: object + properties: + input: + type: array + items: + type: string + description: "Input text to embed, encoded as a string or array of tokens.\nTo embed multiple inputs in a single request, pass an array\nof strings or array of token arrays." + dimensions: + type: integer + description: "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + format: int32 + encoding_format: + allOf: + - $ref: '#/components/schemas/EmbeddingEncodingFormat' + description: Optional. The desired format for the returned embeddings. + input_type: + allOf: + - $ref: '#/components/schemas/EmbeddingInputType' + description: "Optional. The type of the input.\nReturns a 422 error if the model doesn't support the value or parameter." + model: + type: string + description: 'ID of the specific AI model to use, if more than one model is available on the endpoint.' + additionalProperties: { } + description: The configuration information for an embeddings request. + EmbeddingsResult: + required: + - id + - data + - usage + - object + - model + type: object + properties: + id: + type: string + description: Unique identifier for the embeddings result. + readOnly: true + data: + type: array + items: + $ref: '#/components/schemas/EmbeddingItem' + description: Embedding values for the prompts submitted in the request. + readOnly: true + usage: + allOf: + - $ref: '#/components/schemas/EmbeddingsUsage' + description: Usage counts for tokens input using the embeddings API. + readOnly: true + object: + enum: + - list + type: string + description: The object type of the embeddings result. Will always be `list`. + model: + type: string + description: The model ID used to generate this result. + readOnly: true + description: "Representation of the response data from an embeddings request.\nEmbeddings measure the relatedness of text strings and are commonly used for search, clustering,\nrecommendations, and other similar scenarios." + EmbeddingsUsage: + required: + - prompt_tokens + - total_tokens + type: object + properties: + prompt_tokens: + type: integer + description: Number of tokens in the request. + format: int32 + readOnly: true + total_tokens: + type: integer + description: "Total number of tokens transacted in this request/response. Should equal the\nnumber of tokens in the request." + format: int32 + readOnly: true + description: Measurement of the amount of tokens used in this request and response. + ExtraParameters: + anyOf: + - type: string + - enum: + - error + - drop + - pass-through + type: string + description: 'Controls what happens if extra parameters, undefined by the REST API, are passed in the JSON request payload.' + FunctionCall: + required: + - name + - arguments + type: object + properties: + name: + type: string + description: The name of the function to call. + readOnly: true + arguments: + type: string + description: "The arguments to call the function with, as generated by the model in JSON format.\nNote that the model does not always generate valid JSON, and may hallucinate parameters\nnot defined by your function schema. Validate the arguments in your code before calling\nyour function." + readOnly: true + description: 'The name and arguments of a function that should be called, as generated by the model.' + FunctionDefinition: + required: + - name + type: object + properties: + name: + type: string + description: The name of the function to be called. + description: + type: string + description: "A description of what the function does. The model will use this description when selecting the function and\ninterpreting its parameters." + parameters: + type: object + additionalProperties: { } + description: 'The parameters the function accepts, described as a JSON Schema object.' + description: The definition of a caller-specified function that chat completions may invoke in response to matching user input. + ImageEmbeddingInput: + required: + - image + type: object + properties: + image: + type: string + description: 'The input image encoded in base64 string as a data URL. Example: `data:image/{format};base64,{data}`.' + text: + type: string + description: "Optional. The text input to feed into the model (like DINO, CLIP).\nReturns a 422 error if the model doesn't support the value or parameter." + description: Represents an image with optional text. + ImageEmbeddingsOptions: + required: + - input + type: object + properties: + input: + type: array + items: + $ref: '#/components/schemas/ImageEmbeddingInput' + description: "Input image to embed. To embed multiple inputs in a single request, pass an array.\nThe input must not exceed the max input tokens for the model." + dimensions: + type: integer + description: "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + format: int32 + encoding_format: + allOf: + - $ref: '#/components/schemas/EmbeddingEncodingFormat' + description: "Optional. The number of dimensions the resulting output embeddings should have.\nPassing null causes the model to use its default value.\nReturns a 422 error if the model doesn't support the value or parameter." + input_type: + allOf: + - $ref: '#/components/schemas/EmbeddingInputType' + description: "Optional. The type of the input.\nReturns a 422 error if the model doesn't support the value or parameter." + model: + type: string + description: 'ID of the specific AI model to use, if more than one model is available on the endpoint.' + additionalProperties: { } + description: The configuration information for an image embeddings request. + ImageGenerationItem: + required: + - image + type: object + properties: + image: + type: string + description: 'The image generated, encoded in base64.' + seed: + type: integer + description: The seed that can be used to generate the image. + format: int64 + description: Representation of a single image generation. + ImageGenerationOutputFormat: + anyOf: + - type: string + - enum: + - png + - jpg + type: string + description: The image generation format to use in the output. + ImageGenerationPreset: + anyOf: + - type: string + - enum: + - 672x1566 + - 768x1366 + - 836x1254 + - 916x1145 + - 1024x1024 + - 1145x916 + - 1254x836 + - 1366x768 + - 1566x672 + type: string + description: The preset size of the image to generate. + ImageGenerationPrompt: + required: + - image + type: object + properties: + image: + type: string + description: "The input image encoded in base64 string as a data URL.\nExample: `data:image/{format};base64,{data}`.\"" + strength: + type: number + description: "The degree at which the generation process uses the image prompt as guidance. 1 indicates\na generation process that fully follows the input image. 0 a generation a process that doesn't\ntake into consideration the prompt image." + format: float + default: 1 + description: Represents an image to use for guidance during the generation. + ImageGenerationQuality: + anyOf: + - type: string + - enum: + - hd + - standard + type: string + description: The quality of the image to generate. + ImageGenerationSize: + required: + - width + - height + type: object + properties: + width: + type: integer + description: 'The width of the image to generate, in pixels.' + format: int32 + height: + type: integer + description: 'The height of the image to generate, in pixels.' + format: int32 + description: The size of the image to generate. + ImageGenerationUsage: + required: + - image_generations + type: object + properties: + image_generations: + type: integer + description: The number of image generations performed. + format: int32 + description: Usage counts for image generation API. + ImageGenerationsOptions: + required: + - prompt + - size + - n + type: object + properties: + prompt: + type: string + description: "The prompt to use for the image geneartion. Read the model documentation to understand\nwhich language you should use to prompt the model and get specific results." + negative_prompt: + type: string + description: "Optional. The prompt to use for the negative image generation. Read the model documentation\nto understand to prompt the model and get specific results. If the model doesn't support\nnegative prompts, a 422 error is returned." + image_prompt: + allOf: + - $ref: '#/components/schemas/ImageGenerationPrompt' + description: Optional. An image to use as guidance for the image generation process. + size: + anyOf: + - $ref: '#/components/schemas/ImageGenerationPreset' + - $ref: '#/components/schemas/ImageGenerationSize' + description: The size of the image to generate. + output_format: + allOf: + - $ref: '#/components/schemas/ImageGenerationOutputFormat' + description: "Optional. The format in which to generate the image.\nReturns a 422 error if the model doesn't support the value or parameter." + default: png + quality: + allOf: + - $ref: '#/components/schemas/ImageGenerationQuality' + description: "Optional. The quality of the image to generate.\nReturns a 422 error if the model doesn't support the value or parameter." + default: standard + n: + type: integer + description: The number of images to generate. Defaults to 1. + format: int32 + default: 1 + model: + type: string + description: 'ID of the specific AI model to use, if more than one model is available on the endpoint.' + seed: + type: integer + description: "If specified, the system will make a best effort to sample deterministically such that repeated requests with the\nsame seed and parameters should return the same result. Determinism is not guaranteed." + format: int64 + additionalProperties: { } + description: The configuration information for an image generation request. + ImageGenerationsResult: + required: + - id + - created + - model + - data + - usage + - object + type: object + properties: + id: + type: string + description: Unique identifier for the generation result. + created: + type: integer + description: "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970." + format: unixtime + model: + type: string + description: The model used for the chat completion. + data: + type: array + items: + $ref: '#/components/schemas/ImageGenerationItem' + description: Embedding values for the prompts submitted in the request. + usage: + allOf: + - $ref: '#/components/schemas/ImageGenerationUsage' + description: Usage counts for tokens input using the embeddings API. + object: + enum: + - image.generation + type: string + description: The object type of the image generation result. Will always be `list`. + description: The result of an image generation request. + ModelInfo: + required: + - model_name + - model_type + - model_provider_name + type: object + properties: + model_name: + type: string + description: 'The name of the AI model. For example: `Phi21`' + readOnly: true + model_type: + allOf: + - $ref: '#/components/schemas/ModelType' + description: The type of the AI model. A Unique identifier for the profile. + readOnly: true + model_provider_name: + type: string + description: 'The model provider name. For example: `Microsoft`' + readOnly: true + description: Represents some basic information about the AI model. + ModelType: + anyOf: + - type: string + - enum: + - embeddings + - chat-completion + type: string + description: The type of AI model + PromptUsageDetails: + required: + - audio_tokens + - cached_tokens + type: object + properties: + audio_tokens: + type: integer + description: The number of tokens corresponding to audio input. + format: int32 + readOnly: true + cached_tokens: + type: integer + description: The total number of tokens cached. + format: int32 + readOnly: true + description: A breakdown of tokens used in the prompt/chat history. + StreamingChatChoiceUpdate: + required: + - index + - finish_reason + - delta + type: object + properties: + index: + type: integer + description: The ordered index associated with this chat completions choice. + format: int32 + finish_reason: + allOf: + - $ref: '#/components/schemas/CompletionsFinishReason' + description: The reason that this chat completions choice completed its generated. + nullable: true + readOnly: true + delta: + allOf: + - $ref: '#/components/schemas/StreamingChatResponseMessageUpdate' + description: An update to the chat message for a given chat completions prompt. + readOnly: true + description: "Represents an update to a single prompt completion when the service is streaming updates \nusing Server Sent Events (SSE).\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + StreamingChatCompletionsUpdate: + required: + - id + - object + - created + - model + - choices + type: object + properties: + id: + type: string + description: A unique identifier associated with this chat completions response. + object: + enum: + - chat.completion + type: string + description: 'The response object type, which is always `chat.completion`.' + created: + type: integer + description: "The first timestamp associated with generation activity for this completions response,\nrepresented as seconds since the beginning of the Unix epoch of 00:00 on 1 Jan 1970." + format: unixtime + readOnly: true + model: + type: string + description: The model used for the chat completion. + readOnly: true + choices: + minItems: 1 + type: array + items: + $ref: '#/components/schemas/StreamingChatChoiceUpdate' + description: "An update to the collection of completion choices associated with this completions response.\nGenerally, `n` choices are generated per provided prompt with a default value of 1.\nToken limits and other settings may limit the number of choices generated." + readOnly: true + usage: + allOf: + - $ref: '#/components/schemas/CompletionsUsage' + description: Usage information for tokens processed and generated as part of this completions operation. + readOnly: true + description: "Represents a response update to a chat completions request, when the service is streaming updates \nusing Server Sent Events (SSE).\nCompletions support a wide variety of tasks and generate text that continues from or \"completes\"\nprovided prompt data." + StreamingChatResponseMessageUpdate: + type: object + properties: + role: + allOf: + - $ref: '#/components/schemas/ChatRole' + description: 'The chat role associated with the message. If present, should always be ''assistant''' + readOnly: true + content: + type: string + description: The content of the message. + readOnly: true + reasoning_content: + type: string + description: The reasoning content the model used for generating the response + readOnly: true + tool_calls: + type: array + items: + $ref: '#/components/schemas/StreamingChatResponseToolCallUpdate' + description: "The tool calls that must be resolved and have their outputs appended to subsequent input messages for the chat\ncompletions request to resolve as configured." + readOnly: true + description: A representation of a chat message update as received in a streaming response. + StreamingChatResponseToolCallUpdate: + required: + - id + - function + type: object + properties: + id: + type: string + description: The ID of the tool call. + readOnly: true + function: + allOf: + - $ref: '#/components/schemas/FunctionCall' + description: Updates to the function call requested by the AI model. + readOnly: true + description: An update to the function tool call information requested by the AI model. + UserSecurityContext: + type: object + properties: + application_name: + maxLength: 100 + type: string + description: The name of the application. Sensitive personal information should not be included in this field. + end_user_id: + maxLength: 36 + minLength: 36 + pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' + type: string + description: This identifier is the Microsoft Entra ID (formerly Azure Active Directory) user object ID used to authenticate end-users within the generative AI application. Sensitive personal information should not be included in this field. + end_user_tenant_id: + maxLength: 36 + minLength: 36 + pattern: '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' + type: string + description: The Microsoft 365 tenant ID the end user belongs to. It's required when the generative AI application is multi tenant. + source_ip: + maxLength: 45 + minLength: 2 + type: string + description: 'Captures the original client''s IP address, accepting both IPv4 and IPv6 formats.' + description: "User security context contains several parameters that describe the AI application itself, and the end user that interacts with the AI application. \nThese fields assist your security operations teams to investigate and mitigate security incidents by providing a comprehensive approach to protecting your AI applications. \n[Learn more](https://aka.ms/TP4AI/Documentation/EndUserContext) about protecting AI applications using Microsoft Defender for Cloud." + Versions: + enum: + - 2024-05-01-preview + - '2025-04-01' + - '2025-05-01' + - 2025-05-15-preview + type: string + description: The AI.Model service versions. + securitySchemes: + apiKeyHeader: + type: apiKey + name: api-key + in: header + apiKeyQuery: + type: apiKey + name: subscription-key + in: query +security: + - apiKeyHeader: [ ] + - apiKeyQuery: [ ] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/azure-roles.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/azure-roles.json new file mode 100644 index 000000000..65ef928e9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/azure-roles.json @@ -0,0 +1,10 @@ +{ + "__source": "https://learn.microsoft.com/azure/role-based-access-control/built-in-roles", + "AcrPull": "7f951dda-4ed3-4680-a7ca-43fe172d538d", + "AzureEventHubsDataOwner": "f526a384-b230-433a-b45c-95f59c4a2dec", + "AzureEventHubsDataSender": "2b629674-e913-4c01-ae53-ef4638d8f975", + "CognitiveServicesOpenAIUser": "5e0bd9bd-7b93-4f28-af87-19fc36ad61bd", + "CognitiveServicesUser": "a97b65f3-24c7-4388-baec-2e87135dc908", + "SearchIndexDataContributor": "8ebe5a00-799e-43f5-93ac-243d3dce84a7", + "SearchServiceContributor": "7ca78c08-252a-4471-8644-bb5ff32d4ba0" +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/cosmos-db/cosmos-db.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/cosmos-db/cosmos-db.bicep new file mode 100644 index 000000000..bc19073b4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/cosmos-db/cosmos-db.bicep @@ -0,0 +1,282 @@ +@description('Azure Cosmos DB account name, max length 44 characters') +param accountName string + +@description('Location for the Azure Cosmos DB account.') +param location string = resourceGroup().location + +@description('The primary region for the Azure Cosmos DB account.') +param primaryRegion string = location + +param tags object = {} + +@allowed([ + 'Eventual' + 'ConsistentPrefix' + 'Session' + 'BoundedStaleness' + 'Strong' +]) +@description('The default consistency level of the Cosmos DB account.') +param defaultConsistencyLevel string = 'Session' + +@minValue(10) +@maxValue(2147483647) +@description('Max stale requests. Required for BoundedStaleness. Valid ranges, Single Region: 10 to 2147483647. Multi Region: 100000 to 2147483647.') +param maxStalenessPrefix int = 100000 + +@minValue(5) +@maxValue(86400) +@description('Max lag time (minutes). Required for BoundedStaleness. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400.') +param maxIntervalInSeconds int = 300 + +@allowed([ + true + false +]) +@description('Enable system managed failover for regions') +param systemManagedFailover bool = true + +@description('The name for the database') +param databaseName string = 'ai-usage-db' + +@description('The name for the container') +param containerName string = 'ai-usage-container' + +@description('The name for the container') +param pricingContainerName string = 'model-pricing' + +@description('The name for the container') +param piiUsageContainerName string = 'pii-usage-container' + +@description('The name for the container') +param llmUsageContainerName string = 'llm-usage-container' + +@description('The name for the container') +param streamingExportConfigContainerName string = 'streaming-export-config' + +@minValue(400) +@maxValue(1000000) +@description('The throughput for the container') +param throughput int = 400 + +var consistencyPolicy = { + Eventual: { + defaultConsistencyLevel: 'Eventual' + } + ConsistentPrefix: { + defaultConsistencyLevel: 'ConsistentPrefix' + } + Session: { + defaultConsistencyLevel: 'Session' + } + BoundedStaleness: { + defaultConsistencyLevel: 'BoundedStaleness' + maxStalenessPrefix: maxStalenessPrefix + maxIntervalInSeconds: maxIntervalInSeconds + } + Strong: { + defaultConsistencyLevel: 'Strong' + } +} +var locations = [ + { + locationName: primaryRegion + failoverPriority: 0 + isZoneRedundant: false + } +] + +// Networking +param cosmosPrivateEndpointName string +param vNetName string +param privateEndpointSubnetName string +param cosmosDnsZoneName string +param publicAccess string = 'Disabled' + +// Use existing network/dns zone - Legacy parameters (used when dnsZoneResourceId is not provided) +param dnsZoneRG string = '' +param dnsSubscriptionId string = '' + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubscriptionId) +param dnsZoneResourceId string = '' + +param vNetRG string +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +// Get existing subnet +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +resource account 'Microsoft.DocumentDB/databaseAccounts@2024-02-15-preview' = { + name: toLower(accountName) + location: location + tags: union(tags, { 'azd-service-name': accountName }) + kind: 'GlobalDocumentDB' + properties: { + consistencyPolicy: consistencyPolicy[defaultConsistencyLevel] + locations: locations + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: systemManagedFailover + disableKeyBasedMetadataWriteAccess: true + publicNetworkAccess: publicAccess + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-02-15-preview' = { + parent: account + name: databaseName + properties: { + resource: { + id: databaseName + } + } +} + +resource container 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-02-15-preview' = { + parent: database + name: containerName + properties: { + resource: { + id: containerName + partitionKey: { + paths: [ + '/productName' + ] + kind: 'Hash' + } + indexingPolicy: { + indexingMode: 'consistent' + automatic: true + } + } + options: { + throughput: throughput + } + } +} + +resource modelPricingContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-02-15-preview' = { + parent: database + name: pricingContainerName + properties: { + resource: { + id: pricingContainerName + partitionKey: { + paths: [ + '/model' + ] + kind: 'Hash' + } + indexingPolicy: { + indexingMode: 'consistent' + automatic: true + } + } + options: { + throughput: throughput + } + } +} + +resource streamingExportConfigContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-02-15-preview' = { + parent: database + name: streamingExportConfigContainerName + properties: { + resource: { + id: streamingExportConfigContainerName + partitionKey: { + paths: [ + '/type' + ] + kind: 'Hash' + } + indexingPolicy: { + indexingMode: 'consistent' + automatic: true + } + } + options: { + throughput: throughput + } + } +} + +resource piiUsageContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-02-15-preview' = { + parent: database + name: piiUsageContainerName + properties: { + resource: { + id: piiUsageContainerName + partitionKey: { + paths: [ + '/type' + ] + kind: 'Hash' + } + indexingPolicy: { + indexingMode: 'consistent' + automatic: true + } + } + options: { + throughput: throughput + } + } +} + +resource llmUsageContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-02-15-preview' = { + parent: database + name: llmUsageContainerName + properties: { + resource: { + id: llmUsageContainerName + partitionKey: { + paths: [ + '/productName' + ] + kind: 'Hash' + } + indexingPolicy: { + indexingMode: 'consistent' + automatic: true + } + } + options: { + throughput: throughput + } + } +} + +module privateEndpoint '../networking/private-endpoint.bicep' = { + name: '${accountName}-pe' + params: { + groupIds: [ + 'sql' + ] + dnsZoneName: cosmosDnsZoneName + name: cosmosPrivateEndpointName + privateLinkServiceId: account.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + tags: tags + } +} + +output location string = location +output cosmosDbAccountName string = account.name +output cosmosDbDatabaseName string = database.name +output cosmosDbContainerName string = container.name +output cosmosDbPiiUsageContainerName string = piiUsageContainer.name +output cosmosDbLLMUsageContainerName string = llmUsageContainer.name +output cosmosDbPricingContainerName string = modelPricingContainer.name +output cosmosDbStreamingExportConfigContainerName string = streamingExportConfigContainer.name +output resourceId string = database.id +output cosmosDbEndpoint string = 'https://${account.name}.documents.azure.com:443/' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/event-hub/event-hub.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/event-hub/event-hub.bicep new file mode 100644 index 000000000..4f7f4aa03 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/event-hub/event-hub.bicep @@ -0,0 +1,140 @@ +param name string +param location string = resourceGroup().location +param sku string = 'Standard' +param capacity int = 1 +param tags object = {} +param eventHubName string = 'ai-usage' + +param isPIIEnabled bool = true +param eventHubNamePII string = 'pii-usage' + +// Private networking parameters +param eventHubPrivateEndpointName string +param vNetName string +param privateEndpointSubnetName string +param eventHubDnsZoneName string +param publicNetworkAccess string = 'Enabled' + +// Use existing network/dns zone - Legacy parameters (used when dnsZoneResourceId is not provided) +param dnsZoneRG string = '' +param dnsSubscriptionId string = '' +param vNetRG string + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubscriptionId) +param dnsZoneResourceId string = '' + +// Optional: Network rule sets for additional security +// param ipRules array = [] +// param virtualNetworkRules array = [] +// param defaultAction string = 'Deny' + +// Disaster recovery parameters +param zoneRedundant bool = true +param disasterRecoveryConfig object = {} + +// Performance and retention parameters +param messageRetentionInDays int = 7 +param autoInflateEnabled bool = true +param maximumThroughputUnits int = 20 +param kafkaEnabled bool = false + +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +resource eventHubNamespace 'Microsoft.EventHub/namespaces@2025-05-01-preview' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + tier: sku + capacity: capacity + } + properties: { + isAutoInflateEnabled: autoInflateEnabled + maximumThroughputUnits: maximumThroughputUnits + publicNetworkAccess: publicNetworkAccess + zoneRedundant: zoneRedundant + kafkaEnabled: kafkaEnabled + } +} + +resource eventHub 'Microsoft.EventHub/namespaces/eventhubs@2025-05-01-preview' = { + parent: eventHubNamespace + name: eventHubName + properties: { + messageRetentionInDays: messageRetentionInDays + partitionCount: 4 + status: 'Active' + } +} + +resource eventHubPII 'Microsoft.EventHub/namespaces/eventhubs@2025-05-01-preview' = if (isPIIEnabled) { + parent: eventHubNamespace + name: eventHubNamePII + properties: { + messageRetentionInDays: messageRetentionInDays + partitionCount: 2 + status: 'Active' + } +} + +// Consider adding a consumer group for each consumer application +resource defaultConsumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2024-01-01' = { + name: '$Default' + parent: eventHub +} + +resource aiUsageConsumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2024-01-01' = { + name: 'aiUsageIngestion' + parent: eventHub +} + +resource defaultPIIConsumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2024-01-01' = if (isPIIEnabled) { + name: '$Default' + parent: eventHubPII +} + +resource piiUsageConsumerGroup 'Microsoft.EventHub/namespaces/eventhubs/consumergroups@2024-01-01' = if (isPIIEnabled) { + name: 'piiUsageIngestion' + parent: eventHubPII +} + +// Private endpoint for secure connectivity +module privateEndpoint '../networking/private-endpoint.bicep' = { + name: '${eventHubName}-pe' + params: { + groupIds: [ + 'namespace' + ] + dnsZoneName: eventHubDnsZoneName + name: eventHubPrivateEndpointName + privateLinkServiceId: eventHubNamespace.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + tags: tags + } +} + +// Optional: Add disaster recovery configuration if needed +resource disasterRecovery 'Microsoft.EventHub/namespaces/disasterRecoveryConfigs@2024-01-01' = if (!empty(disasterRecoveryConfig)) { + name: 'default' + parent: eventHubNamespace + properties: disasterRecoveryConfig +} + +output eventHubNamespaceName string = eventHubNamespace.name +output eventHubName string = 'ai-usage' +output eventHubEndpoint string = eventHubNamespace.properties.serviceBusEndpoint +output eventHubPIIName string = isPIIEnabled ? 'pii-usage' : '' +output eventHubResourceId string = eventHubNamespace.id diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/deployments.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/deployments.bicep new file mode 100644 index 000000000..69294932c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/deployments.bicep @@ -0,0 +1,26 @@ +@description('Configuration array for the model deployments') +param modelsConfig array = [] + +param cognitiveServiceName string + +resource cognitiveService 'Microsoft.CognitiveServices/accounts@2025-06-01' existing = { + name: cognitiveServiceName +} + +@batchSize(1) +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2025-06-01' = [for (model, i) in modelsConfig: { + name: model.name + parent: cognitiveService + sku: { + name: model.sku + capacity: model.capacity + } + properties: { + model: { + format: model.publisher + name: model.name + version: model.version + } + raiPolicyName: 'Microsoft.DefaultV2' + } +}] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/foundry.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/foundry.bicep new file mode 100644 index 000000000..4a98a84f5 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/foundry/foundry.bicep @@ -0,0 +1,332 @@ +/** + * @module openai-v2 + * @description This module defines the Azure Cognitive Services OpenAI resources using Bicep. + * This is version 2 (v2) of the OpenAI Bicep module. + */ + +// ------------------ +// PARAMETERS +// ------------------ + +// ------------------ + +// aiservices_config = [{"name": "foundry1", "location": "swedencentral"}, +// {"name": "foundry2", "location": "eastus2"}] + +// models_config = [{"name": "gpt-4o-mini", "publisher": "OpenAI", "version": "2024-07-18", "sku": "GlobalStandard", "capacity": 100}, +// {"name": "DeepSeek-R1", "publisher": "DeepSeek", "version": "1", "sku": "GlobalStandard", "capacity": 1}, +// {"name": "Phi-4", "publisher": "Microsoft", "version": "3", "sku": "GlobalStandard", "capacity": 1}] + +// aiservices_config = [{"name": "foundry1", "location": "eastus"}, +// {"name": "foundry2", "location": "swedencentral"}, +// {"name": "foundry3", "location": "eastus2"}] + +// models_config = [{"name": "gpt-4.1", "publisher": "OpenAI", "version": "2025-04-14", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry1"}, +// {"name": "gpt-4.1-mini", "publisher": "OpenAI", "version": "2025-04-14", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry2"}, +// {"name": "gpt-4.1-nano", "publisher": "OpenAI", "version": "2025-04-14", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry2"}, +// {"name": "model-router", "publisher": "OpenAI", "version": "2025-05-19", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry3"}, +// {"name": "gpt-5", "publisher": "OpenAI", "version": "2025-08-07", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry3"}, +// {"name": "DeepSeek-R1", "publisher": "DeepSeek", "version": "1", "sku": "GlobalStandard", "capacity": 20, "aiservice": "foundry3"}] + +@description('Configuration array for AI Foundry resources') +param aiServicesConfig array = [] + +@description('Configuration array for the model deployments') +param modelsConfig array = [] + +@description('Log Analytics Workspace Id') +param lawId string = '' + +@description('APIM Pricipal Id') +param apimPrincipalId string + +@description('AI Foundry project name') +param foundryProjectName string = 'citadel-governance-project' + +@description('The instrumentation key for Application Insights') +@secure() +param appInsightsInstrumentationKey string = '' + +@description('The resource ID for Application Insights') +param appInsightsId string = '' + +@description('Controls public network access for the Cognitive Services account') +@allowed(['Enabled', 'Disabled']) +param publicNetworkAccess string = 'Enabled' + +@description('Disable key based authentication, enabling only Azure AD authentication') +param disableKeyAuth bool = false + +@description('Main deployment resource token') +param resourceToken string = uniqueString(subscription().id, resourceGroup().id) + +@description('Tags to be applied to all resources') +param tags object = {} + +// ------------------ +// NETWORKING PARAMETERS +// ------------------ + +@description('Name of the Virtual Network') +param vNetName string + +@description('Location of the Virtual Network') +param vNetLocation string + +@description('Name of the private endpoint subnet') +param privateEndpointSubnetName string + +@description('Resource group containing the Virtual Network') +param vNetRG string + +@description('Base name for AI Foundry private endpoints. Leave blank to use default naming.') +param aiFoundryPrivateEndpointBaseName string = '' + +@description('DNS zone names for AI Foundry private endpoints (supports all required zones)') +param aiServicesDnsZoneNames array = [ + 'privatelink.cognitiveservices.azure.com' + 'privatelink.openai.azure.com' + 'privatelink.services.ai.azure.com' +] + +@description('Resource group containing the DNS zones (legacy - used when dnsZoneResourceId is not provided)') +param dnsZoneRG string = '' + +@description('Subscription ID containing the DNS zones (legacy - used when dnsZoneResourceIds is not provided)') +param dnsSubscriptionId string = '' + +@description('Array of direct DNS zone resource IDs (preferred over dnsZoneRG/dnsSubscriptionId). Order should match aiServicesDnsZoneNames.') +param dnsZoneResourceIds array = [] + +@description('Enable network injection for AI Foundry resources (requires additional configuration)') +param networkInjectionEnabled bool = false + +@description('Name of the subnet to use for AI Foundry network injection (required if network injection is enabled)') +param agentSubnetName string = '' +// ------------------ +// KEY VAULT PARAMETERS +// ------------------ + +@description('Key Vault resource ID for connection') +param keyVaultId string = '' + +@description('Key Vault URI for connection') +param keyVaultUri string = '' + +// ------------------ +// VARIABLES +// ------------------ + +var azureRoles = loadJsonContent('../azure-roles.json') +var cognitiveServicesUserRoleDefinitionID = resourceId('Microsoft.Authorization/roleDefinitions', azureRoles.CognitiveServicesUser) + +// Get existing VNet and subnet for private endpoint +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +resource agentSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = if (networkInjectionEnabled) { + name: agentSubnetName + parent: vnet +} + + +// ------------------ +// RESOURCES +// ------------------ +resource foundryResources 'Microsoft.CognitiveServices/accounts@2026-01-15-preview' = [for (config, i) in aiServicesConfig: { + name: !empty(config.name) ? config.name : 'aif-${resourceToken}-${i}' + location: config.location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'S0' + } + kind: 'AIServices' + properties: { + // required to work in AI Foundry + allowProjectManagement: true + customSubDomainName: toLower(!empty(config.customSubDomainName) ? config.customSubDomainName : (!empty(config.name) ? config.name : 'aif-${resourceToken}-${i}')) + + disableLocalAuth: disableKeyAuth + + publicNetworkAccess: publicNetworkAccess + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + ipRules: [] + virtualNetworkRules: [] + } + // Per-instance network injection: each entry in aiServicesConfig may set + // `networkInjectionEnabled: true|false`. When the property is omitted, the + // module-level `networkInjectionEnabled` flag (default) applies. Injection is + // only attempted when the agent subnet is available. + networkInjections: ((networkInjectionEnabled && (config.?networkInjectionEnabled ?? true)) ? [ + { + scenario: 'agent' + subnetArmId: agentSubnet.id + useMicrosoftManagedNetwork: false + } + ] : null) + } +}] + +resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = [for (config, i) in aiServicesConfig: { + #disable-next-line BCP334 + name: config.defaultProjectName != null ? config.defaultProjectName : foundryProjectName + parent: foundryResources[i] + location: config.location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + description: 'Citadel Governance Hub default project for AI Evaluation default LLMs' + } + dependsOn: [ + privateEndpoints + ] +}] + + +var aiProjectManagerRoleDefinitionID = 'eadc314b-1a2d-4efa-be10-5d325db5065e' +resource aiProjectManagerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (config, i) in aiServicesConfig: { + scope: foundryResources[i] + name: guid(subscription().id, resourceGroup().id, foundryResources[i].name, aiProjectManagerRoleDefinitionID) + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', aiProjectManagerRoleDefinitionID) + principalId: deployer().objectId + } +}] + + +// https://learn.microsoft.com/azure/templates/microsoft.insights/diagnosticsettings +resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [for (config, i) in aiServicesConfig: if (lawId != '') { + name: '${foundryResources[i].name}-diagnostics' + scope: foundryResources[i] + properties: { + workspaceId: lawId != '' ? lawId : null + logs: [] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +}] + +resource appInsightsConnection 'Microsoft.CognitiveServices/accounts/connections@2025-06-01' = [for (config, i) in aiServicesConfig: if (length(appInsightsId) > 0 && length(appInsightsInstrumentationKey) > 0) { + parent: foundryResources[i] + name: '${foundryResources[i].name}-appInsights-connection' + properties: { + authType: 'ApiKey' + category: 'AppInsights' + target: appInsightsId + useWorkspaceManagedIdentity: false + isSharedToAll: false + sharedUserList: [] + peRequirement: 'NotRequired' + peStatus: 'NotApplicable' + metadata: { + ApiType: 'Azure' + ResourceId: appInsightsId + } + credentials: { + key: appInsightsInstrumentationKey + } + } +}] + +// Key Vault connection for AI Foundry resources +// resource keyVaultConnection 'Microsoft.CognitiveServices/accounts/connections@2025-06-01' = [for (config, i) in aiServicesConfig: if (!empty(keyVaultId) && !empty(keyVaultUri)) { +// parent: foundryResources[i] +// name: 'keyvault-connection' +// properties: { +// authType: 'AccountManagedIdentity' +// category: 'AzureKeyVault' +// target: keyVaultUri +// useWorkspaceManagedIdentity: true +// isSharedToAll: true +// sharedUserList: [] +// peRequirement: 'NotRequired' +// peStatus: 'NotApplicable' +// metadata: { +// ApiType: 'Azure' +// ResourceId: keyVaultId +// } +// } +// }] + +resource roleAssignmentCognitiveServicesUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (config, i) in aiServicesConfig: { + scope: foundryResources[i] + name: guid(subscription().id, resourceGroup().id, foundryResources[i].name, cognitiveServicesUserRoleDefinitionID, apimPrincipalId) + properties: { + roleDefinitionId: cognitiveServicesUserRoleDefinitionID + principalId: apimPrincipalId + principalType: 'ServicePrincipal' + } +}] + +// Serialize model deployment modules across Foundry accounts to reduce +// transient parent-account operation conflicts during first-time provisioning. +@batchSize(1) +module modelDeployments 'deployments.bicep' = [for (config, i) in aiServicesConfig: { + name: take('models-${foundryResources[i].name}', 64) + params: { + cognitiveServiceName: foundryResources[i].name + modelsConfig: filter(modelsConfig, model => !contains(model, 'aiservice') || model.aiservice == foundryResources[i].name ) + } + dependsOn: [ + aiProject[i] + roleAssignmentCognitiveServicesUser[i] + ] +}] + +// Private endpoints for AI Foundry instances (with all 3 required DNS zones) +module privateEndpoints '../networking/private-endpoint-multi-dns.bicep' = [for (config, i) in aiServicesConfig: { + name: 'pe-${foundryResources[i].name}' + params: { + name: !empty(aiFoundryPrivateEndpointBaseName) ? '${aiFoundryPrivateEndpointBaseName}-${i}' : '${foundryResources[i].name}-pe' + privateLinkServiceId: foundryResources[i].id + groupIds: [ + 'account' + ] + dnsZoneNames: aiServicesDnsZoneNames + location: vNetLocation + privateEndpointSubnetId: subnet.id + dnsZoneRG: dnsZoneRG + dnsSubId: dnsSubscriptionId + dnsZoneResourceIds: dnsZoneResourceIds + tags: tags + } + dependsOn: [] +}] + + +// ------------------ +// OUTPUTS +// ------------------ + +output extendedAIServicesConfig array = [for (config, i) in aiServicesConfig: { + // Original openAIConfig properties + name: config.name + location: config.location + priority: config.?priority + weight: config.?weight + // Additional properties + cognitiveService: foundryResources[i] + cognitiveServiceName: foundryResources[i].name + endpoint: foundryResources[i].properties.endpoint + foundryProjectEndpoint: 'https://${foundryResources[i].name}.services.ai.azure.com/api/projects/${aiProject[i].name}' +}] + +output aiFoundryPrincipalIds array = [for (config, i) in aiServicesConfig: foundryResources[i].identity.principalId] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/functionapp.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/functionapp.bicep new file mode 100644 index 000000000..e8cbff015 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/functionapp.bicep @@ -0,0 +1,142 @@ + +param functionAppName string +param tags object = {} +param azdserviceName string +param storageAccountName string +param functionContentShareName string + +param functionAppIdentityName string + +param applicationInsightsName string +param eventHubNamespaceName string +param eventHubName string +//param vnetName string +param functionAppSubnetId string + +param cosmosDBEndpoint string +param cosmosDatabaseName string +param cosmosContainerName string + +param location string = resourceGroup().location + +var functionPlanOS = 'Linux' +var functionRuntime = 'dotnet-isolated' +var dotnetFrameworkVersion = '8.0' +var linuxFxVersion = 'DOTNET-ISOLATED|8.0' +var isReserved = functionPlanOS == 'Linux' + +resource functionAppmanagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = { + name: functionAppIdentityName +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { + name: storageAccountName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + + +var storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net' + +resource hostingPlan 'Microsoft.Web/serverfarms@2023-12-01' = { + name: 'hosting-plan-${functionAppName}' + tags: union(tags, { 'azd-service-name': 'hosting-plan-${functionAppName}' }) + location: location + sku: { + name: 'EP1' + tier: 'ElasticPremium' + family: 'EP' + } + kind: 'elastic' + properties: { + maximumElasticWorkerCount: 10 + reserved: isReserved + } +} + +resource functionApp 'Microsoft.Web/sites@2023-12-01' = { + name: functionAppName + location: location + kind: 'functionapp,linux' + tags: union(tags, { 'azd-service-name': azdserviceName }) + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${functionAppmanagedIdentity.id}': {} + } + } + properties: { + enabled: true + serverFarmId: hostingPlan.id + reserved: isReserved + virtualNetworkSubnetId: functionAppSubnetId + } +} + + +// Add the function to the subnet +resource networkConfig 'Microsoft.Web/sites/networkConfig@2023-12-01' = { + parent: functionApp + name: 'virtualNetwork' + properties: { + subnetResourceId: functionAppSubnetId + swiftSupported: true + } +} + +//create functionapp siteconfig +resource functionAppSiteConfig 'Microsoft.Web/sites/config@2023-12-01' = { + parent: functionApp + name: 'web' + properties: { + linuxFxVersion: linuxFxVersion + detailedErrorLoggingEnabled: true + vnetRouteAllEnabled: true + ftpsState: 'FtpsOnly' + minTlsVersion: '1.2' + scmMinTlsVersion: '1.2' + minimumElasticInstanceCount: 1 + //vnetName: vnetName + publicNetworkAccess: 'Enabled' + functionsRuntimeScaleMonitoringEnabled: true + netFrameworkVersion: dotnetFrameworkVersion + } + dependsOn: [ + applicationInsights + ] +} + +//Create functionapp appsettings + +resource functionAppSettings 'Microsoft.Web/sites/config@2023-12-01' = { + parent: functionApp + name: 'appsettings' + properties: { + APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString + AzureWebJobsStorage: storageAccountConnectionString + //AzureWebJobsStorage__accountname: storageAccountName + FUNCTIONS_EXTENSION_VERSION: '~4' + FUNCTIONS_WORKER_RUNTIME: functionRuntime + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: storageAccountConnectionString + WEBSITE_CONTENTSHARE: functionContentShareName + WEBSITE_VNET_ROUTE_ALL: '1' + WEBSITE_CONTENTOVERVNET: '1' + //EventHub Input Trigger Settings With Managed Identity + //https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=eventhubs&pivots=programming-language-csharp#common-properties-for-identity-based-connections + EventHubConnection__clientId: functionAppmanagedIdentity.properties.clientId + EventHubConnection__credential: 'managedidentity' + EventHubConnection__fullyQualifiedNamespace: '${eventHubNamespaceName}.servicebus.windows.net' + EventHubName: eventHubName + + //CosmosDB + CosmosAccountEndpoint: cosmosDBEndpoint + CosmosDatabaseName: cosmosDatabaseName + CosmosContainerName: cosmosContainerName + CosmosManagedIdentityId: functionAppmanagedIdentity.properties.clientId + } + dependsOn: [ + storageAccount + ] +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/storageaccount.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/storageaccount.bicep new file mode 100644 index 000000000..868856b03 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/functionapp/storageaccount.bicep @@ -0,0 +1,166 @@ +param storageAccountName string +param location string = resourceGroup().location +param tags object = {} + +param functionAppManagedIdentityName string + +//Networking +param vNetName string +param privateEndpointSubnetName string +param storageBlobDnsZoneName string +param storageBlobPrivateEndpointName string +param storageFileDnsZoneName string +param storageFilePrivateEndpointName string +param storageTableDnsZoneName string +param storageTablePrivateEndpointName string +param storageQueueDnsZoneName string +param storageQueuePrivateEndpointName string +// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#storage-blob-data-owner +var storageBlobDataOwnerRoleId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b') + +// Use existing network/dns zone - Legacy parameters (used when dnsZoneResourceId is not provided) +param dnsZoneRG string = '' +param dnsSubscriptionId string = '' +param vNetRG string + +// New parameters: Direct DNS zone resource IDs (preferred over dnsZoneRG/dnsSubscriptionId) +param storageBlobDnsZoneResourceId string = '' +param storageFileDnsZoneResourceId string = '' +param storageTableDnsZoneResourceId string = '' +param storageQueueDnsZoneResourceId string = '' + +param provisionLogicShare bool = true + +param logicContentShareName string + +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +// Get existing subnet +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +resource functionAppmanagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: functionAppManagedIdentityName +} + +@description('Storage Account type') +@allowed([ + 'Standard_LRS' + 'Standard_GRS' + 'Standard_RAGRS' +]) +param storageAccountType string = 'Standard_LRS' + + +resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: storageAccountName + location: location + tags: union(tags, { 'azd-service-name': storageAccountName }) + sku: { + name: storageAccountType + } + kind: 'StorageV2' + properties: { + supportsHttpsTrafficOnly: true + minimumTlsVersion: 'TLS1_2' + publicNetworkAccess: 'Disabled' + allowBlobPublicAccess: false + accessTier: 'Hot' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + } +} + +resource shareLogicApp 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-05-01' = if (provisionLogicShare) { + name: '${storageAccountName}/default/${logicContentShareName}' + dependsOn: [ + storageAccount + ] +} + +resource storageAccountFunctionAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(storageAccount.id, functionAppmanagedIdentity.name, storageBlobDataOwnerRoleId) + properties: { + principalId: functionAppmanagedIdentity.properties.principalId + roleDefinitionId: storageBlobDataOwnerRoleId + } + scope: storageAccount +} + +module privateEndpointBlob '../networking/private-endpoint.bicep' = { + name: '${storageAccountName}-blob-pe' + params: { + groupIds: [ + 'blob' + ] + dnsZoneName: storageBlobDnsZoneName + name: storageBlobPrivateEndpointName + privateLinkServiceId: storageAccount.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: storageBlobDnsZoneResourceId + } +} + +module privateEndpointFile '../networking/private-endpoint.bicep' = { + name: '${storageAccountName}-file-pe' + params: { + groupIds: [ + 'file' + ] + dnsZoneName: storageFileDnsZoneName + name: storageFilePrivateEndpointName + privateLinkServiceId: storageAccount.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: storageFileDnsZoneResourceId + } +} + +module privateEndpointTable '../networking/private-endpoint.bicep' = { + name: '${storageAccountName}-table-pe' + params: { + groupIds: [ + 'table' + ] + dnsZoneName: storageTableDnsZoneName + name: storageTablePrivateEndpointName + privateLinkServiceId: storageAccount.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: storageTableDnsZoneResourceId + } +} + +module privateEndpointQueue '../networking/private-endpoint.bicep' = { + name: '${storageAccountName}-queue-pe' + params: { + groupIds: [ + 'queue' + ] + dnsZoneName: storageQueueDnsZoneName + name: storageQueuePrivateEndpointName + privateLinkServiceId: storageAccount.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: storageQueueDnsZoneResourceId + } +} + + +output storageAccountName string = storageAccount.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-rbac.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-rbac.bicep new file mode 100644 index 000000000..ad361beed --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-rbac.bicep @@ -0,0 +1,36 @@ +@description('Key Vault resource ID to assign roles on') +param keyVaultName string + +@description('Principal IDs for AI Foundry resources to grant Key Vault access') +param aiFoundryPrincipalIds array = [] + +// Key Vault built-in roles +var keyVaultSecretsUserRoleId = '4633458b-17de-408a-b874-0445c86b69e6' +var keyVaultCertificatesOfficerRoleId = 'a4417e6f-fecd-4de8-b567-7b0420556985' + +// Reference existing Key Vault +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: keyVaultName +} + +// RBAC: Grant AI Foundry resources Key Vault Secrets User role +resource aiFoundrySecretsUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (principalId, i) in aiFoundryPrincipalIds: if (!empty(principalId)) { + scope: keyVault + name: guid(subscription().id, resourceGroup().id, keyVault.name, keyVaultSecretsUserRoleId, principalId) + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretsUserRoleId) + principalId: principalId + principalType: 'ServicePrincipal' + } +}] + +// RBAC: Grant AI Foundry resources Key Vault Certificates Officer role +resource aiFoundryCertificatesOfficerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for (principalId, i) in aiFoundryPrincipalIds: if (!empty(principalId)) { + scope: keyVault + name: guid(subscription().id, resourceGroup().id, keyVault.name, keyVaultCertificatesOfficerRoleId, principalId) + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', keyVaultCertificatesOfficerRoleId) + principalId: principalId + principalType: 'ServicePrincipal' + } +}] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-secret.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-secret.bicep new file mode 100644 index 000000000..d670abf9e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault-secret.bicep @@ -0,0 +1,25 @@ +@description('Name of the existing Key Vault') +param keyVaultName string + +@description('Name of the secret to create or update') +param secretName string + +@secure() +@description('Value of the secret') +param secretValue string + +resource kv 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: keyVaultName +} + +resource secret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + name: secretName + parent: kv + properties: { + value: secretValue + contentType: 'string' + } +} + +output secretUri string = secret.properties.secretUri +output secretName string = secret.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault.bicep new file mode 100644 index 000000000..e45a217f2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/keyvault/keyvault.bicep @@ -0,0 +1,151 @@ +@description('Name of the Key Vault') +param keyVaultName string + +@description('Location for the Key Vault') +param location string = resourceGroup().location + +@description('Tags to be applied to the Key Vault') +param tags object = {} + +@description('SKU for the Key Vault') +@allowed(['standard', 'premium']) +param skuName string = 'standard' + +@description('SKU family for the Key Vault') +param skuFamily string = 'A' + +@description('Enable soft delete for the Key Vault') +param enableSoftDelete bool = true + +@description('Soft delete retention in days') +@minValue(7) +@maxValue(90) +param softDeleteRetentionInDays int = 90 + +@description('Enable purge protection for the Key Vault') +param enablePurgeProtection bool = true + +@description('Enable RBAC authorization for the Key Vault') +param enableRbacAuthorization bool = true + +@description('Public network access for the Key Vault') +@allowed(['Enabled', 'Disabled']) +param publicNetworkAccess string = 'Disabled' + +@description('Enable vault for deployment') +param enabledForDeployment bool = false + +@description('Enable vault for disk encryption') +param enabledForDiskEncryption bool = false + +@description('Enable vault for template deployment') +param enabledForTemplateDeployment bool = true + +// Networking parameters +@description('Name of the Virtual Network') +param vNetName string + +@description('Name of the private endpoint subnet') +param privateEndpointSubnetName string + +@description('Resource group containing the Virtual Network') +param vNetRG string + +@description('Name of the Key Vault private endpoint') +param keyVaultPrivateEndpointName string + +@description('DNS zone name for Key Vault private endpoint') +param keyVaultDnsZoneName string = 'privatelink.vaultcore.azure.net' + +// DNS Zone parameters (legacy approach - used when dnsZoneResourceId is not provided) +@description('Resource group containing the DNS zones') +param dnsZoneRG string = '' + +@description('Subscription ID containing the DNS zones') +param dnsSubscriptionId string = '' + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubscriptionId) +@description('Direct DNS zone resource ID for Key Vault (preferred over dnsZoneRG/dnsSubscriptionId)') +param dnsZoneResourceId string = '' + +// RBAC assignments +@description('Principal ID of the APIM managed identity for RBAC assignments') +param apimPrincipalId string = '' + +// Key Vault built-in roles +var keyVaultSecretsUserRoleId = '4633458b-17de-408a-b874-0445c86b69e6' + +// Get existing VNet and subnet for private endpoint +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +// Key Vault resource +resource keyVault 'Microsoft.KeyVault/vaults@2025-05-01' = { + name: keyVaultName + location: location + tags: union(tags, { 'azd-service-name': keyVaultName }) + properties: { + tenantId: subscription().tenantId + sku: { + family: skuFamily + name: skuName + } + enableSoftDelete: enableSoftDelete + softDeleteRetentionInDays: softDeleteRetentionInDays + enablePurgeProtection: enablePurgeProtection ? true : null + enableRbacAuthorization: enableRbacAuthorization + enabledForDeployment: enabledForDeployment + enabledForDiskEncryption: enabledForDiskEncryption + enabledForTemplateDeployment: enabledForTemplateDeployment + publicNetworkAccess: publicNetworkAccess + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + ipRules: [] + virtualNetworkRules: [] + } + } +} + +// Private endpoint for Key Vault +module privateEndpoint '../networking/private-endpoint.bicep' = { + name: '${keyVaultName}-pe' + params: { + groupIds: [ + 'vault' + ] + dnsZoneName: keyVaultDnsZoneName + name: keyVaultPrivateEndpointName + privateLinkServiceId: keyVault.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + tags: tags + } +} + +// RBAC: Grant APIM managed identity Key Vault Secrets User role +resource apimSecretsUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(apimPrincipalId)) { + scope: keyVault + name: guid(subscription().id, resourceGroup().id, keyVault.name, keyVaultSecretsUserRoleId, apimPrincipalId) + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', keyVaultSecretsUserRoleId) + principalId: apimPrincipalId + principalType: 'ServicePrincipal' + } +} + +// Outputs +output keyVaultName string = keyVault.name +output keyVaultId string = keyVault.id +output keyVaultUri string = keyVault.properties.vaultUri +output location string = location diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection-access.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection-access.bicep new file mode 100644 index 000000000..3bf5a1c4c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection-access.bicep @@ -0,0 +1,23 @@ +param connectionName string +param accessPolicyName string +param identityPrincipalId string +param location string = resourceGroup().location +param tags object = {} + +resource logicAppConnectionExisting 'Microsoft.Web/connections@2016-06-01' existing = { + name: connectionName + resource accessPolicy 'accessPolicies@2016-06-01' = { + name: accessPolicyName + location: location + tags: tags + properties: { + principal: { + type: 'ActiveDirectory' + identity: { + tenantId: subscription().tenantId + objectId: identityPrincipalId + } + } + } + } +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection.json new file mode 100644 index 000000000..a72d8361e --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/api-connection.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "connection_name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": { + "environment": "dev", + "owner": "platform" + } + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.Web/connections", + "name": "[parameters('connection_name')]", + "apiVersion": "[providers('Microsoft.Web','connections').apiVersions[0]]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "V2", + "properties": { + "alternativeParameterValues": {}, + "api": { + "id": "[subscriptionResourceId('Microsoft.Web/locations/managedApis', parameters('location'), parameters('connection_name'))]" + }, + "authenticatedUser": {}, + "connectionState": "Enabled", + "customParameterValues": {}, + "displayName": "[parameters('display_name')]", + "parameterValueSet": { + "name": "managedIdentityAuth", + "values": {} + } + } + } + ], + "outputs": { + "connectRuntimeUrl": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Web/connections', parameters('connection_name')), '2016-06-01').connectionRuntimeUrl]" + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Web/connections', parameters('connection_name'))]" + }, + "apiId": { + "type": "string", + "value": "[subscriptionResourceId('Microsoft.Web/locations/managedApis', parameters('location'), parameters('connection_name'))]" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/logicapp.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/logicapp.bicep new file mode 100644 index 000000000..d2789cb46 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/logicapp/logicapp.bicep @@ -0,0 +1,212 @@ +param logicAppName string + +param tags object = {} +param azdserviceName string + +param storageAccountName string +param fileShareName string + +param applicationInsightsName string + +param location string = resourceGroup().location + +param skuName string +param skuFamily string +param skuSize string +param skuCapaicty int +param skuTier string +param isReserved bool + +param cosmosDbAccountName string + +param functionAppSubnetId string + +param dotnetFrameworkVersion string = 'v6.0' + +var docDbAccNativeContributorRoleDefinitionId = '00000000-0000-0000-0000-000000000002' +var eventHubsDataOwnerRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'f526a384-b230-433a-b45c-95f59c4a2dec') +var azureMonitorLogsRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + +param eventHubNamespaceName string +param eventHubName string +param eventHubPIIName string + +param cosmosDBDatabaseName string +param cosmosDBContainerConfigName string +param cosmosDBContainerUsageName string +param cosmosDBContainerPIIName string +param cosmosDBContainerLLMUsageName string + +param apimAppInsightsName string + +resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2024-02-15-preview' existing = { + name: cosmosDbAccountName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { + name: storageAccountName +} + +var storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=core.windows.net' + +resource hostingPlan 'Microsoft.Web/serverfarms@2024-04-01' = { + name: 'hosting-plan-${logicAppName}' + tags: union(tags, { 'azd-service-name': 'hosting-plan-${logicAppName}' }) + location: location + sku: { + name: skuName //'WS1' + tier: skuTier //'WorkflowStandard' + family: skuFamily //'WS' + size: skuSize //'WS1' + capacity: skuCapaicty //1 + } + kind: 'elastic' + properties: { + maximumElasticWorkerCount: 20 + reserved: isReserved + } +} + +resource logicApp 'Microsoft.Web/sites@2024-04-01' = { + name: logicAppName + location: location + kind: 'functionapp,workflowapp' + tags: union(tags, { 'azd-service-name': azdserviceName }) + identity: { + type: 'SystemAssigned' + } + properties: { + enabled: true + serverFarmId: hostingPlan.id + reserved: isReserved + virtualNetworkSubnetId: functionAppSubnetId + } +} + +resource networkConfig 'Microsoft.Web/sites/networkConfig@2024-04-01' = { + parent: logicApp + name: 'virtualNetwork' + properties: { + subnetResourceId: functionAppSubnetId + swiftSupported: true + } +} + +resource functionAppSiteConfig 'Microsoft.Web/sites/config@2024-04-01' = { + parent: logicApp + name: 'web' + properties: { + detailedErrorLoggingEnabled: true + vnetRouteAllEnabled: true + ftpsState: 'FtpsOnly' + minTlsVersion: '1.2' + scmMinTlsVersion: '1.2' + minimumElasticInstanceCount: 1 + publicNetworkAccess: 'Enabled' + functionsRuntimeScaleMonitoringEnabled: true + netFrameworkVersion: dotnetFrameworkVersion + preWarmedInstanceCount: 1 + cors: { + allowedOrigins: ['https://portal.azure.com', 'https://ms.portal.azure.com'] + supportCredentials: false + } + } + dependsOn: [ + applicationInsights + ] +} + +resource functionAppSettings 'Microsoft.Web/sites/config@2024-04-01' = { + parent: logicApp + name: 'appsettings' + properties: { + APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString + AzureWebJobsStorage: storageAccountConnectionString + //AzureWebJobsStorage__accountname: storageAccountName + FUNCTIONS_EXTENSION_VERSION: '~4' + FUNCTIONS_WORKER_RUNTIME: 'node' + WEBSITE_NODE_DEFAULT_VERSION: '~20' + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: storageAccountConnectionString + WEBSITE_CONTENTSHARE: fileShareName + WEBSITE_VNET_ROUTE_ALL: '0' + WEBSITE_CONTENTOVERVNET: '1' + eventHub_fullyQualifiedNamespace: '${eventHubNamespaceName}.servicebus.windows.net' + eventHub_name: eventHubName + eventHub_pii_name: eventHubPIIName + APP_KIND: 'workflowapp' + AzureFunctionsJobHost_extensionBundle: 'Microsoft.Azure.Functions.ExtensionBundle.Workflows' + CosmosDBAccount: cosmosDbAccount.name + CosmosDBDatabase: cosmosDBDatabaseName + CosmosDBContainerConfig: cosmosDBContainerConfigName + CosmosDBContainerUsage: cosmosDBContainerUsageName + CosmosDBContainerPII: cosmosDBContainerPIIName + CosmosDBContainerLLMUsage: cosmosDBContainerLLMUsageName + AzureCosmosDB_connectionString: cosmosDbAccount.listConnectionStrings().connectionStrings[0].connectionString + AppInsights_SubscriptionId: subscription().subscriptionId + AppInsights_ResourceGroup: resourceGroup().name + AppInsights_Name: apimAppInsightsName + AzureMonitor_Resource_Id: azureMonitorConnection.outputs.resourceId + AzureMonitor_Api_Id: azureMonitorConnection.outputs.apiId + AzureMonitor_ConnectRuntime_Url: azureMonitorConnection.outputs.connectRuntimeUrl + } + dependsOn: [ + storageAccount + azureMonitorConnection + ] +} + +resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = { + name: guid(docDbAccNativeContributorRoleDefinitionId, logicAppName, cosmosDbAccount.id) + parent: cosmosDbAccount + properties:{ + principalId: logicApp.identity.principalId + roleDefinitionId: '/${cosmosDbAccount.id}/sqlRoleDefinitions/${docDbAccNativeContributorRoleDefinitionId}' + scope: cosmosDbAccount.id + } +} + +// Assign to Azure Event Hubs Data Owner role to the user-defined managed identity used by workloads +resource eventHubsDataOwnerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(logicAppName, eventHubsDataOwnerRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: eventHubsDataOwnerRoleDefinitionId + principalId: logicApp.identity.principalId + principalType: 'ServicePrincipal' + } +} + +resource azureMonitorReaderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(logicAppName, azureMonitorLogsRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: azureMonitorLogsRoleDefinitionId + principalId: logicApp.identity.principalId + principalType: 'ServicePrincipal' + } +} + +module azureMonitorConnection 'api-connection.json' = { + name: 'azuremonitorlogs-conn' + params: { + connection_name: 'azuremonitorlogs' + display_name: 'conn-azure-monitor' + location: location + tags: tags + } +} + +module azureMonitorConnectionAccess 'api-connection-access.bicep' = { + name: 'azuremonitorlogs-access' + params: { + connectionName: 'azuremonitorlogs' + accessPolicyName: 'azuremonitorlogs-access' + identityPrincipalId: logicApp.identity.principalId + location: location + tags: tags + } +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights-dashboard.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 000000000..b7af2c1a1 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1235 @@ +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights.bicep new file mode 100644 index 000000000..83dcb54f6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/applicationinsights.bicep @@ -0,0 +1,52 @@ +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} + +param logAnalyticsWorkspaceId string + +param createDashboard bool + +// Networking +param privateLinkScopeName string + +resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-preview' existing = if (privateLinkScopeName != '') { + name: privateLinkScopeName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + publicNetworkAccessForIngestion: privateLinkScopeName != '' ? 'Disabled' : 'Enabled' + publicNetworkAccessForQuery: privateLinkScopeName != '' ? 'Enabled' : 'Enabled' + CustomMetricsOptedInType: 'WithDimensions' + } +} + +resource appInsightsScopedResource 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = if (privateLinkScopeName != '') { + parent: privateLinkScope + name: '${applicationInsights.name}-connection' + properties: { + linkedResourceId: applicationInsights.id + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if(createDashboard) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + tags: tags + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name +output id string = applicationInsights.id diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/loganalytics.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/loganalytics.bicep new file mode 100644 index 000000000..3f874dc7c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/loganalytics.bicep @@ -0,0 +1,51 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +// Networking +param privateLinkScopeName string + +// Existing Log Analytics workspace parameters +param useExistingLogAnalytics bool = false +param existingLogAnalyticsName string = '' +param existingLogAnalyticsRG string = '' +param existingLogAnalyticsSubscriptionId string = '' + +resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-preview' existing = if (privateLinkScopeName != '') { + name: privateLinkScopeName +} + +// Reference existing Log Analytics workspace (potentially cross-subscription) +resource existingLogAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (useExistingLogAnalytics) { + name: existingLogAnalyticsName + scope: resourceGroup(existingLogAnalyticsSubscriptionId, existingLogAnalyticsRG) +} + +// Create new Log Analytics workspace +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = if (!useExistingLogAnalytics) { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + publicNetworkAccessForIngestion: privateLinkScopeName != '' ? 'Disabled' : 'Enabled' + publicNetworkAccessForQuery: privateLinkScopeName != '' ? 'Enabled' : 'Enabled' + }) +} + +resource logAnalyticsScopedResource 'Microsoft.Insights/privateLinkScopes/scopedResources@2021-07-01-preview' = if (privateLinkScopeName != '' && !useExistingLogAnalytics) { + parent: privateLinkScope + name: '${logAnalytics.name}-connection' + properties: { + linkedResourceId: logAnalytics.id + } +} + +output id string = useExistingLogAnalytics ? existingLogAnalytics.id : logAnalytics.id +output name string = useExistingLogAnalytics ? existingLogAnalytics.name : logAnalytics.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/monitoring.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/monitoring.bicep new file mode 100644 index 000000000..c27321fd0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/monitor/monitoring.bicep @@ -0,0 +1,145 @@ +param logAnalyticsName string +param useExistingLogAnalytics bool = false +param existingLogAnalyticsName string = '' +param existingLogAnalyticsRG string = '' +param existingLogAnalyticsSubscriptionId string = '' +param apimApplicationInsightsName string +param apimApplicationInsightsDashboardName string +param functionApplicationInsightsName string +param functionApplicationInsightsDashboardName string +param foundryApplicationInsightsName string +param foundryApplicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} + +param createDashboard bool + +// Networking +param usePrivateLinkScope bool = true +var privateLinkScopeName = 'ampls-monitoring' +param vNetName string +param privateEndpointSubnetName string +param applicationInsightsDnsZoneName string + +// Use existing network/dns zone - Legacy parameters (used when dnsZoneResourceId is not provided) +param dnsZoneRG string = '' +param dnsSubscriptionId string = '' +param vNetRG string + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubscriptionId) +param dnsZoneResourceId string = '' +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +// Get existing subnet +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +resource privateLinkScope 'microsoft.insights/privateLinkScopes@2021-07-01-preview' = if (usePrivateLinkScope) { + name: privateLinkScopeName + location: 'global' + tags: tags + properties: { + accessModeSettings: { + ingestionAccessMode: 'Open' + queryAccessMode: 'Open' + } + } +} + +module logAnalytics 'loganalytics.bicep' = { + name: 'log-analytics' + params: { + name: logAnalyticsName + location: location + tags: tags + privateLinkScopeName: usePrivateLinkScope ? privateLinkScopeName : '' + useExistingLogAnalytics: useExistingLogAnalytics + existingLogAnalyticsName: existingLogAnalyticsName + existingLogAnalyticsRG: existingLogAnalyticsRG + existingLogAnalyticsSubscriptionId: existingLogAnalyticsSubscriptionId + } +} + +// APIM App Insights +module apimApplicationInsights 'applicationinsights.bicep' = { + name: 'application-insights' + params: { + name: apimApplicationInsightsName + location: location + tags: tags + dashboardName: apimApplicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + privateLinkScopeName: usePrivateLinkScope ? privateLinkScopeName : '' + createDashboard: createDashboard + } +} + +// Function App Insights +module functionApplicationInsights 'applicationinsights.bicep' = { + name: 'func-application-insights' + params: { + name: functionApplicationInsightsName + location: location + tags: tags + dashboardName: functionApplicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + privateLinkScopeName: usePrivateLinkScope ? privateLinkScopeName : '' + createDashboard: createDashboard + } +} + +module foundryApplicationInsights 'applicationinsights.bicep' = { + name: 'foundry-application-insights' + params: { + name: foundryApplicationInsightsName + location: location + tags: tags + dashboardName: foundryApplicationInsightsDashboardName + logAnalyticsWorkspaceId: logAnalytics.outputs.id + privateLinkScopeName: usePrivateLinkScope ? privateLinkScopeName : '' + createDashboard: createDashboard + } +} + +module privateEndpoint '../networking/private-endpoint.bicep' = if (usePrivateLinkScope) { + name: '${privateLinkScopeName}-pe' + params: { + groupIds: [ + 'azuremonitor' + ] + dnsZoneName: applicationInsightsDnsZoneName + name: '${privateLinkScopeName}-pe' + privateLinkServiceId: privateLinkScope.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + enableDnsIntegration: usePrivateLinkScope + tags: tags + } + dependsOn: [ + logAnalytics + apimApplicationInsights + functionApplicationInsights + ] +} + +output apimApplicationInsightsName string = apimApplicationInsights.outputs.name +output apimApplicationInsightsConnectionString string = apimApplicationInsights.outputs.connectionString +output apimApplicationInsightsInstrumentationKey string = apimApplicationInsights.outputs.instrumentationKey +output apimApplicationInsightsId string = apimApplicationInsights.outputs.id +output funcApplicationInsightsName string = functionApplicationInsights.outputs.name +output funcApplicationInsightsConnectionString string = functionApplicationInsights.outputs.connectionString +output funcApplicationInsightsInstrumentationKey string = functionApplicationInsights.outputs.instrumentationKey +output foundryApplicationInsightsName string = foundryApplicationInsights.outputs.name +output foundryApplicationInsightsConnectionString string = foundryApplicationInsights.outputs.connectionString +output foundryApplicationInsightsId string = foundryApplicationInsights.outputs.id +output foundryApplicationInsightsInstrumentationKey string = foundryApplicationInsights.outputs.instrumentationKey +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/dns.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/dns.bicep new file mode 100644 index 000000000..d54d012b7 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/dns.bicep @@ -0,0 +1,10 @@ +param name string +param tags object = {} + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: name + location: 'global' + tags: union(tags, { 'azd-service-name': name }) +} + +output privateDnsZoneName string = privateDnsZone.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint-multi-dns.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint-multi-dns.bicep new file mode 100644 index 000000000..307a8af68 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint-multi-dns.bicep @@ -0,0 +1,86 @@ +/** + * @module private-endpoint-multi-dns + * @description Private endpoint module that supports multiple DNS zones + * Used for resources like AI Foundry that require multiple DNS zone configurations + */ + +param name string +param privateLinkServiceId string +param groupIds array +param location string +param privateEndpointSubnetId string + +@description('Array of DNS zone names to configure for this private endpoint') +param dnsZoneNames array = [] + +// Legacy parameters for backward compatibility (used when dnsZoneResourceIds is not provided) +param dnsZoneRG string = '' +param dnsSubId string = '' + +// New parameter: Array of direct DNS zone resource IDs (preferred over dnsZoneRG/dnsSubId) +@description('Array of DNS zone resource IDs. When provided, takes precedence over dnsZoneRG/dnsSubId lookup') +param dnsZoneResourceIds array = [] + +param tags object = {} + +// Add a parameter to control DNS integration +param enableDnsIntegration bool = length(dnsZoneResourceIds) > 0 || (!empty(dnsZoneRG) && length(dnsZoneNames) > 0) + +// Determine if using legacy lookup +var useLegacyDnsLookup = length(dnsZoneResourceIds) == 0 && !empty(dnsZoneRG) && length(dnsZoneNames) > 0 + +// Reference existing DNS zones using legacy lookup (when dnsZoneResourceIds not provided) +resource privateEndpointDnsZones 'Microsoft.Network/privateDnsZones@2024-06-01' existing = [for zoneName in dnsZoneNames: if (useLegacyDnsLookup) { + name: zoneName + scope: resourceGroup(dnsSubId, dnsZoneRG) +}] + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2025-05-01' = { + name: name + location: location + tags: tags + properties: { + subnet: { + id: privateEndpointSubnetId + } + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: privateLinkServiceId + groupIds: groupIds + } + } + ] + } +} + +// DNS zone group using direct resource IDs (preferred) +resource privateEndpointDnsGroupDirect 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2025-05-01' = if (enableDnsIntegration && length(dnsZoneResourceIds) > 0) { + parent: privateEndpoint + name: 'privateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [for (zoneId, i) in dnsZoneResourceIds: { + name: 'dnsConfig${i}' + properties: { + privateDnsZoneId: zoneId + } + }] + } +} + +// DNS zone group using legacy lookup (when dnsZoneResourceIds not provided) +resource privateEndpointDnsGroupLegacy 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2025-05-01' = if (enableDnsIntegration && length(dnsZoneResourceIds) == 0 && useLegacyDnsLookup) { + parent: privateEndpoint + name: 'privateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [for (zoneName, i) in dnsZoneNames: { + name: 'dnsConfig${i}' + properties: { + privateDnsZoneId: privateEndpointDnsZones[i].id + } + }] + } +} + +output privateEndpointName string = privateEndpoint.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint.bicep new file mode 100644 index 000000000..68a53fda4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/private-endpoint.bicep @@ -0,0 +1,69 @@ +param name string +param privateLinkServiceId string +param groupIds array +param dnsZoneName string +param location string + +param privateEndpointSubnetId string + +// Legacy parameters for backward compatibility (used when dnsZoneResourceId is not provided) +param dnsZoneRG string = '' +param dnsSubId string = '' + +// New parameter: Direct DNS zone resource ID (preferred over dnsZoneRG/dnsSubId) +// When provided, this takes precedence over dnsZoneRG/dnsSubId lookup +param dnsZoneResourceId string = '' + +param tags object = {} + +// Add a parameter to control DNS integration +param enableDnsIntegration bool = !empty(dnsZoneResourceId) || !empty(dnsZoneRG) + +// Determine the effective DNS zone ID to use +// Priority: 1) dnsZoneResourceId if provided, 2) Legacy lookup using dnsZoneRG/dnsSubId +var useLegacyDnsLookup = empty(dnsZoneResourceId) && !empty(dnsZoneRG) && !empty(dnsZoneName) + +resource privateEndpointDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (useLegacyDnsLookup) { + name: dnsZoneName + scope: resourceGroup(dnsSubId, dnsZoneRG) +} + +// Effective DNS zone ID: use direct resource ID if provided, otherwise use legacy lookup +var effectiveDnsZoneId = !empty(dnsZoneResourceId) ? dnsZoneResourceId : (useLegacyDnsLookup ? privateEndpointDnsZone.id : '') + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2025-05-01' = { + name: name + location: location + tags: tags + properties: { + subnet: { + id: privateEndpointSubnetId + } + privateLinkServiceConnections: [ + { + name: name + properties: { + privateLinkServiceId: privateLinkServiceId + groupIds: groupIds + } + } + ] + } +} + +resource privateEndpointDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2025-05-01' = if (enableDnsIntegration && !empty(effectiveDnsZoneId)) { + parent: privateEndpoint + name: 'privateDnsZoneGroup' + properties: { + privateDnsZoneConfigs: [ + { + name: 'default' + properties: { + privateDnsZoneId: effectiveDnsZoneId + } + } + ] + } +} + +output privateEndpointName string = privateEndpoint.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/subnet.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/subnet.bicep new file mode 100644 index 000000000..63f991e76 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/subnet.bicep @@ -0,0 +1,15 @@ +param name string +param properties object +param vnetName string +param vnetRG string + +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vnetName + scope: resourceGroup(vnetRG) +} + + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' = { + name: '${vnet.name}/${name}' + properties: properties +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet-existing.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet-existing.bicep new file mode 100644 index 000000000..9688453ce --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet-existing.bicep @@ -0,0 +1,41 @@ +param name string +param vnetRG string +param apimSubnetName string +param privateEndpointSubnetName string +param functionAppSubnetName string + +@description('Name of the existing AI Foundry agent (network injection) subnet. Required when Foundry network injection is enabled. Leave blank when not using network injection.') +param agentSubnetName string = '' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' existing = { + name: name + scope: resourceGroup(vnetRG) +} + +resource apimSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' existing = { + name: apimSubnetName + parent: virtualNetwork +} + +resource privateEndpointSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' existing = { + name: privateEndpointSubnetName + parent: virtualNetwork +} + +resource functionAppSubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' existing = { + name: functionAppSubnetName + parent: virtualNetwork +} + +output virtualNetworkId string = virtualNetwork.id +output vnetName string = virtualNetwork.name +output apimSubnetName string = apimSubnet.name +output apimSubnetId string = '${virtualNetwork.id}/subnets/${apimSubnetName}' +output privateEndpointSubnetName string = privateEndpointSubnet.name +output privateEndpointSubnetId string = '${virtualNetwork.id}/subnets/${privateEndpointSubnetName}' +output functionAppSubnetName string = functionAppSubnet.name +output functionAppSubnetId string = '${virtualNetwork.id}/subnets/${functionAppSubnetName}' +output agentSubnetName string = agentSubnetName +output agentSubnetId string = !empty(agentSubnetName) ? '${virtualNetwork.id}/subnets/${agentSubnetName}' : '' +output location string = virtualNetwork.location +output vnetRG string = vnetRG diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet.bicep new file mode 100644 index 000000000..b15953752 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/networking/vnet.bicep @@ -0,0 +1,352 @@ +param name string +param location string = resourceGroup().location +param apimSubnetName string +param apimNsgName string +param privateEndpointSubnetName string +param privateEndpointNsgName string +param functionAppSubnetName string +param functionAppNsgName string +param apimRouteTableName string +param privateDnsZoneNames array +param vnetAddressPrefix string +param apimSubnetAddressPrefix string +param privateEndpointSubnetAddressPrefix string +param functionAppSubnetAddressPrefix string +param isAPIMV2SKU bool +param tags object = {} + +@description('Enable provisioning of the AI Foundry agent network injection subnet (delegated to Microsoft.App/environments).') +param enableAgentSubnet bool = true + +@description('Name of the AI Foundry agent (network injection) subnet.') +param agentSubnetName string = 'snet-agents' + +@description('NSG name for the AI Foundry agent (network injection) subnet.') +param agentSubnetNsgName string = 'nsg-agents' + +@description('Address prefix for the AI Foundry agent (network injection) subnet. Must not overlap other subnets.') +param agentSubnetAddressPrefix string = '10.170.0.192/26' + +// Set to true to enable service endpoints for APIM subnet +param enableServiceEndpointsForAPIM bool = true + +resource apimNsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { + name: apimNsgName + location: location + tags: union(tags, { 'azd-service-name': apimNsgName }) + properties: { + securityRules: [ + { + name: 'AllowPublicAccess' // Only External + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'Internet' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 3000 + direction: 'Inbound' + } + } + { + name: 'AllowAPIMManagement' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '3443' + sourceAddressPrefix: 'ApiManagement' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 3010 + direction: 'Inbound' + } + } + { + name: 'AllowAPIMLoadBalancer' + properties: { + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '6390' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 3020 + direction: 'Inbound' + } + } + { + name: 'AllowAzureTrafficManager' //Only External + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'AzureTrafficManager' + destinationAddressPrefix: 'VirtualNetwork' + access: 'Allow' + priority: 3030 + direction: 'Inbound' + } + } + { + name: 'AllowStorage' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'Storage' + access: 'Allow' + priority: 3000 + direction: 'Outbound' + } + } + { + name: 'AllowSql' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '1433' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'Sql' + access: 'Allow' + priority: 3010 + direction: 'Outbound' + } + } + { + name: 'AllowKeyVault' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'AzureKeyVault' + access: 'Allow' + priority: 3020 + direction: 'Outbound' + } + } + { + name: 'AllowMonitor' + properties: { + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRanges: ['1886', '443'] + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: 'AzureMonitor' + access: 'Allow' + priority: 3030 + direction: 'Outbound' + } + } + ] + } +} + +resource privateEndpointNsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { + name: privateEndpointNsgName + location: location + tags: union(tags, { 'azd-service-name': privateEndpointNsgName }) + properties: { + securityRules: [] + } +} + +resource functionAppNsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = { + name: functionAppNsgName + location: location + tags: union(tags, { 'azd-service-name': functionAppNsgName }) + properties: { + securityRules: [] + } +} + +resource agentNsg 'Microsoft.Network/networkSecurityGroups@2020-07-01' = if (enableAgentSubnet) { + name: agentSubnetNsgName + location: location + tags: union(tags, { 'azd-service-name': agentSubnetNsgName }) + properties: { + securityRules: [] + } +} + +resource apimRouteTable 'Microsoft.Network/routeTables@2023-11-01' = { + name: apimRouteTableName + location: location + tags: union(tags, { 'azd-service-name': apimRouteTableName }) + properties: { + routes: [ + { + name: 'apim-management' + properties: { + addressPrefix: 'ApiManagement' + nextHopType: 'Internet' + } + } + // Add additional routes as required + ] + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + properties: { + addressSpace: { + addressPrefixes: [ + vnetAddressPrefix + ] + } + subnets: concat([ + { + name: apimSubnetName + properties: { + addressPrefix: apimSubnetAddressPrefix + networkSecurityGroup: apimNsg.id == '' ? null : { + id: apimNsg.id + } + routeTable: { + id: apimRouteTable.id + } + serviceEndpoints: enableServiceEndpointsForAPIM ? [ + { + service: 'Microsoft.AzureActiveDirectory' + } + { + service: 'Microsoft.EventHub' + } + { + service: 'Microsoft.KeyVault' + } + { + service: 'Microsoft.ServiceBus' + } + { + service: 'Microsoft.Sql' + } + { + service: 'Microsoft.Storage' + } + { + service: 'Microsoft.CognitiveServices' + } + ] : [] + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + delegations: isAPIMV2SKU ? [ + { + name: 'Microsoft.Web/serverFarms' + properties: { + serviceName: 'Microsoft.Web/serverFarms' + } + } + ] : [] + } + } + { + name: privateEndpointSubnetName + properties: { + addressPrefix: privateEndpointSubnetAddressPrefix + networkSecurityGroup: privateEndpointNsg.id == '' ? null : { + id: privateEndpointNsg.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + } + } + { + name: functionAppSubnetName + properties: { + addressPrefix: functionAppSubnetAddressPrefix + networkSecurityGroup: functionAppNsg.id == '' ? null : { + id: functionAppNsg.id + } + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + serviceEndpoints: [ + { + service: 'Microsoft.CognitiveServices' + } + ] + delegations: [ + { + name: 'Microsoft.Web/serverFarms' + properties: { + serviceName: 'Microsoft.Web/serverFarms' + } + } + ] + } + } + ], enableAgentSubnet ? [ + { + name: agentSubnetName + properties: { + addressPrefix: agentSubnetAddressPrefix + networkSecurityGroup: { + id: agentNsg.id + } + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Enabled' + delegations: [ + { + name: 'Microsoft.app/environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + } + ] : []) + } + + + resource apimSubnet 'subnets' existing = { + name: apimSubnetName + } + + resource privateEndpointSubnet 'subnets' existing = { + name: privateEndpointSubnetName + } + + resource functionAppSubnet 'subnets' existing = { + name: functionAppSubnetName + } + + resource agentSubnet 'subnets' existing = if (enableAgentSubnet) { + name: agentSubnetName + } +} + +resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = [for privateDnsZoneName in privateDnsZoneNames: { + name: '${privateDnsZoneName}/privateDnsZoneLink' + location: 'global' + tags: tags + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } +}] + +output virtualNetworkId string = virtualNetwork.id +output vnetName string = virtualNetwork.name +output apimSubnetName string = virtualNetwork::apimSubnet.name +output apimSubnetId string = virtualNetwork::apimSubnet.id +output privateEndpointSubnetName string = virtualNetwork::privateEndpointSubnet.name +output privateEndpointSubnetId string = virtualNetwork::privateEndpointSubnet.id +output functionAppSubnetName string = virtualNetwork::functionAppSubnet.name +output functionAppSubnetId string = virtualNetwork::functionAppSubnet.id +output agentSubnetName string = enableAgentSubnet ? virtualNetwork::agentSubnet.name : '' +output agentSubnetId string = enableAgentSubnet ? virtualNetwork::agentSubnet.id : '' +output location string = virtualNetwork.location +output vnetRG string = resourceGroup().name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/redis/redis.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/redis/redis.bicep new file mode 100644 index 000000000..d13ff4dc8 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/redis/redis.bicep @@ -0,0 +1,185 @@ +@description('Name of the Azure Managed Redis resource (Microsoft.Cache/redisEnterprise).') +param name string + +@description('Location for the Redis resource') +param location string = resourceGroup().location + +@description('Tags to be applied to Redis and related resources') +param tags object = {} + +@description('Redis Enterprise / Azure Managed Redis SKU name. Allowed values align to Microsoft.Cache/redisEnterprise@2025-07-01.') +@allowed([ + 'Enterprise_E1' + 'Enterprise_E5' + 'Enterprise_E10' + 'Enterprise_E20' + 'Enterprise_E50' + 'Enterprise_E100' + 'Enterprise_E200' + 'Enterprise_E400' + 'EnterpriseFlash_F300' + 'EnterpriseFlash_F700' + 'EnterpriseFlash_F1500' + 'Balanced_B0' + 'Balanced_B1' + 'Balanced_B3' + 'Balanced_B5' + 'Balanced_B10' + 'Balanced_B20' + 'Balanced_B50' + 'Balanced_B100' + 'Balanced_B150' + 'Balanced_B250' + 'Balanced_B350' + 'Balanced_B500' + 'Balanced_B700' + 'Balanced_B1000' + 'MemoryOptimized_M10' + 'MemoryOptimized_M20' + 'MemoryOptimized_M50' + 'MemoryOptimized_M100' + 'MemoryOptimized_M150' + 'MemoryOptimized_M250' + 'MemoryOptimized_M350' + 'MemoryOptimized_M500' + 'MemoryOptimized_M700' + 'MemoryOptimized_M1000' + 'MemoryOptimized_M1500' + 'MemoryOptimized_M2000' + 'ComputeOptimized_X3' + 'ComputeOptimized_X5' + 'ComputeOptimized_X10' + 'ComputeOptimized_X20' + 'ComputeOptimized_X50' + 'ComputeOptimized_X100' + 'ComputeOptimized_X150' + 'ComputeOptimized_X250' + 'ComputeOptimized_X350' + 'ComputeOptimized_X500' + 'ComputeOptimized_X700' + 'FlashOptimized_A250' + 'FlashOptimized_A500' + 'FlashOptimized_A700' + 'FlashOptimized_A1000' + 'FlashOptimized_A1500' + 'FlashOptimized_A2000' + 'FlashOptimized_A4500' +]) +param skuName string = 'Balanced_B10' + +@description('Redis Enterprise cluster capacity. Only used for Enterprise_* and EnterpriseFlash_* SKUs. Valid values are (2, 4, 6, ...) for Enterprise SKUs and (3, 9, 15, ...) for EnterpriseFlash SKUs.') +param skuCapacity int = 2 + +@description('Whether public endpoint access is allowed for this Redis. If Disabled, private endpoints are the exclusive access method.') +@allowed(['Enabled', 'Disabled']) +param publicNetworkAccess string = 'Disabled' + +@description('Minimum TLS version for Redis connections') +param minimumTlsVersion string = '1.2' + +@description('Whether to create a private endpoint for Redis') +param usePrivateEndpoint bool = true + +// Networking parameters +@description('Name of the Virtual Network') +param vNetName string + +@description('Name of the private endpoint subnet') +param privateEndpointSubnetName string + +@description('Resource group containing the Virtual Network') +param vNetRG string + +@description('Name of the Redis private endpoint') +param redisPrivateEndpointName string + +@description('DNS zone name for Redis private endpoint') +param redisDnsZoneName string = 'privatelink.redis.azure.net' + +// DNS Zone parameters (legacy approach - used when dnsZoneResourceId is not provided) +@description('Resource group containing the DNS zones') +param dnsZoneRG string = '' + +@description('Subscription ID containing the DNS zones') +param dnsSubscriptionId string = '' + +@description('Direct DNS zone resource ID for Redis (preferred over dnsZoneRG/dnsSubscriptionId)') +param dnsZoneResourceId string = '' + +// Existing VNet and subnet for private endpoint +resource vnet 'Microsoft.Network/virtualNetworks@2022-01-01' existing = { + name: vNetName + scope: resourceGroup(vNetRG) +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2022-01-01' existing = { + name: privateEndpointSubnetName + parent: vnet +} + +var usesSkuCapacity = startsWith(skuName, 'Enterprise_') || startsWith(skuName, 'EnterpriseFlash_') +var redisSku = usesSkuCapacity ? { + name: skuName + capacity: skuCapacity +} : { + name: skuName +} + +resource redis 'Microsoft.Cache/redisEnterprise@2025-07-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: redisSku + properties: { + minimumTlsVersion: minimumTlsVersion + publicNetworkAccess: publicNetworkAccess + } +} + +resource redisDb 'Microsoft.Cache/redisEnterprise/databases@2025-07-01' = { + name: 'default' + parent: redis + properties: { + accessKeysAuthentication: 'Enabled' + evictionPolicy: 'NoEviction' + clusteringPolicy: 'EnterpriseCluster' + clientProtocol: 'Encrypted' + modules: [ + {name: 'RediSearch' } + ] + // Per Azure Managed Redis Private Link guidance, clients connect on port 10000. + port: 10000 + } +} + +module privateEndpoint '../networking/private-endpoint.bicep' = if (usePrivateEndpoint) { + name: '${name}-pe' + params: { + groupIds: [ + 'redisEnterprise' + ] + dnsZoneName: redisDnsZoneName + name: redisPrivateEndpointName + privateLinkServiceId: redis.id + location: location + dnsZoneRG: dnsZoneRG + privateEndpointSubnetId: subnet.id + dnsSubId: dnsSubscriptionId + dnsZoneResourceId: dnsZoneResourceId + tags: tags + } + dependsOn: [ + redisDb + ] +} + +var redisHostName = redis.properties.hostName +var redisPort = redisDb.properties.port +var redisPrimaryKey = redisDb.listKeys().primaryKey + +@secure() +output redisCacheConnectionString string = '${redisHostName}:${redisPort},password=${redisPrimaryKey},ssl=true' + +output redisResourceId string = redis.id +output hostName string = redisHostName +output port int = redisPort diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-apim.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-apim.bicep new file mode 100644 index 000000000..1aa6ae400 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-apim.bicep @@ -0,0 +1,54 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +var cognitiveServicesOpenAIUserRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') +var cognitiveServicesUserRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908') +var eventHubsDataSenderRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '2b629674-e913-4c01-ae53-ef4638d8f975') + +// Getting definitions for 'Search Index Data Reader' and 'Search Index Data Contributor' +// var searchIndexDataReaderRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '1407120a-92aa-4202-b7e9-c0e197c71c8f') +// var searchIndexDataContributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7') + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) +} + +// Assign the Cognitive Services User role to the user-defined managed identity used by workloads +resource cognitiveServicesUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, cognitiveServicesUserRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: cognitiveServicesUserRoleDefinitionId + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +// Assign the Cognitive Services User role to the user-defined managed identity used by workloads +resource cognitiveServicesOpenAIUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, cognitiveServicesOpenAIUserRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: cognitiveServicesOpenAIUserRoleDefinitionId + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + +// Assign to Azure Event Hubs Data Sender role to the user-defined managed identity used by workloads +resource eventHubsDataSenderRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, eventHubsDataSenderRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: eventHubsDataSenderRoleDefinitionId + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + + +output managedIdentityName string = managedIdentity.name +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-usage.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-usage.bicep new file mode 100644 index 000000000..ed43bd28f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/bicep/infra/modules/security/managed-identity-usage.bicep @@ -0,0 +1,41 @@ +param name string +param location string = resourceGroup().location +param tags object = {} +param cosmosDbAccountName string + +var docDbAccNativeContributorRoleDefinitionId = '00000000-0000-0000-0000-000000000002' +var eventHubsDataOwnerRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'f526a384-b230-433a-b45c-95f59c4a2dec') + +resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2024-02-15-preview' existing = { + name: cosmosDbAccountName +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) +} + +resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = { + name: guid(docDbAccNativeContributorRoleDefinitionId, managedIdentity.id, cosmosDbAccount.id) + parent: cosmosDbAccount + properties:{ + principalId: managedIdentity.properties.principalId + roleDefinitionId: '/${cosmosDbAccount.id}/sqlRoleDefinitions/${docDbAccNativeContributorRoleDefinitionId}' + scope: cosmosDbAccount.id + } +} + +// Assign to Azure Event Hubs Data Owner role to the user-defined managed identity used by workloads +resource eventHubsDataOwnerRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(managedIdentity.id, eventHubsDataOwnerRoleDefinitionId) + scope: resourceGroup() + properties: { + roleDefinitionId: eventHubsDataOwnerRoleDefinitionId + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + + +output managedIdentityName string = managedIdentity.name diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md index bc821c48c..3f4318146 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-01.md @@ -30,7 +30,7 @@ Part A below); every workshop notebook reads its configuration via `azd env get- ### Part B — Run the LLM backend onboarding notebook (30 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/1. llm-backend-onboarding-runner.ipynb` + `workshop/1. llm-backend-onboarding-runner.ipynb` in VS Code / Jupyter. 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md index a03546f88..fc3607e45 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-02.md @@ -28,7 +28,7 @@ been run for your attendee credentials. ### Part B — Run the Universal LLM API validation notebook (25 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb`. + `workshop/2. citadel-universal-llm-api-all-models-tests.ipynb` 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI and Connected Subscription` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md index a106913d0..4960d7709 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-03.md @@ -33,7 +33,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the access contracts notebook (35 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/3. citadel-access-contracts-tests.ipynb`. + `workshop/3. citadel-access-contracts-tests.ipynb` 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI and Connected Subscription` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md index 2da611cf8..98a43a1c8 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-04.md @@ -31,7 +31,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the agent frameworks notebook (30 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/4. citadel-agent-frameworks-tests.ipynb`. + `workshop/4. citadel-agent-frameworks-tests.ipynb` 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI and Connected Subscription` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md index 18075a417..90bf0a66d 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-05.md @@ -27,7 +27,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the PII processing notebook (35 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/5. citadel-pii-processing-tests.ipynb`. + `workshop/5. citadel-pii-processing-tests.ipynb` 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI and Connected Subscription` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md index 43d012934..facf66a57 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-06.md @@ -27,7 +27,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the Unified AI API validation notebook (35 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/6. citadel-unified-ai-api-tests.ipynb`. + `workshop/6. citadel-unified-ai-api-tests.ipynb` 2. Run the setup sections: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI and Connected Subscription` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md index d44b6df2c..e8f63a4df 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-07.md @@ -38,7 +38,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the hosted agent notebook (40 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/7. citadel-hosted-agent-with-agt.ipynb`. + `workshop/7. citadel-hosted-agent-with-agt.ipynb` 2. Run the notebook's sections in order: - `0️⃣ Initialize Notebook Variables` - `1️⃣ Verify Azure CLI Login` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md index f311f5d6f..38e1a0e87 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-08.md @@ -36,7 +36,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run the A2A publish & consume notebook (35 min) 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/8. publish-and-use-a2a-endpoint.ipynb`. + `workshop/8. publish-and-use-a2a-endpoint.ipynb` 2. Run the notebook's sections in order: - `1. Configuration` - `2. Helpers` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md index 70ff9f0a6..d21248132 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/challenge-09.md @@ -45,7 +45,7 @@ Same prerequisite as every notebook in this MicroHack — see ### Part B — Run as far as your environment allows 1. Open - `../reference/ai-hub-gateway-solution-accelerator/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb`. + `workshop/9. publish-and-use-hr-mcp-via-apim.ipynb` 2. **If the HR MCP backend has not been provisioned for you**, run only the read-only setup sections and stop before acquiring a token: - `0️⃣ Initialize notebook variables from azd env and environment variables` diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/apimtools.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/apimtools.py new file mode 100644 index 000000000..8fc9cd12a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/apimtools.py @@ -0,0 +1,330 @@ +import os, sys, json, requests +sys.path.insert(1, '../shared') # add the shared directory to the Python path +import utils +from azure.identity import DefaultAzureCredential +from azure.mgmt.apimanagement import ApiManagementClient +from azure.mgmt.apimanagement.models import SubscriptionKeysContract + +class APIMClientTool: + def __init__(self, resource_group_name, apim_resource_name = ""): + self.resource_group_name = resource_group_name + self.apim_resource_name = apim_resource_name + self.azure_endpoint: str = None + + def initialize(self): + output = utils.run("az account show", "Retrieved az account", "Failed to get the current az account") + if output.success and output.json_data: + self.current_user = output.json_data['user']['name'] + self.tenant_id = output.json_data['tenantId'] + self.subscription_id = output.json_data['id'] + utils.print_info(f"Current user: {self.current_user}") + utils.print_info(f"Tenant ID: {self.tenant_id}") + utils.print_info(f"Subscription ID: {self.subscription_id}") + + if not self.apim_resource_name: + output = utils.run(f"az resource list -g {self.resource_group_name} --resource-type Microsoft.ApiManagement/service", "Listing APIM Resources", "Failed to list APIM resources") + if output.success and output.json_data and len(output.json_data) > 0: + self.apim_resource_name = output.json_data[0]['name'] + else: + raise Exception(f"APIM resource not found in resource group {self.resource_group_name}.") + + self.client = ApiManagementClient(credential=DefaultAzureCredential(), subscription_id=self.subscription_id) + + api_management_service = self.client.api_management_service.get(self.resource_group_name, self.apim_resource_name) + + self.apim_service_id = api_management_service.id + utils.print_info(f"APIM Service Id: {self.apim_service_id}") + + self.apim_resource_gateway_url: str = api_management_service.gateway_url + utils.print_info(f"APIM Gateway URL: {self.apim_resource_gateway_url}") + self.apim_subscriptions = [] + subscriptions = self.client.subscription.list(self.resource_group_name, self.apim_resource_name) + for subscription in subscriptions: + subscription_secrets = self.client.subscription.list_secrets(self.resource_group_name, self.apim_resource_name, str(subscription.name)) + self.apim_subscriptions.append({ "name": subscription.name, "key": subscription_secrets.primary_key}) + utils.print_info(f"Retrieved key {len(self.apim_subscriptions) - 1} for subscription: {subscription.name}") + + def discover_api(self, api_path_filter = "/openai"): + api_management_service = self.client.api_management_service.get(self.resource_group_name, self.apim_resource_name) + apis = self.client.api.list_by_service(self.resource_group_name, self.apim_resource_name) + api_path = None + for api in apis: + if api_path_filter in api.path: + api_path = api.path + self.api_id = api.id + utils.print_info(f"Found API with id {self.api_id} and path {api_path}") + self.azure_endpoint = f"{self.apim_resource_gateway_url}/{api_path.replace(api_path_filter, "")}" + utils.print_info(f"Azure Endpoint with APIM {self.azure_endpoint}") + break + if not api_path: + raise Exception(f"API with path filter `{api_path_filter}` not found.") + + def get_debug_credentials(self, expire_after) -> str | None: + request = { + "credentialsExpireAfter": expire_after, + "apiId": f"{self.apim_service_id}/apis/{self.api_id}", + "purposes": ["tracing"] + } + output = utils.run(f"az rest --method post --uri {self.apim_service_id}/gateways/managed/listDebugCredentials?api-version=2023-05-01-preview --body \"{str(request)}\"", + "Retrieved APIM debug credentials", "Failed to get the APIM debug credentials") + return output.json_data['token'] if output.success and output.json_data else None + + def get_trace(self, trace_id) -> str | None: + request = { + "traceId": trace_id + } + output = utils.run(f"az rest --method post --uri {self.apim_service_id}/gateways/managed/listTrace?api-version=2023-05-01-preview --body \"{str(request)}\"", + "Retrieved trace details", "Failed to get the trace details") + return output.json_data if output.success and output.json_data else None + + def get_policy_fragment_supported_models(self, fragment_name: str = "set-backend-pools", debug: bool = False) -> list[str]: + """ + Retrieves a policy fragment from APIM and extracts the list of supported model names. + + Args: + fragment_name: Name of the policy fragment (default: "set-backend-pools") + debug: If True, prints the raw policy XML for debugging (default: False) + + Returns: + List of unique supported model names extracted from the policy fragment + + Raises: + RuntimeError: If the policy fragment cannot be retrieved or parsed + """ + try: + # Get the policy fragment using Azure SDK + policy_fragment = self.client.policy_fragment.get( + resource_group_name=self.resource_group_name, + service_name=self.apim_resource_name, + id=fragment_name + ) + + # Extract the XML policy content + policy_xml = policy_fragment.value + utils.print_info(f"Retrieved policy fragment: {fragment_name}") + + if debug: + utils.print_info("=" * 80) + utils.print_info("RAW POLICY XML:") + utils.print_info("=" * 80) + print(policy_xml) + utils.print_info("=" * 80) + + # Parse the XML to extract supported models + supported_models = self._extract_supported_models_from_policy(policy_xml) + + utils.print_info(f"Found {len(supported_models)} unique supported models") + return supported_models + + except Exception as e: + raise RuntimeError(f"Failed to retrieve or parse policy fragment '{fragment_name}': {str(e)}") from e + + def _extract_supported_models_from_policy(self, policy_xml: str) -> list[str]: + """ + Extracts supported model names from the policy XML content. + + Args: + policy_xml: The XML content of the policy fragment + + Returns: + List of unique model names found in supportedModels arrays + """ + import re + import html + + supported_models = set() + + # Decode HTML entities if present (e.g., " -> ") + decoded_xml = html.unescape(policy_xml) + + # Multiple patterns to handle different formatting styles + patterns = [ + # Pattern 1: Standard format with quotes + r'"supportedModels"\s*,\s*new\s+JArray\s*\((.*?)\)', + # Pattern 2: With escaped quotes + r'"supportedModels"\s*,\s*new\s+JArray\s*\((.*?)\)', + # Pattern 3: More flexible whitespace and line breaks + r'supportedModels["\s]*,\s*new\s+JArray\s*\((.*?)\)', + ] + + for pattern in patterns: + matches = re.findall(pattern, decoded_xml, re.DOTALL | re.IGNORECASE) + + for match in matches: + # Extract individual model names from the JArray + # Handle both regular quotes and escaped quotes + model_patterns = [ + r'"([^"]+)"', # Regular quotes + r'"([^&]+)"', # HTML escaped quotes + ] + + for model_pattern in model_patterns: + models = re.findall(model_pattern, match) + # Filter out non-model strings (like "supportedModels" itself) + models = [m for m in models if m and m != 'supportedModels'] + supported_models.update(models) + + # If still no models found, log a warning + if not supported_models: + utils.print_warning("No supported models found in policy fragment. Check if the policy format matches expected pattern.") + + # Convert set to sorted list for consistent output + return sorted(list(supported_models)) + + def get_backends(self) -> tuple[list[dict], list[dict]]: + """ + Retrieves all backends from APIM and categorizes them into individual backends and backend pools. + Uses the Azure REST API directly with a recent API version that supports backend pools, + since the azure-mgmt-apimanagement SDK v4.0.0 (API 2022-08-01) predates the pool feature. + + Returns: + Tuple of (individual_backends, backend_pools) where each is a list of dictionaries + + Raises: + RuntimeError: If backends cannot be retrieved + """ + try: + utils.print_info("Retrieving APIM backends using Azure REST API...") + + credential = DefaultAzureCredential() + token = credential.get_token("https://management.azure.com/.default") + + api_version = "2024-05-01" + url = ( + f"https://management.azure.com/subscriptions/{self.subscription_id}" + f"/resourceGroups/{self.resource_group_name}" + f"/providers/Microsoft.ApiManagement/service/{self.apim_resource_name}" + f"/backends?api-version={api_version}" + ) + + response = requests.get(url, headers={"Authorization": f"Bearer {token.token}"}) + response.raise_for_status() + + backends_data = response.json().get("value", []) + + individual_backends = [] + backend_pools = [] + + for backend in backends_data: + props = backend.get("properties", {}) + backend_name = backend.get("name", "") + backend_url = props.get("url", "") + description = props.get("description", "") + backend_type = props.get("type", "") + + if backend_type == "Pool": + pool = props.get("pool", {}) + pool_services = [ + { + 'id': svc.get('id', ''), + 'priority': svc.get('priority'), + 'weight': svc.get('weight') + } + for svc in pool.get("services", []) + ] + backend_pools.append({ + 'name': backend_name, + 'description': description, + 'services': pool_services + }) + utils.print_info(f"📦 Backend Pool: {backend_name} ({len(pool_services)} backends)") + else: + # Extract supported models from description if present + supported_models = [] + if 'Supports models:' in description: + models_str = description.split('Supports models:')[-1].strip() + supported_models = [m.strip() for m in models_str.split(',')] + + individual_backends.append({ + 'name': backend_name, + 'url': backend_url, + 'description': description, + 'supportedModels': supported_models + }) + utils.print_info(f"🔗 Backend: {backend_name} -> {backend_url}") + + utils.print_ok(f"Found {len(individual_backends)} individual backends and {len(backend_pools)} backend pools") + return individual_backends, backend_pools + + except Exception as e: + raise RuntimeError(f"Failed to retrieve backends: {str(e)}") from e + + def get_named_value(self, named_value_id: str) -> str | None: + """ + Retrieves a named value from APIM. + + Args: + named_value_id: The ID of the named value to retrieve + + Returns: + The value of the named value, or None if not found + """ + try: + named_value = self.client.named_value.get( + resource_group_name=self.resource_group_name, + service_name=self.apim_resource_name, + named_value_id=named_value_id + ) + return named_value.value + except Exception as e: + utils.print_warning(f"Named value '{named_value_id}' not found: {str(e)}") + return None + + def get_managed_identity_info(self) -> dict: + """ + Retrieves managed identity information from the APIM service. + + Returns: + Dictionary with managed identity details including clientId, name, and resourceGroup + """ + try: + apim_service = self.client.api_management_service.get( + resource_group_name=self.resource_group_name, + service_name=self.apim_resource_name + ) + + result = { + 'clientId': None, + 'name': None, + 'resourceGroup': self.resource_group_name + } + + # Try to get from named values first + client_id = self.get_named_value('uami-client-id') + if client_id: + result['clientId'] = client_id + utils.print_ok(f"Found managed identity client ID in named values: {client_id[:8]}...") + + # Get identity details from APIM service + if hasattr(apim_service, 'identity') and apim_service.identity: + identity = apim_service.identity + + # Check for user-assigned identities + if hasattr(identity, 'user_assigned_identities') and identity.user_assigned_identities: + user_identities = identity.user_assigned_identities + + if user_identities: + # Get the first user-assigned identity + first_identity_id = list(user_identities.keys())[0] + first_identity = user_identities[first_identity_id] + + if not result['clientId'] and hasattr(first_identity, 'client_id'): + result['clientId'] = first_identity.client_id + + # Extract identity name and resource group from resource ID + result['name'] = first_identity_id.split('/')[-1] + if '/resourceGroups/' in first_identity_id: + result['resourceGroup'] = first_identity_id.split('/resourceGroups/')[-1].split('/')[0] + + utils.print_ok(f"Found user-assigned managed identity: {result['name']}") + + return result + + except Exception as e: + utils.print_warning(f"Could not retrieve managed identity info: {str(e)}") + return { + 'clientId': None, + 'name': None, + 'resourceGroup': self.resource_group_name + } + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/requirements.txt new file mode 100644 index 000000000..5197252f0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/requirements.txt @@ -0,0 +1,38 @@ +# This is a comprehensive requirements.txt file for all labs to ensure that one installation covers everything. + +# Do not include azure-functions-worker in this file +# The Python Worker is managed by the Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +requests +jsonref +python-dotenv +pandas +matplotlib +redis +msal +openai +openai[realtime] +azure-cli +azure-identity +opentelemetry-api +opentelemetry-sdk +opentelemetry-instrumentation-openai-v2 +azure-monitor-opentelemetry +azure-monitor-ingestion +azure-ai-projects +azure-ai-agents +azure-ai-inference[prompts] +azure-search-documents +azure-functions +azure-mgmt-apimanagement + +# MCP Labs +mcp +autogen-core +autogen-ext[openai,azure,mcp] +autogen-agentchat +semantic-kernel[mcp] + +# Agent Framework +agent-framework \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/README.md new file mode 100644 index 000000000..3a850df1b --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/README.md @@ -0,0 +1,36 @@ +# Snippets + +We use snippets to reduce redundancy and improve maintainability. Snippets are references from the many Jupyter notebook files throughout our labs. They work by using [Python's _%load_ magic command](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-load). + +## Loaded Snippets + +Jupyter notebooks will have Python code blocks similar to this one: + +```python +# %load ../../shared/snippets/openai-api-requests.py + +import time +from openai import AzureOpenAI + +... +``` + +What you see here is an executed load as the command is now commented out after the code has been loaded immediately below it. + +While this is generally very maintable, the loading is static, unlike a function reference, for example. That means that code may be stale. When you are editing Jupyter notebook files, it is advisable that you execute the load commands fresh and check for git changes. + +## Updating a Loaded Snippet + +1. In a Jupyter Notebook's Python code cell, remove everything below the `%load` command in line 1. +1. Uncomment the `%load` command. The cell should then look similar to this: + + ```python + %load ../../shared/snippets/openai-api-requests.py + ``` + +1. Press the _Play_ button in the code cell to execute the `%load` command. +1. Repeate these steps for all code blocks with `%load` commands in the current file. +1. Save the file. +1. Check git for any changes to the file. +1. Test the changes. +1. Check in the changes. diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/api-http-requests.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/api-http-requests.py new file mode 100644 index 000000000..096fdce00 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/api-http-requests.py @@ -0,0 +1,63 @@ +import json +import requests +import time + +runs = 10 +sleep_time_ms = 100 +url = f"{apim_resource_gateway_url}/openai/deployments/{openai_deployment_name}/chat/completions?api-version={openai_api_version}" +api_runs = [] +messages = {"messages": [ + {"role": "system", "content": "You are a sarcastic, unhelpful assistant."}, + {"role": "user", "content": "Can you tell me the time, please?"} +]} + +# Initialize a session for connection pooling +session = requests.Session() +# Set default headers +session.headers.update({ + 'api-key': apim_subscription_key, + 'x-user-id': 'alex' +}) + +try: + # Warm up the APIM instance prior to the first meaningful request. + apim_status_url = f"{apim_resource_gateway_url}/status-0123456789abcdef" + print(f"🔥 Warming up the APIM instance with a GET request to {apim_status_url}") + response = session.get(apim_status_url) + print(f"Response status: {response.status_code}\n") + + for i in range(runs): + print(f"▶️ Run {i+1}/{runs}:") + + start_time = time.time() + response = session.post(url, json = messages) + response_time = time.time() - start_time + print(f"⌚ {response_time:.2f} seconds") + + # Check the response status code and apply formatting + if 200 <= response.status_code < 300: + status_code_str = f"\x1b[1;32m{response.status_code} - {response.reason}\x1b[0m" # Bold and green + elif response.status_code >= 400: + status_code_str = f"\x1b[1;31m{response.status_code} - {response.reason}\x1b[0m" # Bold and red + else: + status_code_str = str(response.status_code) # No formatting + + # Print the response status with the appropriate formatting + print(f"Response status: {status_code_str}") + print(f"Response headers: {json.dumps(dict(response.headers), indent = 4)}") + + if "x-ms-region" in response.headers: + print(f"x-ms-region: \x1b[1;32m{response.headers.get("x-ms-region")}\x1b[0m") # this header is useful to determine the region of the backend that served the request + api_runs.append((response_time, response.headers.get("x-ms-region"))) + + if (response.status_code == 200): + data = json.loads(response.text) + print(f"Token usage: {json.dumps(dict(data.get("usage")), indent = 4)}\n") + print(f"💬 {data.get("choices")[0].get("message").get("content")}\n") + else: + print(response.text) + + time.sleep(sleep_time_ms/1000) +finally: + # Close the session to release the connection + session.close() diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-deployment.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-deployment.py new file mode 100644 index 000000000..0d75cd2f6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-deployment.py @@ -0,0 +1,37 @@ +# type: ignore + +import json + +backend_id = "openai-backend-pool" if len(openai_resources) > 1 else openai_resources[0].get("name") + +with open("policy.xml", 'r') as policy_xml_file: + policy_xml = policy_xml_file.read() + + if "{backend-id}" in policy_xml: + policy_xml = policy_xml.replace("{backend-id}", backend_id) + + if "{aad-client-application-id}" in policy_xml: + policy_xml = policy_xml.replace("{aad-client-application-id}", client_id) + + if "{aad-tenant-id}" in policy_xml: + policy_xml = policy_xml.replace("{aad-tenant-id}", tenant_id) + + policy_xml_file.close() +open("policy-updated.xml", 'w').write(policy_xml) + +bicep_parameters = { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "openAIConfig": { "value": openai_resources }, + "openAIDeploymentName": { "value": openai_deployment_name }, + "openAIModelName": { "value": openai_model_name }, + "openAIModelVersion": { "value": openai_model_version }, + "openAIAPIVersion": { "value": openai_api_version } + } +} + +with open('params.json', 'w') as bicep_parameters_file: + bicep_parameters_file.write(json.dumps(bicep_parameters)) + +! az deployment group create --name {deployment_name} --resource-group {resource_group_name} --template-file "main.bicep" --parameters "params.json" diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-resource-group.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-resource-group.py new file mode 100644 index 000000000..1b11171c1 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/create-az-resource-group.py @@ -0,0 +1,10 @@ +# type: ignore + +import datetime + +resource_group_stdout = ! az group create --name {resource_group_name} --location {resource_group_location} + +if resource_group_stdout.n.startswith("ERROR"): + print(resource_group_stdout) +else: + print(f"✅ Azure Resource Group {resource_group_name} created ⌚ {datetime.datetime.now().time()}") diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/deployment-outputs.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/deployment-outputs.py new file mode 100644 index 000000000..fb389e1b2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/deployment-outputs.py @@ -0,0 +1,52 @@ +# type: ignore + +# Obtain all of the outputs from the deployment +stdout = ! az deployment group show --name {deployment_name} -g {resource_group_name} --query properties.outputs -o json +outputs = json.loads(stdout.n) + +# Extract the individual properties +apim_service_id = outputs.get('apimServiceId', {}).get('value', '') +apim_subscription_key = outputs.get('apimSubscriptionKey', {}).get('value', '') +apim_subscription1_key = outputs.get('apimSubscription1Key', {}).get('value', '') +apim_subscription2_key = outputs.get('apimSubscription2Key', {}).get('value', '') +apim_subscription3_key = outputs.get('apimSubscription3Key', {}).get('value', '') +apim_resource_gateway_url = outputs.get('apimResourceGatewayURL', {}).get('value', '') +workspace_id = outputs.get('logAnalyticsWorkspaceId', {}).get('value', '') +app_id = outputs.get('applicationInsightsAppId', {}).get('value', '') +app_insights_name = outputs.get('applicationInsightsName', {}).get('value', '') +function_app_resource_name = outputs.get('functionAppResourceName', {}).get('value', '') +cosmosdb_connection_string = outputs.get('cosmosDBConnectionString', {}).get('value', '') + +# Print the extracted properties if they are not empty +if apim_service_id: + print(f"👉🏻 APIM Service Id: {apim_service_id}") + +if apim_subscription_key: + print(f"👉🏻 APIM Subscription Key (masked): ****{apim_subscription_key[-4:]}") + +if apim_subscription1_key: + print(f"👉🏻 APIM Subscription Key 1 (masked): ****{apim_subscription1_key[-4:]}") + +if apim_subscription2_key: + print(f"👉🏻 APIM Subscription Key 2 (masked): ****{apim_subscription2_key[-4:]}") + +if apim_subscription3_key: + print(f"👉🏻 APIM Subscription Key 3 (masked): ****{apim_subscription3_key[-4:]}") + +if apim_resource_gateway_url: + print(f"👉🏻 APIM API Gateway URL: {apim_resource_gateway_url}") + +if workspace_id: + print(f"👉🏻 Workspace ID: {workspace_id}") + +if app_id: + print(f"👉🏻 App ID: {app_id}") + +if app_insights_name: + print(f"👉🏻 Application Insights Name: {app_insights_name}") + +if function_app_resource_name: + print(f"👉🏻 Function Name: {function_app_resource_name}") + +if cosmosdb_connection_string: + print(f"👉🏻 Cosmos DB Connection String: {cosmosdb_connection_string}") diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/openai-api-requests.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/openai-api-requests.py new file mode 100644 index 000000000..8d9526848 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/snippets/openai-api-requests.py @@ -0,0 +1,30 @@ + +import time +from openai import AzureOpenAI + +runs = 10 +sleep_time_ms = 100 + +client = AzureOpenAI( + azure_endpoint = apim_resource_gateway_url, + api_key = apim_subscription_key, + api_version = openai_api_version +) + +messages = [ + {"role": "system", "content": "You are a sarcastic, unhelpful assistant."}, + {"role": "user", "content": "Can you tell me the time, please?"} +] + +for i in range(runs): + print(f"▶️ Run {i+1}/{runs}:") + + start_time = time.time() + response = client.chat.completions.create(model = openai_model_name, messages = messages) # type: ignore + response_time = time.time() - start_time + print(f"⌚ {response_time:.2f} seconds") + if response.usage: + print(f"Token usage:\n Total tokens: {response.usage.total_tokens}\n Prompt tokens: {response.usage.prompt_tokens}\n Completion tokens: {response.usage.completion_tokens}\n") + print(f"💬 {response.choices[0].message.content}\n") + + time.sleep(sleep_time_ms/1000) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/utils.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/utils.py new file mode 100644 index 000000000..9c7405d00 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/shared/utils.py @@ -0,0 +1,424 @@ +import datetime, json, os, subprocess, requests, time, traceback + +# Define ANSI escape code constants vor clarity in the print commands below +RESET_FORMATTING = "\x1b[0m" +BOLD_BLUE = "\x1b[1;34m" +BOLD_RED = "\x1b[1;31m" +BOLD_GREEN = "\x1b[1;32m" +BOLD_YELLOW = "\x1b[1;33m" + +print_command = lambda command='': print(f"⚙️ {BOLD_BLUE}Running: {command} {RESET_FORMATTING}") +print_error = lambda message, output='', duration='': print(f"❌ {BOLD_YELLOW}{message}{RESET_FORMATTING} ⌚ {datetime.datetime.now().time()} {duration}{' ' if output else ''}{output}") +print_info = lambda message: print(f"👉🏽 {BOLD_BLUE}{message}{RESET_FORMATTING}") +print_message = lambda message, output='', duration='': print(f"👉🏽 {BOLD_GREEN}{message}{RESET_FORMATTING} ⌚ {datetime.datetime.now().time()} {duration}{' ' if output else ''}{output}") +print_ok = lambda message, output='', duration='': print(f"✅ {BOLD_GREEN}{message}{RESET_FORMATTING} ⌚ {datetime.datetime.now().time()} {duration}{' ' if output else ''}{output}") +print_warning = lambda message, output='', duration='': print(f"⚠️ {BOLD_YELLOW}{message}{RESET_FORMATTING} ⌚ {datetime.datetime.now().time()} {duration}{' ' if output else ''}{output}") + +def mask_sensitive_values(data, sensitive_keys=None): + """ + Recursively mask sensitive values in nested dictionaries and lists. + + Args: + data: The data structure to process (dict, list, or primitive) + sensitive_keys: List of key names to mask (case-insensitive). + Defaults to ['apiKey', 'api_key', 'key', 'secret', 'password'] + + Returns: + A copy of the data with sensitive values replaced by '########' + """ + if sensitive_keys is None: + sensitive_keys = ['apikey', 'api_key', 'secret', 'password'] + + sensitive_keys_lower = [k.lower() for k in sensitive_keys] + + if isinstance(data, dict): + return { + k: '########' if k.lower() in sensitive_keys_lower and isinstance(v, str) + else mask_sensitive_values(v, sensitive_keys) + for k, v in data.items() + } + elif isinstance(data, list): + return [mask_sensitive_values(item, sensitive_keys) for item in data] + else: + return data + +class Output(object): + def __init__(self, success, text): + self.success = success + self.text = text + + try: + self.json_data = json.loads(text) + except: + # stdout may contain non-JSON text (progress indicators, warnings) before/after the JSON. + # Try to extract the outermost JSON object or array from the text. + self.json_data = Output._extract_json(text) + + @staticmethod + def _extract_json(text): + """Extract the first valid JSON object or array from mixed text.""" + for start_char, end_char in [('{', '}'), ('[', ']')]: + start = text.find(start_char) + if start == -1: + continue + # Search from the end backwards for the matching closing character + end = text.rfind(end_char) + if end > start: + try: + return json.loads(text[start:end + 1]) + except json.JSONDecodeError: + continue + return json.loads("{}") # return empty dict if no valid JSON found + + +def get_current_subscription(): + try: + output = run("az account show", "Retrieved az account", "Failed to get the current az account") + + if output.success and output.json_data: + subscription_id = output.json_data['id'] + subscription_name = output.json_data['name'] + print_info(f"Using Subscription ID: {subscription_id} ({subscription_name})") + return subscription_id + else: + print_error("No current subscription found.") + return None + except Exception as e: + print_error(f"Error retrieving current subscription: {e}") + return None + +# Retrieves resources in a resource group +def get_resources(resource_group_name, config): + if not resource_group_name: + print_error("Missing resource group name parameter.") + return + + resources = {} + try: + ## retrieve resource group location + output = run(f"az group show --name {resource_group_name}") + + if output.success: + print_info(f"Using existing resource group '{resource_group_name}'") + output = run(f"az group show --name {resource_group_name} -o json", "Retrieved resource group ", "Failed to retrieve resource group") + if output.success and output.json_data: + resources['resourceGroupLocation'] = output.json_data["location"] + + ## retrieve resources + output = run(f'az resource list -g {resource_group_name} -o json', "Listed resources", "Failed to list resources") + if output.success and output.json_data: + for resource in output.json_data: + match resource["type"].lower(): + case "microsoft.operationalinsights/workspaces": + resources['logAnalyticsResourceId'] = resource["id"] + resources['logAnalyticsResourceName'] = resource["name"] + case "microsoft.insights/components": + resources['appInsightsResourceId'] = resource["id"] + resources['appInsightsResourceName'] = resource["name"] + output = run(f'az resource show -g {resource_group_name} -n {resource["name"]} --resource-type "microsoft.insights/components" -o json', "Retrieved App Insights resource", "Failed to retrieve App Insights resource") + if output.success and output.json_data: + resources['appInsightsInstrumentationKey'] = output.json_data["properties"]["InstrumentationKey"] + case "microsoft.cognitiveservices/accounts": + resources['foundryResourceId'] = resource["id"] + resources['foundryResourceName'] = resource["name"] + case "microsoft.cognitiveservices/accounts/projects": + resources['foundryProjectId'] = resource["id"] + resources['foundryProjectName'] = resource["name"] + case "microsoft.apimanagement/service": + resources['apimResourceId'] = resource["id"] + resources['apimResourceName'] = resource["name"] + resources['apimPrincipalId'] = resource["identity"]["principalId"] + else: + return config + + except Exception as e: + print_error(f"Error retrieving resources: {e}") + + return resources + +# Cleans up resources associated with a deployment in a resource group +def cleanup_resources(deployment_name, resource_group_name = None): + if not deployment_name: + print_error("Missing deployment name parameter.") + return + + if not resource_group_name: + resource_group_name = f"lab-{deployment_name}" + + try: + print_info(f"🧹 Cleaning up resource group '{resource_group_name}'...") + + # Show the deployment details + output = run(f"az deployment group show --name {deployment_name} -g {resource_group_name} -o json", "Deployment retrieved", "Failed to retrieve the deployment") + + if output.success and output.json_data: + provisioning_state = output.json_data.get("properties").get("provisioningState") + print_info(f"Deployment provisioning state: {provisioning_state}") + + # Delete AI Foundry projects + output = run(f'az resource list -g {resource_group_name} --resource-type "microsoft.cognitiveservices/accounts/projects"', "Retrieved AI Foundry projects", "Failed to list AI Foundry projects") + if output.success and output.json_data: + for resource in output.json_data: + print_info(f"Deleting AI Foundry project '{resource['name']}' in resource group '{resource_group_name}'...") + output = run(f'az resource delete --ids "{resource['id']}"', f"AI Foundry project '{resource['name']}' deleted", f"Failed to delete AI Foundry project '{resource['name']}'") + + # Delete and purge CognitiveService accounts + output = run(f"az cognitiveservices account list -g {resource_group_name}", f"Listed CognitiveService accounts", f"Failed to list CognitiveService accounts") + if output.success and output.json_data: + for resource in output.json_data: + print_info(f"Deleting and purging Cognitive Service Account '{resource['name']}' in resource group '{resource_group_name}'...") + output = run(f"az cognitiveservices account delete -g {resource_group_name} -n {resource['name']}", f"Cognitive Services '{resource['name']}' deleted", f"Failed to delete Cognitive Services '{resource['name']}'") + output = run(f"az cognitiveservices account purge -g {resource_group_name} -n {resource['name']} -l \"{resource['location']}\"", f"Cognitive Services '{resource['name']}' purged", f"Failed to purge Cognitive Services '{resource['name']}'") + + # Delete and purge APIM resources + output = run(f" az apim list -g {resource_group_name}", f"Listed APIM resources", f"Failed to list APIM resources") + if output.success and output.json_data: + for resource in output.json_data: + print_info(f"Deleting and purging API Management '{resource['name']}' in resource group '{resource_group_name}'...") + output = run(f"az apim delete -n {resource['name']} -g {resource_group_name} -y", f"API Management '{resource['name']}' deleted", f"Failed to delete API Management '{resource['name']}'") + output = run(f"az apim deletedservice purge --service-name {resource['name']} --location \"{resource['location']}\"", f"API Management '{resource['name']}' purged", f"Failed to purge API Management '{resource['name']}'") + + # Delete and purge Key Vault resources + output = run(f"az keyvault list -g {resource_group_name}", f"Listed Key Vault resources", f"Failed to list Key Vault resources") + if output.success and output.json_data: + for resource in output.json_data: + print_info(f"Deleting and purging Key Vault '{resource['name']}' in resource group '{resource_group_name}'...") + output = run(f"az keyvault delete -n {resource['name']} -g {resource_group_name}", f"Key Vault '{resource['name']}' deleted", f"Failed to delete Key Vault '{resource['name']}'") + output = run(f"az keyvault purge -n {resource['name']} --location \"{resource['location']}\"", f"Key Vault '{resource['name']}' purged", f"Failed to purge Key Vault '{resource['name']}'") + + # Delete the resource group last + print_message(f"🧹 Deleting resource group '{resource_group_name}'...") + output = run(f"az group delete --name {resource_group_name} -y", f"Resource group '{resource_group_name}' deleted", f"Failed to delete resource group '{resource_group_name}'") + + print_message("🧹 Cleanup completed.") + + except Exception as e: + print(f"An error occurred during cleanup: {e}") + traceback.print_exc() + +def create_resource_group(resource_group_name, resource_group_location = None): + if not resource_group_name: + print_error('Please specify the resource group name.') + else: + output = run(f"az group show --name {resource_group_name}") + + if output.success: + print_info(f"Using existing resource group '{resource_group_name}'") + else: + if not resource_group_location: + print_error('Please specify the resource group location.') + else: + print_info(f"Resource group {resource_group_name} does not yet exist. Creating the resource group now...") + + output = run(f"az group create --name {resource_group_name} --location {resource_group_location} --tags source=ai-gateway", + f"Resource group '{resource_group_name}' created", + f"Failed to create the resource group '{resource_group_name}'") + +# Deletes a specific resource based on its type +def delete_resource(resource, resource_group_name): + resource_name = resource.get("name") + resource_type = resource.get("type") + resource_location = resource.get("location") + + print(f"🗑 Deleting {resource_type} '{resource_name}' in resource group '{resource_group_name}'...") + + # API Management + if resource_type == "Microsoft.ApiManagement/service": + output = run(f"az apim delete -n {resource_name} -g {resource_group_name} -y", f"API Management '{resource_name}' deleted", f"Failed to delete API Management '{resource_name}'") + + output = run(f"az apim deletedservice purge --service-name {resource_name} --location \"{resource_location}\"", f"API Management '{resource_name}' purged", f"Failed to purge API Management '{resource_name}'") + + # Cognitive Services + elif resource_type == "Microsoft.CognitiveServices/accounts": + output = run(f"az cognitiveservices account delete -g {resource_group_name} -n {resource_name}", f"Cognitive Services '{resource_name}' deleted", f"Failed to delete Cognitive Services '{resource_name}'") + + output = run(f"az cognitiveservices account purge -g {resource_group_name} -n {resource_name} -l \"{resource_location}\"", f"Cognitive Services '{resource_name}' purged", f"Failed to purge Cognitive Services '{resource_name}'") + + # Key Vault + elif resource_type == "Microsoft.KeyVault/vaults": + output = run(f"az keyvault delete -n {resource_name} -g {resource_group_name}", f"Key Vault '{resource_name}' deleted", f"Failed to delete Key Vault '{resource_name}'") + +def get_deployment_output(output, output_property, output_label = '', secure = False) -> str: + try: + deployment_output = output.json_data['properties']['outputs'][output_property]['value'] + + if output_label: + if secure: + print_info(f"{output_label}: ****{deployment_output[-4:]}") + else: + print_info(f"{output_label}: {deployment_output}") + + return str(deployment_output) + except Exception as e: + error = f"Failed to retrieve output property: '{output_property}'\nError: {e}" + print_error(error) + raise Exception(error) + +def print_response(response): + print("Response headers: ", response.headers) + + if (response.status_code == 200): + print_ok(f"Status Code: {response.status_code}") + data = json.loads(response.text) + print(json.dumps(data, indent=4)) + else: + print_warning(f"Status Code: {response.status_code}") + print(response.text) + +def print_response_code(response): + # Check the response status code and apply formatting + if 200 <= response.status_code < 300: + status_code_str = f"{BOLD_GREEN}{response.status_code} - {response.reason}{RESET_FORMATTING}" + elif response.status_code >= 400: + status_code_str = f"{BOLD_RED}{response.status_code} - {response.reason}{RESET_FORMATTING}" + else: + status_code_str = str(response.status_code) + + # Print the response status with the appropriate formatting + print(f"Response status: {status_code_str}") + +# Simple: print full error body (JSON if available, else raw text) +def print_full_http_error(response): + try: + data = response.json() + print_error("Request failed. Full JSON body:", json.dumps(data, indent=2)) + # If ARM-style error present, surface message too + if isinstance(data, dict) and isinstance(data.get("error"), dict): + code = data["error"].get("code", "") + msg = data["error"].get("message", "") + if msg or code: + print_error(f"Service error:", f"{code} - {msg}") + except ValueError: + print_error("Request failed. Full text body:", response.text or "") + +def run(command, ok_message = '', error_message = '', print_output = False, print_command_to_run = True): + if print_command_to_run: + print_command(command) + + start_time = time.time() + + try: + completed_process = subprocess.run( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + encoding="utf-8", + errors="replace", + ) + output_text = completed_process.stdout or "" + stderr_text = completed_process.stderr or "" + success = completed_process.returncode == 0 + except subprocess.CalledProcessError as e: + output_text = e.output.decode("utf-8", errors="replace") if isinstance(e.output, (bytes, bytearray)) else (e.output or "") + stderr_text = e.stderr.decode("utf-8", errors="replace") if isinstance(e.stderr, (bytes, bytearray)) else (e.stderr or "") + success = False + + # Combine stdout and stderr for error reporting, but keep stdout clean for JSON parsing + combined_text = output_text + ("\n" + stderr_text if stderr_text else "") + + minutes, seconds = divmod(time.time() - start_time, 60) + + print_message = print_ok if success else print_error + + if (ok_message or error_message): + print_message(ok_message if success else error_message, combined_text if not success or print_output else "", f"[{int(minutes)}m:{int(seconds)}s]") + + return Output(success, output_text) + +def create_bicep_params(policy_xml_filepath, parameters_filepath, bicep_parameters, replacements_list): + # Read the specified policy XML file + with open(policy_xml_filepath, 'r') as policy_xml_file: + policy_template_xml = policy_xml_file.read() + + # Replace the placeholders in the policy XML with the actual values from the replacements_lists array + for key, value in replacements_list: + policy_template_xml = policy_template_xml.replace(key, str(value)) + + # Set or update the policyXml parameter in the bicep parameters file + bicep_parameters['parameters'].setdefault('policyXml', {}) + bicep_parameters['parameters']['policyXml']['value'] = policy_template_xml + + # Write the updated bicep parameters to the specified parameters file + with open(parameters_filepath, 'w') as bicep_parameters_file: + bicep_parameters_file.write(json.dumps(bicep_parameters)) + + print(f"📝 Updated the policy XML in the bicep parameters file '{parameters_filepath}'") + + return bicep_parameters + +def update_api_policy(subscription_id, resource_group_name, apim_service_name, api_id, policy_xml): + # We first need to obtain an access token for the REST API + output = run(f"az account get-access-token --resource https://management.azure.com/", + f"Successfully obtained access token", f"Failed to obtain access token") + + if output.success and output.json_data: + access_token = output.json_data['accessToken'] + + print("Updating the API policy...") + # https://learn.microsoft.com/en-us/rest/api/apimanagement/api-policy/create-or-update?view=rest-apimanagement-2024-06-01-preview + url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.ApiManagement/service/{apim_service_name}/apis/{api_id}/policies/policy?api-version=2024-06-01-preview" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {access_token}" + } + + body = { + "properties": { + "format": "rawxml", + "value": policy_xml + } + } + + response = requests.put(url, headers = headers, json = body) + if 200 <= response.status_code < 300: + print_response_code(response) + else: + print_response_code(response) + print_full_http_error(response) + +def update_api_operation_policy(subscription_id, resource_group_name, apim_service_name, api_id, operation_id, policy_xml): + # We first need to obtain an access token for the REST API + output = run(f"az account get-access-token --resource https://management.azure.com/", + f"Successfully obtained access token", f"Failed to obtain access token") + + if output.success and output.json_data: + access_token = output.json_data['accessToken'] + + print("Updating the API policy...") + # https://learn.microsoft.com/en-us/rest/api/apimanagement/api-policy/create-or-update?view=rest-apimanagement-2024-06-01-preview + url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.ApiManagement/service/{apim_service_name}/apis/{api_id}/operations/{operation_id}/policies/policy?api-version=2024-06-01-preview" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {access_token}" + } + + body = { + "properties": { + "format": "rawxml", + "value": policy_xml + } + } + + response = requests.put(url, headers = headers, json = body) + print_response_code(response) + +def get_debug_credentials(apim_service_id, api_id, expire_after = 'PT1H') -> str | None: + request = { + "credentialsExpireAfter": expire_after, + "apiId": f"{apim_service_id}/apis/{api_id}", + "purposes": ["tracing"] + } + output = run(f"az rest --method post --uri {apim_service_id}/gateways/managed/listDebugCredentials?api-version=2023-05-01-preview --body \"{str(request)}\"", + "Retrieved APIM debug credentials", "Failed to get the APIM debug credentials") + return output.json_data['token'] if output.success and output.json_data else None + +def get_trace(apim_service_id, trace_id) -> str | None: + request = { + "traceId": trace_id + } + output = run(f"az rest --method post --uri {apim_service_id}/gateways/managed/listTrace?api-version=2023-05-01-preview --body \"{str(request)}\"", + "Retrieved trace details", "Failed to get the trace details") + return output.json_data if output.success and output.json_data else None + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/1. llm-backend-onboarding-runner.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/1. llm-backend-onboarding-runner.ipynb new file mode 100644 index 000000000..9d3221154 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/1. llm-backend-onboarding-runner.ipynb @@ -0,0 +1,1224 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d21f1606", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🏰 Citadel Backend Contracts - Testing Center\n", + "\n", + "## Explore How to Bring New AI Backends/Models into the Citadel Governance Hub!\n", + "\n", + "Use this Jupyter notebook with Python code snippets to onboard all your AI backends and all associated routing logic, including:\n", + "- Configuring LLM backend endpoints and authentication\n", + "- Generating Bicep parameter files for deployment\n", + "- Deploying backend pools and policy fragments\n", + "- Testing deployed models through multiple API formats\n", + "- Verifying streaming and SDK integration\n", + "\n", + "> **Note:** This notebook assumes you have already deployed your Citadel Governance Hub. If you haven't done so, please refer to the [Citadel Governance Hub Deployment Guide](../guides/full-deployment-guide.md) or [Citadel Governance Hub Quick Deployment Guide](../guides/quick-deployment-guide.md) before proceeding.\n", + "\n", + "## Azure Prerequisites\n", + "\n", + "- An existing Citadel Governance Hub deployment with APIM configured\n", + "- LLM backend endpoints (AI Foundry, Azure OpenAI, or external) provisioned and accessible\n", + "- Managed Identity configured for APIM authentication to backend services" + ] + }, + { + "cell_type": "markdown", + "id": "09a5052f", + "metadata": {}, + "source": [ + "\n", + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:\n", + "\n", + "> **NOTE:** If you used Azure Developer CLI to deploy your AI Governance Hub, you can find the required `llm_backends_config` value by running the following command: `azd env get LLM_BACKENDS_CONFIG`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea866817", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "env_llm_backends_config = json.loads(azd_get_value(\"LLM_BACKEND_CONFIG\"))\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")\n", + "print(f\"LLM Backends: {len(env_llm_backends_config)} backend(s) configured\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0082cc35", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "\n", + "# ============================================================================\n", + "# REQUIRED: Update these values for your environment\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "#### WARNING: UPDATE THIS CONFIGURATION BASED ON YOUR DEPLOYED BACKENDS ####\n", + "#### If you used azd to deploy backends as well, you can copy the current backend configuration using `azd env get-value LLM_BACKEND_CONFIG` and paste it here, then update the values as needed. ####\n", + "llm_backends_config = env_llm_backends_config\n", + "\n", + "# ============================================================================\n", + "# OPTIONAL: Model Aliases\n", + "# Group multiple models under a single client-facing name. Clients send the\n", + "# alias as the `model` value; the gateway resolves it to a real model.\n", + "# strategy: 'priority' (first available wins) | 'weighted' (round-robin by weights)\n", + "# Example:\n", + "# model_aliases = [\n", + "# { \"name\": \"gpt-advanced\", \"models\": [\"gpt-5\", \"gpt-4.1\", \"gpt-4o\"], \"strategy\": \"priority\" }\n", + "# ]\n", + "# ============================================================================\n", + "model_aliases = []\n", + "\n", + "# ============================================================================\n", + "# OPTIONAL: Key Vault for backend credential references\n", + "# Required when any backend's authConfig uses `keyVaultSecretUri`. The APIM\n", + "# managed identity must have `Key Vault Secrets User` role on this vault.\n", + "# ============================================================================\n", + "key_vault_name = \"\"\n", + "\n", + "# ============================================================================\n", + "# OPTIONAL: AWS credentials (only required when an aws-bedrock backend is used)\n", + "# Stored as APIM secret named values: aws-access-key / aws-secret-key / aws-region\n", + "# ============================================================================\n", + "aws_access_key = \"\"\n", + "aws_secret_key = \"\"\n", + "aws_region = \"\"\n", + "\n", + "# Managed Identity for APIM authentication (will be auto-discovered if not specified)\n", + "apim_managed_identity_name = \"\" # Leave empty to auto-discover\n", + "\n", + "utils.print_info(f\"Initialization completed.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "39e7c8fd", + "metadata": {}, + "source": [ + "\n", + "### 1️⃣ Verify Azure CLI and Connected Subscription\n", + "\n", + "Ensure Azure CLI is authenticated and connected to the correct subscription:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57f655ad", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "92ac82f7", + "metadata": {}, + "source": [ + "\n", + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "👉 An existing Citadel Governance Hub deployment is expected. Initialize the APIM client to interact with your deployment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7ed7607", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(\n", + " governance_hub_resource_group\n", + " )\n", + " apimClientTool.initialize()\n", + " \n", + " apim_resource_name = apimClientTool.apim_resource_name\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " \n", + " utils.print_ok(f\"APIM Client Tool initialized successfully!\")\n", + " utils.print_info(f\"APIM Resource Name: {apim_resource_name}\")\n", + " utils.print_info(f\"APIM Gateway URL: {apim_resource_gateway_url}\")\n", + " \n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client Tool: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "933b7e05", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣ Extract Current APIM Backend-Pools Configuration\n", + "\n", + "Retrieve and analyze the existing backend pools and backends configured in your APIM instance:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "517e10fc", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract current backends from APIM using the SDK\n", + "utils.print_info(\"Extracting current APIM backends configuration...\")\n", + "\n", + "try:\n", + " # Use the APIMClientTool's new get_backends method (uses Azure SDK instead of CLI)\n", + " existing_backends, existing_backend_pools = apimClientTool.get_backends()\n", + " \n", + "except Exception as e:\n", + " utils.print_error(f\"Error extracting backends: {e}\")\n", + " existing_backends = []\n", + " existing_backend_pools = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "309c5431", + "metadata": {}, + "outputs": [], + "source": [ + "# Get supported models from the policy fragment (if exists)\n", + "try:\n", + " supported_models_from_policy = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_ok(f\"Supported models in APIM policy fragment 'set-backend-pools':\")\n", + " for model in supported_models_from_policy:\n", + " print(f\" • {model}\")\n", + "except Exception as e:\n", + " utils.print_warning(f\"Could not retrieve policy fragment (may not exist yet): {e}\")\n", + " supported_models_from_policy = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae0ad630", + "metadata": {}, + "outputs": [], + "source": [ + "# Display summary of current configuration\n", + "utils.print_info(\"\\n\" + \"=\"*60)\n", + "utils.print_info(\"CURRENT APIM BACKEND CONFIGURATION SUMMARY\")\n", + "utils.print_info(\"=\"*60)\n", + "\n", + "if existing_backends:\n", + " print(\"\\n📋 Individual Backends:\")\n", + " for backend in existing_backends:\n", + " print(f\" • {backend['name']}\")\n", + " print(f\" URL: {backend['url']}\")\n", + " if backend['supportedModels']:\n", + " print(f\" Models: {', '.join(backend['supportedModels'])}\")\n", + "\n", + "if existing_backend_pools:\n", + " print(\"\\n📦 Backend Pools:\")\n", + " for pool in existing_backend_pools:\n", + " print(f\" • {pool['name']}\")\n", + " for svc in pool['services']:\n", + " print(f\" - {svc.get('id', 'N/A')} (priority: {svc.get('priority', 'N/A')}, weight: {svc.get('weight', 'N/A')})\")\n", + "\n", + "if supported_models_from_policy:\n", + " print(f\"\\n🤖 Total Supported Models: {len(supported_models_from_policy)}\")\n", + " print(f\" {', '.join(supported_models_from_policy)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "588f4643", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣ Discover Managed Identity for APIM Authentication\n", + "\n", + "Auto-discover or specify the user-assigned managed identity used by APIM:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e56519b", + "metadata": {}, + "outputs": [], + "source": [ + "# Discover managed identity from APIM using the SDK\n", + "utils.print_info(\"Discovering managed identity configuration...\")\n", + "\n", + "# Use the APIMClientTool's get_managed_identity_info method\n", + "managed_identity_info = apimClientTool.get_managed_identity_info()\n", + "\n", + "managed_identity_client_id = managed_identity_info.get('clientId')\n", + "managed_identity_name = managed_identity_info.get('name') or apim_managed_identity_name\n", + "managed_identity_resource_group = managed_identity_info.get('resourceGroup') or governance_hub_resource_group\n", + "\n", + "if not managed_identity_client_id:\n", + " utils.print_warning(\"Could not auto-discover managed identity. Please specify it manually in the configuration.\")\n", + "else:\n", + " utils.print_info(f\"Client ID: {managed_identity_client_id}\")\n", + "\n", + "if managed_identity_name:\n", + " utils.print_ok(f\"Managed Identity Name: {managed_identity_name}\")\n", + " utils.print_ok(f\"Managed Identity Resource Group: {managed_identity_resource_group}\")" + ] + }, + { + "cell_type": "markdown", + "id": "58385417", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣ Generate LLM Backend Parameter File\n", + "\n", + "Generate a customizable `.bicepparam` file with the full list of LLM backends to be integrated with APIM:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b64e79b", + "metadata": {}, + "outputs": [], + "source": [ + "# Configure the LLM backends for deployment\n", + "# You can modify the llm_backends_config list defined in the initialization cell\n", + "\n", + "utils.print_info(\"LLM Backends to be deployed:\")\n", + "for backend in llm_backends_config:\n", + " print(f\"\\n 🔗 {backend['backendId']}\")\n", + " print(f\" Type: {backend['backendType']}\")\n", + " print(f\" Endpoint: {backend['endpoint']}\")\n", + " auth = backend.get('authType') or backend.get('authScheme') or 'unspecified'\n", + " print(f\" Auth: {auth}\")\n", + " auth_config = backend.get('authConfig')\n", + " if auth_config:\n", + " nv = auth_config.get('namedValueKey', 'N/A')\n", + " if auth_config.get('keyVaultSecretUri'):\n", + " print(f\" Auth Source: Key Vault → {auth_config['keyVaultSecretUri']} (named value: {nv})\")\n", + " elif auth_config.get('secretValue'):\n", + " print(f\" Auth Source: inline secret (named value: {nv}) ⚠️ testing only\")\n", + " else:\n", + " print(f\" Auth Source: named value '{nv}'\")\n", + " print(f\" Priority: {backend.get('priority', 1)}, Weight: {backend.get('weight', 100)}\")\n", + " # Display supported models with their per-model metadata\n", + " print(f\" Models ({len(backend['supportedModels'])}):\")\n", + " for model in backend['supportedModels']:\n", + " model_name = model['name'] if isinstance(model, dict) else model\n", + " if isinstance(model, dict):\n", + " sku = model.get('sku', 'Standard')\n", + " capacity = model.get('capacity', 100)\n", + " fmt = model.get('modelFormat', 'OpenAI')\n", + " ver = model.get('modelVersion', '1')\n", + " extras = []\n", + " if 'retirementDate' in model:\n", + " extras.append(f\"Retirement: {model['retirementDate']}\")\n", + " if 'apiVersion' in model:\n", + " extras.append(f\"API: {model['apiVersion']}\")\n", + " if 'timeout' in model:\n", + " extras.append(f\"Timeout: {model['timeout']}s\")\n", + " if 'inferenceApiVersion' in model:\n", + " extras.append(f\"Inference API: {model['inferenceApiVersion']}\")\n", + " extra_str = f\", {', '.join(extras)}\" if extras else \"\"\n", + " print(f\" - {model_name} (SKU: {sku}, Capacity: {capacity}, Format: {fmt}, Version: {ver}{extra_str})\")\n", + " else:\n", + " print(f\" - {model_name}\")\n", + "\n", + "# Display model aliases if defined\n", + "if model_aliases:\n", + " utils.print_info(f\"\\nModel Aliases ({len(model_aliases)}):\")\n", + " for alias in model_aliases:\n", + " strategy = alias.get('strategy', 'priority')\n", + " models_str = ', '.join(alias.get('models', []))\n", + " weights = alias.get('weights')\n", + " weights_str = f\" weights={weights}\" if weights else \"\"\n", + " print(f\" • {alias['name']} → [{models_str}] (strategy: {strategy}{weights_str})\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e204ab03", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate the .bicepparam file content\n", + "bicep_dir = \"../bicep/infra/llm-backend-onboarding\"\n", + "params_file = os.path.join(bicep_dir, \"llm-backends-generated-local.bicepparam\")\n", + "\n", + "# Format a single model object for Bicep\n", + "def format_model_for_bicep(model):\n", + " \"\"\"Format a model object for Bicep with per-model metadata including optional routing attributes.\"\"\"\n", + " if isinstance(model, str):\n", + " # Legacy format: just model name string - convert to object with defaults\n", + " return f\"{{ name: '{model}' }}\"\n", + " \n", + " # New format: model object with metadata\n", + " parts = [f\"name: '{model['name']}'\"]\n", + " if 'sku' in model:\n", + " parts.append(f\"sku: '{model['sku']}'\")\n", + " if 'capacity' in model:\n", + " parts.append(f\"capacity: {model['capacity']}\")\n", + " if 'modelFormat' in model:\n", + " parts.append(f\"modelFormat: '{model['modelFormat']}'\")\n", + " if 'modelVersion' in model:\n", + " parts.append(f\"modelVersion: '{model['modelVersion']}'\")\n", + " if 'retirementDate' in model:\n", + " parts.append(f\"retirementDate: '{model['retirementDate']}'\")\n", + " if 'apiVersion' in model:\n", + " parts.append(f\"apiVersion: '{model['apiVersion']}'\")\n", + " if 'timeout' in model:\n", + " parts.append(f\"timeout: {model['timeout']}\")\n", + " if 'inferenceApiVersion' in model:\n", + " parts.append(f\"inferenceApiVersion: '{model['inferenceApiVersion']}'\")\n", + " \n", + " return \"{ \" + \", \".join(parts) + \" }\"\n", + "\n", + "# Format an authConfig object for Bicep\n", + "def format_auth_config_for_bicep(auth_config):\n", + " parts = []\n", + " if 'namedValueKey' in auth_config:\n", + " parts.append(f\"namedValueKey: '{auth_config['namedValueKey']}'\")\n", + " if 'keyVaultSecretUri' in auth_config:\n", + " parts.append(f\"keyVaultSecretUri: '{auth_config['keyVaultSecretUri']}'\")\n", + " if 'secretValue' in auth_config:\n", + " parts.append(f\"secretValue: '{auth_config['secretValue']}'\")\n", + " return \"{ \" + \", \".join(parts) + \" }\"\n", + "\n", + "# Format backends array for Bicep (uses per-model metadata)\n", + "def format_backend_for_bicep(backend):\n", + " \"\"\"Format a backend configuration for Bicep with per-model metadata.\"\"\"\n", + " # Format each model with its individual metadata\n", + " models_formatted = [format_model_for_bicep(m) for m in backend['supportedModels']]\n", + " models_str = \"\\n \".join(models_formatted)\n", + "\n", + " # Auth: prefer new authType, fall back to legacy authScheme\n", + " auth_lines = []\n", + " if 'authType' in backend:\n", + " auth_lines.append(f\" authType: '{backend['authType']}'\")\n", + " elif 'authScheme' in backend:\n", + " auth_lines.append(f\" authScheme: '{backend['authScheme']}'\")\n", + " if 'authConfig' in backend:\n", + " auth_lines.append(f\" authConfig: {format_auth_config_for_bicep(backend['authConfig'])}\")\n", + " auth_block = (\"\\n\".join(auth_lines) + \"\\n\") if auth_lines else \"\"\n", + "\n", + " return f\"\"\" {{\n", + " backendId: '{backend['backendId']}'\n", + " backendType: '{backend['backendType']}'\n", + " endpoint: '{backend['endpoint']}'\n", + "{auth_block} supportedModels: [\n", + " {models_str}\n", + " ]\n", + " priority: {backend.get('priority', 1)}\n", + " weight: {backend.get('weight', 100)}\n", + " }}\"\"\"\n", + "\n", + "# Format model alias array for Bicep\n", + "def format_alias_for_bicep(alias):\n", + " parts = [f\"name: '{alias['name']}'\"]\n", + " models_arr = \", \".join([f\"'{m}'\" for m in alias.get('models', [])])\n", + " parts.append(f\"models: [ {models_arr} ]\")\n", + " if 'strategy' in alias:\n", + " parts.append(f\"strategy: '{alias['strategy']}'\")\n", + " if 'weights' in alias:\n", + " weights_arr = \", \".join([str(w) for w in alias['weights']])\n", + " parts.append(f\"weights: [ {weights_arr} ]\")\n", + " return \" { \" + \", \".join(parts) + \" }\"\n", + "\n", + "backends_bicep_str = \"\\n\".join([format_backend_for_bicep(b) for b in llm_backends_config])\n", + "\n", + "# Optional sections\n", + "aliases_block = \"\"\n", + "if model_aliases:\n", + " aliases_str = \"\\n\".join([format_alias_for_bicep(a) for a in model_aliases])\n", + " aliases_block = f\"\"\"\n", + "\n", + "// ============================================================================\n", + "// Model Aliases — group multiple models under a single client-facing name\n", + "// ============================================================================\n", + "param modelAliases = [\n", + "{aliases_str}\n", + "]\n", + "\"\"\"\n", + "else:\n", + " aliases_block = \"\\n\\nparam modelAliases = []\\n\"\n", + "\n", + "key_vault_block = \"\"\n", + "if key_vault_name:\n", + " key_vault_block = f\"\"\"\n", + "// ============================================================================\n", + "// Key Vault for backend credential references (used by authConfig.keyVaultSecretUri)\n", + "// ============================================================================\n", + "param keyVaultName = '{key_vault_name}'\n", + "\"\"\"\n", + "\n", + "aws_block = \"\"\n", + "if aws_access_key or aws_secret_key or aws_region:\n", + " aws_block = f\"\"\"\n", + "// ============================================================================\n", + "// AWS credentials for Amazon Bedrock backends (stored as APIM secret named values)\n", + "// ============================================================================\n", + "param awsAccessKey = '{aws_access_key}'\n", + "param awsSecretKey = '{aws_secret_key}'\n", + "param awsRegion = '{aws_region}'\n", + "\"\"\"\n", + "\n", + "params_content = f\"\"\"using './main.bicep'\n", + "\n", + "// ============================================================================\n", + "// LLM Backend Onboarding - Generated Parameter File\n", + "// Generated: {time.strftime('%Y-%m-%d %H:%M:%S')}\n", + "// ============================================================================\n", + "\n", + "// ============================================================================\n", + "// API Management (APIM) Configuration\n", + "// ============================================================================\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apim_resource_name}'\n", + "}}\n", + "\n", + "// ============================================================================\n", + "// APIM Managed Identity Configuration\n", + "// ============================================================================\n", + "param apimManagedIdentity = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{managed_identity_resource_group}'\n", + " name: '{managed_identity_name}'\n", + "}}\n", + "\n", + "// ============================================================================\n", + "// LLM Backend Configuration Array\n", + "// Each backend uses 'authType' (managed-identity | aws-sigv4 | api-key-bearer\n", + "// | api-key-header | none); api-key auth types also require 'authConfig'.\n", + "// Each model in supportedModels has its own metadata (sku, capacity,\n", + "// modelFormat, modelVersion, retirementDate, apiVersion, timeout,\n", + "// inferenceApiVersion).\n", + "// ============================================================================\n", + "param llmBackendConfig = [\n", + "{backends_bicep_str}\n", + "]\n", + "\n", + "// ============================================================================\n", + "// Circuit Breaker Configuration\n", + "// ============================================================================\n", + "param configureCircuitBreaker = true\n", + "{aliases_block}{key_vault_block}{aws_block}\"\"\"\n", + "\n", + "# Write the parameter file\n", + "utils.print_info(f\"Generating parameter file: {params_file}\")\n", + "with open(params_file, 'w') as f:\n", + " f.write(params_content)\n", + "\n", + "utils.print_ok(f\"Parameter file generated successfully!\")\n", + "print(\"\\n\" + \"=\"*60)\n", + "print(\"GENERATED PARAMETER FILE CONTENT:\")\n", + "print(\"=\"*60)\n", + "print(params_content)\n" + ] + }, + { + "cell_type": "markdown", + "id": "79744a37", + "metadata": {}, + "source": [ + "\n", + "### 6️⃣ Deploy LLM Backend Onboarding Bicep\n", + "\n", + "Deploy the LLM backends, backend pools, and policy fragments to APIM:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00749dfb", + "metadata": {}, + "outputs": [], + "source": [ + "# Deploy the LLM backend onboarding\n", + "deployment_name = f\"llm-backend-onboarding-{time.strftime('%Y%m%d%H%M%S')}\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "utils.print_info(f\"Starting deployment: {deployment_name}\")\n", + "utils.print_info(f\"Template: {template_file}\")\n", + "utils.print_info(f\"Parameters: {params_file}\")\n", + "\n", + "# Run the subscription-level deployment\n", + "deployment_cmd = f\"az deployment sub create --name {deployment_name} --location {location} --template-file {template_file} --parameters {params_file}\"\n", + "\n", + "output = utils.run(\n", + " deployment_cmd,\n", + " f\"Deployment '{deployment_name}' succeeded\",\n", + " f\"Deployment '{deployment_name}' failed\"\n", + ")\n", + "\n", + "if output.success:\n", + " utils.print_ok(\"Deployment completed successfully!\")\n", + " \n", + " # Display deployment outputs if available\n", + " outputs = output.json_data.get('properties', {}).get('outputs', {}) if output.json_data else {}\n", + " \n", + " if outputs:\n", + " print(\"\\n\" + \"=\"*60)\n", + " print(\"DEPLOYMENT OUTPUTS:\")\n", + " print(\"=\"*60)\n", + " \n", + " for key, value in outputs.items():\n", + " print(f\" {key}: {value.get('value')}\")\n", + " else:\n", + " utils.print_info(\"No deployment outputs returned.\")\n", + "else:\n", + " utils.print_error(\"Deployment failed. Check the error messages above.\")" + ] + }, + { + "cell_type": "markdown", + "id": "c5155939", + "metadata": {}, + "source": [ + "\n", + "### 7️⃣ Verify Deployed Configuration\n", + "\n", + "Verify that the backends, pools, and policy fragments were created successfully:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56c33691", + "metadata": {}, + "outputs": [], + "source": [ + "# Re-initialize APIM client to pick up new backends\n", + "apimClientTool.initialize()\n", + "\n", + "# Get updated supported models from policy fragment\n", + "try:\n", + " updated_supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_ok(f\"Updated supported models in APIM policy fragment 'set-backend-pools':\")\n", + " for model in updated_supported_models:\n", + " print(f\" • {model}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error retrieving policy fragment: {e}\")\n", + " updated_supported_models = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4fc5260", + "metadata": {}, + "outputs": [], + "source": [ + "# Display summary of current configuration\n", + "utils.print_info(\"\\n\" + \"=\"*60)\n", + "utils.print_info(\"CURRENT APIM BACKEND CONFIGURATION SUMMARY\")\n", + "utils.print_info(\"=\"*60)\n", + "\n", + "if existing_backends:\n", + " print(\"\\n📋 Individual Backends:\")\n", + " for backend in existing_backends:\n", + " print(f\" • {backend['name']}\")\n", + " print(f\" URL: {backend['url']}\")\n", + " if backend['supportedModels']:\n", + " print(f\" Models: {', '.join(backend['supportedModels'])}\")\n", + "\n", + "if existing_backend_pools:\n", + " print(\"\\n📦 Backend Pools:\")\n", + " for pool in existing_backend_pools:\n", + " print(f\" • {pool['name']}\")\n", + " for svc in pool['services']:\n", + " print(f\" - {svc.get('id', 'N/A')} (priority: {svc.get('priority', 'N/A')}, weight: {svc.get('weight', 'N/A')})\")\n", + "\n", + "if supported_models_from_policy:\n", + " print(f\"\\n🤖 Total Supported Models: {len(supported_models_from_policy)}\")\n", + " print(f\" {', '.join(supported_models_from_policy)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "052b0a74", + "metadata": {}, + "source": [ + "\n", + "### 8️⃣ Verify Get Available Models Policy Fragment\n", + "\n", + "Verify that the new `get-available-models` policy fragment was created successfully:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60a60734", + "metadata": {}, + "outputs": [], + "source": [ + "# Verify the get-available-models policy fragment exists\n", + "try:\n", + " # Use the Azure SDK to check if the policy fragment exists\n", + " policy_fragment = apimClientTool.client.policy_fragment.get(\n", + " resource_group_name=apimClientTool.resource_group_name,\n", + " service_name=apimClientTool.apim_resource_name,\n", + " id=\"get-available-models\"\n", + " )\n", + " \n", + " if policy_fragment:\n", + " utils.print_ok(\"Policy fragment 'get-available-models' exists!\")\n", + " utils.print_info(\"This fragment returns available model deployments in a format similar to Azure Cognitive Services API.\")\n", + " utils.print_info(f\"Description: {policy_fragment.description}\")\n", + "except Exception as e:\n", + " utils.print_warning(f\"Could not retrieve 'get-available-models' policy fragment: {e}\")\n", + " utils.print_info(\"This fragment will be created after running the deployment.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a5b41a43", + "metadata": {}, + "source": [ + "\n", + "### 🧪 Test GET /deployments (Microsoft Foundry Integration)\n", + "\n", + "Test the `GET /deployments` endpoint which leverages the `get-available-models` policy fragment. This endpoint is used by Microsoft Foundry to discover available model deployments:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ad5b073", + "metadata": {}, + "outputs": [], + "source": [ + "# Test GET /deployments endpoint (Microsoft Foundry integration)\n", + "# This endpoint uses the get-available-models policy fragment to return available model deployments\n", + "# Testing both Universal LLM API and Azure OpenAI API endpoints\n", + "\n", + "def test_get_deployments(base_endpoint, api_name, api_key):\n", + " \"\"\"Test GET /deployments for a given API endpoint.\"\"\"\n", + " deployments_url = f\"{base_endpoint}deployments?api-version={inference_api_version}\"\n", + " utils.print_info(f\"\\nTesting {api_name} GET /deployments: {deployments_url}\")\n", + " \n", + " try:\n", + " response = requests.get(\n", + " deployments_url,\n", + " headers={\"api-key\": api_key},\n", + " timeout=30\n", + " )\n", + " \n", + " utils.print_response_code(response)\n", + " \n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " deployments = data.get(\"value\", [])\n", + " \n", + " utils.print_ok(f\"{api_name} GET /deployments returned {len(deployments)} model deployment(s)\")\n", + " \n", + " print(\"\\n\" + \"=\"*60)\n", + " print(f\"AVAILABLE MODEL DEPLOYMENTS ({api_name})\")\n", + " print(\"=\"*60)\n", + " \n", + " for deployment in deployments:\n", + " print(f\"\\n📦 Deployment: {deployment.get('name', 'N/A')}\")\n", + " print(f\" ID: {deployment.get('id', 'N/A')}\")\n", + " print(f\" Type: {deployment.get('type', 'N/A')}\")\n", + " \n", + " sku = deployment.get('sku', {})\n", + " if sku:\n", + " print(f\" SKU: {sku.get('name', 'N/A')} (Capacity: {sku.get('capacity', 'N/A')})\")\n", + " \n", + " props = deployment.get('properties', {})\n", + " if props:\n", + " model = props.get('model', {})\n", + " if model:\n", + " print(f\" Model: {model.get('name', 'N/A')} (Format: {model.get('format', 'N/A')}, Version: {model.get('version', 'N/A')})\")\n", + " \n", + " capabilities = props.get('capabilities', {})\n", + " if capabilities:\n", + " caps_list = [k for k, v in capabilities.items() if v == 'true' or v == True]\n", + " print(f\" Capabilities: {', '.join(caps_list) if caps_list else 'N/A'}\")\n", + " \n", + " print(f\" Status: {props.get('provisioningState', 'N/A')}\")\n", + " \n", + " print(\"\\n\" + \"=\"*60)\n", + " utils.print_ok(f\"{api_name} GET /deployments test completed successfully!\")\n", + " return True\n", + " else:\n", + " utils.print_error(f\"{api_name} GET /deployments failed: {response.status_code}\")\n", + " print(f\"Response: {response.text}\")\n", + " return False\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"{api_name} GET /deployments test failed: {str(e)}\")\n", + " return False\n", + "\n", + "# Get an API key from subscriptions\n", + "if apimClientTool.apim_subscriptions:\n", + " api_key = apimClientTool.apim_subscriptions[0].get(\"key\")\n", + " utils.print_ok(f\"Using subscription: {apimClientTool.apim_subscriptions[0].get('name')}\")\n", + "else:\n", + " utils.print_error(\"No APIM subscriptions found. Please create a subscription first.\")\n", + " api_key = None\n", + "\n", + "if api_key:\n", + " # Test 1: Universal LLM API - GET /models/deployments\n", + " apimClientTool.discover_api(\"models\")\n", + " azure_endpoint_models = str(apimClientTool.azure_endpoint)\n", + " utils.print_info(f\"Universal LLM API Base Endpoint: {azure_endpoint_models}models\")\n", + " test_get_deployments(f\"{azure_endpoint_models}models/\", \"Universal LLM API\", api_key)\n", + " \n", + " # Test 2: Azure OpenAI API - GET /openai/deployments\n", + " try:\n", + " apimClientTool.discover_api(\"openai\")\n", + " azure_endpoint_openai = str(apimClientTool.azure_endpoint)\n", + " utils.print_info(f\"\\nAzure OpenAI API Base Endpoint: {azure_endpoint_openai}openai\")\n", + " test_get_deployments(f\"{azure_endpoint_openai}openai/\", \"Azure OpenAI API\", api_key)\n", + " except Exception as e:\n", + " utils.print_warning(f\"Azure OpenAI API not found in APIM: {e}\")\n", + "else:\n", + " utils.print_warning(\"Cannot test GET /deployments - missing API key\")" + ] + }, + { + "cell_type": "markdown", + "id": "665410dc", + "metadata": {}, + "source": [ + "---\n", + "## 🧪 Test Deployed Models\n", + "\n", + "The following sections test the deployed models through both the Universal LLM API and Azure OpenAI API endpoints.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "8542c43a", + "metadata": {}, + "source": [ + "\n", + "### 🧪 Test via Universal LLM API (models/chat/completions)\n", + "\n", + "Test the deployed models using the Universal LLM API which routes based on the `model` field in the request body:\n", + "\n", + "> **Note:** Some models may not support the chat/completions format. Please refer to your LLM backend documentation for supported API formats per model. There is a validation notebook that is designed to test both chat/completions, embeddings, and response formats." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7842c141", + "metadata": {}, + "outputs": [], + "source": [ + "# Discover the Universal LLM API endpoint\n", + "apimClientTool.discover_api(\"models\")\n", + "azure_endpoint_models = str(apimClientTool.azure_endpoint)\n", + "chat_completions_url_models = f\"{azure_endpoint_models}models/chat/completions?api-version={inference_api_version}\"\n", + "\n", + "utils.print_info(f\"Universal LLM API Endpoint: {chat_completions_url_models}\")\n", + "\n", + "# Get an API key from subscriptions (it will get the most recently created Access Contract which should be the one we created for testing)\n", + "if apimClientTool.apim_subscriptions:\n", + " api_key = apimClientTool.apim_subscriptions[0].get(\"key\")\n", + " utils.print_ok(f\"Using subscription: {apimClientTool.apim_subscriptions[0].get('name')}\")\n", + "else:\n", + " utils.print_error(\"No APIM subscriptions found. Please create a subscription first.\")\n", + " api_key = None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "603223fd", + "metadata": {}, + "outputs": [], + "source": [ + "# Test each supported model via Universal LLM API\n", + "if api_key and updated_supported_models:\n", + " utils.print_info(f\"\\nTesting {len(updated_supported_models)} models via Universal LLM API...\\n\")\n", + " \n", + " test_messages = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Be concise.\"},\n", + " {\"role\": \"user\", \"content\": \"What is 2+2? Answer in one word.\"}\n", + " ]\n", + " \n", + " for model_name in updated_supported_models: # add [:3]: to test first 3 models only\n", + " utils.print_info(f\"Testing model: {model_name}\")\n", + " \n", + " payload = {\n", + " \"model\": model_name,\n", + " \"messages\": test_messages\n", + " }\n", + " \n", + " try:\n", + " response = requests.post(\n", + " chat_completions_url_models,\n", + " headers={\"api-key\": api_key},\n", + " json=payload,\n", + " timeout=60\n", + " )\n", + " \n", + " utils.print_response_code(response)\n", + " \n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " answer = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"No response\")\n", + " region = response.headers.get(\"x-ms-region\", \"unknown\")\n", + " print(f\" 💬 Response: {answer}\")\n", + " print(f\" 📍 Backend Region: {region}\")\n", + " utils.print_ok(f\"Model '{model_name}' - SUCCESS\\n\")\n", + " else:\n", + " utils.print_error(f\"Model '{model_name}' - FAILED: {response.text}\\n\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Model '{model_name}' - ERROR: {str(e)}\\n\")\n", + "else:\n", + " utils.print_warning(\"Cannot run tests - missing API key or supported models\")" + ] + }, + { + "cell_type": "markdown", + "id": "1c1f16b0", + "metadata": {}, + "source": [ + "\n", + "### 🧪 Test via Azure OpenAI API (openai/deployments/{model}/chat/completions)\n", + "\n", + "Test the deployed models using the Azure OpenAI compatible API which uses the deployment name in the URL path:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1bc65b1e", + "metadata": {}, + "outputs": [], + "source": [ + "# Discover the Azure OpenAI API endpoint\n", + "try:\n", + " apimClientTool.discover_api(\"openai\")\n", + " azure_endpoint_openai = str(apimClientTool.azure_endpoint)\n", + " utils.print_info(f\"Azure OpenAI API Base Endpoint: {azure_endpoint_openai}\")\n", + "except Exception as e:\n", + " utils.print_warning(f\"Azure OpenAI API not found in APIM: {e}\")\n", + " azure_endpoint_openai = None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0513c742", + "metadata": {}, + "outputs": [], + "source": [ + "# Test models via Azure OpenAI API format\n", + "if api_key and azure_endpoint_openai and updated_supported_models:\n", + " utils.print_info(f\"\\nTesting models via Azure OpenAI API format...\\n\")\n", + " \n", + " test_messages = [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Be concise.\"},\n", + " {\"role\": \"user\", \"content\": \"What is the capital of France? Answer in one word.\"}\n", + " ]\n", + " \n", + " for model_name in updated_supported_models: # add [:3]: to test first 3 models only\n", + " utils.print_info(f\"Testing model: {model_name}\")\n", + " \n", + " # Azure OpenAI format uses deployment name in URL path\n", + " chat_completions_url_openai = f\"{azure_endpoint_openai}openai/deployments/{model_name}/chat/completions?api-version={inference_api_version}\"\n", + " \n", + " payload = {\n", + " \"messages\": test_messages # No model field needed - it's in the URL\n", + " }\n", + " \n", + " try:\n", + " response = requests.post(\n", + " chat_completions_url_openai,\n", + " headers={\"api-key\": api_key},\n", + " json=payload,\n", + " timeout=60\n", + " )\n", + " \n", + " utils.print_response_code(response)\n", + " \n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " answer = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"No response\")\n", + " region = response.headers.get(\"x-ms-region\", \"unknown\")\n", + " print(f\" 💬 Response: {answer}\")\n", + " print(f\" 📍 Backend Region: {region}\")\n", + " utils.print_ok(f\"Model '{model_name}' - SUCCESS\\n\")\n", + " else:\n", + " utils.print_error(f\"Model '{model_name}' - FAILED: {response.text}\\n\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Model '{model_name}' - ERROR: {str(e)}\\n\")\n", + "else:\n", + " utils.print_warning(\"Cannot run Azure OpenAI API tests - missing API key, endpoint, or supported models\")" + ] + }, + { + "cell_type": "markdown", + "id": "25fdf14e", + "metadata": {}, + "source": [ + "\n", + "### 🧪 Test using Azure OpenAI Python SDK\n", + "\n", + "Test using the official Azure OpenAI Python SDK:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63338618", + "metadata": {}, + "outputs": [], + "source": [ + "from openai import AzureOpenAI\n", + "import httpx\n", + "\n", + "def strip_auth_header(request):\n", + " \"\"\"Remove Authorization header to prevent APIM JWT validation conflict.\n", + " The AzureOpenAI SDK sends both api-key and Authorization: Bearer headers,\n", + " but APIM tries to validate the Authorization header as a JWT token.\"\"\"\n", + " request.headers.pop(\"authorization\", None)\n", + "\n", + "if api_key and azure_endpoint_openai and updated_supported_models:\n", + " model_name = updated_supported_models[0] # Use first available model\n", + " utils.print_info(f\"Testing with Azure OpenAI SDK using model: {model_name}\")\n", + " \n", + " try:\n", + " client = AzureOpenAI(\n", + " azure_endpoint=azure_endpoint_openai,\n", + " api_key=api_key,\n", + " api_version=inference_api_version,\n", + " http_client=httpx.Client(\n", + " event_hooks={\"request\": [strip_auth_header]}\n", + " )\n", + " )\n", + " \n", + " response = client.chat.completions.create(\n", + " model=model_name,\n", + " messages=[\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Say 'Hello from Azure OpenAI SDK!'\"}\n", + " ]\n", + " )\n", + " \n", + " utils.print_ok(\"SDK Test Successful!\")\n", + " print(f\"💬 Response: {response.choices[0].message.content}\")\n", + " print(f\"📊 Usage: {response.usage.total_tokens} tokens\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"SDK Test Failed: {str(e)}\")\n", + "else:\n", + " utils.print_warning(\"Cannot run SDK test - missing prerequisites\")" + ] + }, + { + "cell_type": "markdown", + "id": "037d9e66", + "metadata": {}, + "source": [ + "\n", + "### 🧪 Test Streaming Response\n", + "\n", + "Test streaming responses using the Azure OpenAI SDK:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f192e6d", + "metadata": {}, + "outputs": [], + "source": [ + "from openai import AzureOpenAI\n", + "import httpx\n", + "\n", + "def strip_auth_header(request):\n", + " \"\"\"Remove Authorization header to prevent APIM JWT validation conflict.\"\"\"\n", + " request.headers.pop(\"authorization\", None)\n", + "\n", + "if api_key and azure_endpoint_openai and updated_supported_models:\n", + " model_name = updated_supported_models[0] # Use first available model\n", + " utils.print_info(f\"Testing streaming with model: {model_name}\")\n", + " \n", + " try:\n", + " client = AzureOpenAI(\n", + " azure_endpoint=azure_endpoint_openai,\n", + " api_key=api_key,\n", + " api_version=inference_api_version,\n", + " http_client=httpx.Client(\n", + " event_hooks={\"request\": [strip_auth_header]}\n", + " )\n", + " )\n", + " \n", + " start_time = time.time()\n", + " \n", + " response = client.chat.completions.with_raw_response.create(\n", + " model=model_name,\n", + " messages=[\n", + " {\"role\": \"user\", \"content\": \"Count from 1 to 10 with commas between each number.\"}\n", + " ],\n", + " stream=True\n", + " )\n", + " \n", + " print(f\"📡 x-ms-region: {response.headers.get('x-ms-region', 'unknown')}\")\n", + " print(f\"📡 x-ms-stream: {response.headers.get('x-ms-stream', 'N/A')}\")\n", + " print(\"\\n💬 Streaming response:\")\n", + " \n", + " completion = response.parse()\n", + " collected_content = []\n", + " total_chunks = 0\n", + " content_chunks = 0\n", + " first_chunk_time = None\n", + " \n", + " for chunk in completion:\n", + " total_chunks += 1\n", + " if first_chunk_time is None:\n", + " first_chunk_time = time.time()\n", + " if chunk.choices and chunk.choices[0].delta.content:\n", + " content = chunk.choices[0].delta.content\n", + " collected_content.append(content)\n", + " content_chunks += 1\n", + " print(content, end='', flush=True)\n", + " \n", + " elapsed = time.time() - start_time\n", + " ttfb = (first_chunk_time - start_time) if first_chunk_time else None\n", + " print(f\"\\n\\n✅ Stream completed in {elapsed:.2f} seconds\")\n", + " print(f\"📦 Chunks received: {total_chunks} (with content: {content_chunks})\")\n", + " if ttfb is not None:\n", + " print(f\"⚡ Time to first chunk: {ttfb:.2f} seconds\")\n", + " print(f\"📝 Full response: {''.join(collected_content)}\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Streaming Test Failed: {str(e)}\")\n", + "else:\n", + " utils.print_warning(\"Cannot run streaming test - missing prerequisites\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "6aa78350", + "metadata": {}, + "source": [ + "---\n", + "## 📊 Results Summary\n", + "---\n", + "\n", + "This notebook completed the following tasks:\n", + "\n", + "1. **Extracted** current APIM backend-pools configurations\n", + "2. **Generated** a customizable LLM backend parameter file (`.bicepparam`) supporting:\n", + " - Backend types: `ai-foundry`, `azure-openai`, `aws-bedrock`, `aws-bedrock-mantle`, `gemini-openai`, `external`\n", + " - Auth types: `managed-identity`, `aws-sigv4`, `api-key-bearer`, `api-key-header`, `none`\n", + " - `authConfig` with optional **Key Vault references** (`keyVaultSecretUri`) for API-key backends\n", + " - Per-model metadata (`sku`, `capacity`, `modelFormat`, `modelVersion`, `retirementDate`, `apiVersion`, `timeout`, `inferenceApiVersion`)\n", + " - **Model aliases** (`modelAliases`) with `priority` or `weighted` strategies\n", + " - **AWS credentials** (`awsAccessKey`, `awsSecretKey`, `awsRegion`) for Bedrock\n", + "3. **Deployed** the LLM onboarding Bicep templates\n", + "4. **Verified** deployment including the `get-available-models` policy fragment\n", + "5. **Tested** the deployed models through:\n", + " - **GET /deployments** (Microsoft Foundry integration — uses `get-available-models` policy fragment)\n", + " - Universal LLM API (`/models/chat/completions`)\n", + " - Azure OpenAI API (`/openai/deployments/{model}/chat/completions`)\n", + " - Azure OpenAI Python SDK\n", + " - Streaming responses\n", + "\n", + "### Next Steps\n", + "\n", + "- Modify the `llm_backends_config` in the initialization cell to add more backends (Azure OpenAI, Bedrock, Bedrock Mantle, Gemini, external)\n", + "- For API-key backends, set `authConfig.keyVaultSecretUri` and `key_vault_name` to source secrets from Azure Key Vault (recommended over inline `secretValue`)\n", + "- Add `model_aliases` to expose simplified client-facing names that route to one or more underlying models\n", + "- Provide `aws_access_key` / `aws_secret_key` / `aws_region` when onboarding `aws-bedrock` (SigV4) backends\n", + "- Re-run the deployment cells to update the APIM configuration\n", + "- Use the generated parameter file as a template for CI/CD pipelines\n", + "- Use the `get-available-models` policy fragment to expose available models to Microsoft Foundry and other clients\n" + ] + }, + { + "cell_type": "markdown", + "id": "b82195c0", + "metadata": {}, + "source": [ + "\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "The backends and policy fragments deployed by this notebook are intended to persist as part of your Governance Hub configuration. To remove them, delete the corresponding backend resources and policy fragments from your APIM instance via the Azure portal or CLI.\n", + "\n", + "> **Note:** Removing backends will affect any access contracts that reference those backend pools. Ensure no active contracts depend on them before cleanup." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb new file mode 100644 index 000000000..d577f94b6 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/2. citadel-universal-llm-api-all-models-tests.ipynb @@ -0,0 +1,820 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dbc10788", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🌐 Universal LLM API — All-Models Validation Tests\n", + "\n", + "## Overview\n", + "\n", + "Validate the **Universal LLM API** (`/models`) against **every model** exposed by the gateway, by provisioning an access contract with **`allowedModels = \"\"`** (no model restriction) and dynamically discovering the live model catalogue:\n", + "\n", + "- `GET /models/models` → returns the OpenAI-compatible list of models the gateway is configured to serve.\n", + "\n", + "For each discovered model the notebook automatically classifies it and runs the appropriate operation:\n", + "\n", + "| Model name pattern | Operations exercised |\n", + "|---|---|\n", + "| Contains `embedding` | `POST /models/embeddings` |\n", + "| Contains `gpt` | `POST /models/chat/completions` **and** the full Responses API trio: `POST /models/responses`, `GET /models/responses/{response_id}`, `GET /models/responses/{response_id}/input_items?limit=20` |\n", + "| Anything else | `POST /models/chat/completions` |\n", + "\n", + "The notebook follows the same overall approach as `citadel-access-contracts-tests.ipynb` (Bicep-deployed access contract → API key → operation tests → summary), but instead of a hand-picked list of models it iterates over the live catalogue returned by the Universal LLM API.\n", + "\n", + "> The Universal LLM API implements the **OpenAI v1 API spec**, which includes the Responses API (`/responses`, `/responses/{id}`, `/responses/{id}/input_items`).\n", + "\n", + "## Expected outcomes\n", + "\n", + "- One Access Contract (APIM product + subscription) deployed via Bicep with `allowedModels = \"\"`\n", + "- A dynamically generated APIM product policy (`ai-product-policy.xml`) with no model RBAC restriction and a generous capacity allocation\n", + "- Per-model operation results (chat / embeddings / responses) summarized in a final table\n", + "\n", + "## Azure Prerequisites\n", + "\n", + "- An existing Citadel Governance Hub deployment (APIM + supporting resources) with the Universal LLM API (`models`) imported and at least one configured backend pool\n", + "- Azure credentials with permissions to deploy at subscription scope" + ] + }, + { + "cell_type": "markdown", + "id": "23438479", + "metadata": {}, + "source": [ + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba248470", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bac803f", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "\n", + "# ============================================================================\n", + "# 🔧 GOVERNANCE HUB CONFIGURATION (REQUIRED)\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "# ============================================================================\n", + "# 🔧 API VERSION CONFIGURATION\n", + "# ============================================================================\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "openai_api_version = \"2024-12-01-preview\"\n", + "targetInferenceApi = \"models\" # 'models' = Universal LLM API\n", + "\n", + "# ============================================================================\n", + "# 🧠 MODEL CONFIGURATION\n", + "# ----------------------------------------------------------------------------\n", + "# This notebook intentionally does NOT pin specific model names.\n", + "# The access contract below is created with allowedModels = \"\" so that ALL\n", + "# gateway-configured models are reachable; the test loop then enumerates them\n", + "# at runtime via GET /models/models.\n", + "# ============================================================================\n", + "max_models_to_test = 0 # 0 = test every discovered model; set a positive int to cap for quick smoke tests\n", + "\n", + "# ============================================================================\n", + "# ⏱️ RESPONSES API TEST CONFIGURATION\n", + "# ----------------------------------------------------------------------------\n", + "# Delay (in seconds) between the initial POST /models/responses call and the\n", + "# subsequent GET /models/responses/{id} and GET .../input_items calls. Gives\n", + "# the backend time to materialize the response object before it is fetched.\n", + "# Set to 0 to disable the wait.\n", + "# ============================================================================\n", + "responses_get_delay_seconds = 0\n", + "\n", + "# ============================================================================\n", + "# 📜 ACCESS CONTRACT CONFIGURATION (created on the fly by this notebook)\n", + "# ============================================================================\n", + "contract_business_unit = \"Testing\"\n", + "contract_use_case_name = \"UniversalLLMAllModels\"\n", + "contract_environment = \"DEV\"\n", + "\n", + "# ============================================================================\n", + "# 🔐 KEY VAULT / 🤖 FOUNDRY (this notebook does not require either)\n", + "# ============================================================================\n", + "use_keyvault_integration = False\n", + "use_foundry_integration = False\n", + "\n", + "import shutil\n", + "\n", + "utils.print_ok(\"Notebook variables initialized!\")" + ] + }, + { + "cell_type": "markdown", + "id": "3d48508c", + "metadata": {}, + "source": [ + "### 1️⃣ Verify Azure CLI and Connected Subscription" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85d59bcc", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "156d1459", + "metadata": {}, + "source": [ + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "Discover the Universal LLM API and retrieve gateway configuration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d99584cf", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(governance_hub_resource_group)\n", + " apimClientTool.initialize()\n", + " apimClientTool.discover_api(targetInferenceApi)\n", + "\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " azure_endpoint = str(apimClientTool.azure_endpoint)\n", + "\n", + " # Get supported models from the policy fragment for cross-reference\n", + " supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_info(f\"Gateway-supported models (from set-backend-pools fragment): {supported_models}\")\n", + "\n", + " utils.print_ok(\"Testing tool initialized successfully!\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client Tool: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4fb6bc75", + "metadata": {}, + "source": [ + "### 3️⃣ Provision Access Contract (`allowedModels = \"\"`)\n", + "\n", + "Deploy an access contract via Bicep that creates an APIM product + subscription and intentionally sets **`allowedModels = \"\"`** so the `validate-model-access` fragment treats the contract as *unrestricted*. The subscription is associated solely with the Universal LLM API; because the Universal LLM API implements the OpenAI v1 spec it natively serves all the operations exercised in this notebook, including the Responses API." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b17fdc82", + "metadata": {}, + "outputs": [], + "source": [ + "bicep_dir = \"../bicep/infra/citadel-access-contracts\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "timestamp = time.strftime('%Y%m%d%H%M%S')\n", + "contract_name = f\"universal-llm-allmodels-{timestamp}\"\n", + "\n", + "# Folder structure: contracts/[businessunit-usecase]/[environment]/\n", + "folder_name = f\"{contract_business_unit.lower()}-{contract_use_case_name.lower()}\"\n", + "environment_folder = contract_environment.lower()\n", + "contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + "os.makedirs(contract_folder, exist_ok=True)\n", + "utils.print_info(f\"📁 Created folder: {contract_folder}\")\n", + "\n", + "# allowedModels = \"\" -> the validate-model-access fragment will allow ANY model\n", + "allowed_models_csv = \"\"\n", + "utils.print_info(\"Allowed models for access contract: '' (all models permitted)\")\n", + "\n", + "# Generate custom product policy with allowedModels deliberately empty\n", + "product_policy = f'''\n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "policy_file_dest = os.path.join(contract_folder, \"ai-product-policy.xml\")\n", + "with open(policy_file_dest, 'w') as f:\n", + " f.write(product_policy)\n", + "utils.print_ok(f\"📋 Custom policy file created: {policy_file_dest}\")\n", + "\n", + "params_file = os.path.join(contract_folder, \"main.bicepparam\")\n", + "\n", + "params_content = f\"\"\"using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// Universal LLM API ALL MODELS Test Contract (generated from notebook)\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " name: 'placeholder'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = false\n", + "\n", + "param useCase = {{\n", + " businessUnit: '{contract_business_unit}'\n", + " useCaseName: '{contract_use_case_name}'\n", + " environment: '{contract_environment}'\n", + "}}\n", + "\n", + "// Subscription is associated with the Universal LLM API only.\n", + "// The Universal LLM API implements OpenAI v1 spec, including /responses.\n", + "param apiNameMapping = {{\n", + " LLM: ['universal-llm-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: 'UNIVERSAL-LLM-ALLMODELS-ENDPOINT'\n", + " apiKeySecretName: 'UNIVERSAL-LLM-ALLMODELS-KEY'\n", + " policyXml: loadTextContent('ai-product-policy.xml')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'Universal LLM API all models test contract. Generated from validation notebook'\n", + "\n", + "// Azure AI Foundry Integration (disabled)\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}}\n", + "\"\"\"\n", + "\n", + "with open(params_file, 'w') as f:\n", + " f.write(params_content)\n", + "utils.print_ok(f\"✅ Parameter file created: {params_file}\")\n", + "\n", + "product_id = f\"LLM-{contract_business_unit}-{contract_use_case_name}-{contract_environment}\"\n", + "utils.print_info(f\"Deploying access contract: {contract_name} (Product: {product_id})...\")\n", + "\n", + "deployment_cmd = f\"az deployment sub create --name {contract_name} --location {location} --template-file {template_file} --parameters {params_file}\"\n", + "output = utils.run(deployment_cmd, f\"Deployment '{contract_name}' succeeded\", f\"Deployment '{contract_name}' failed\")\n", + "\n", + "if output.success:\n", + " utils.print_ok(f\"✅ Access contract deployed! Product ID: {product_id}\")\n", + "else:\n", + " utils.print_error(\"❌ Access contract deployment failed!\")\n", + "\n", + "# Re-initialize APIM client to pick up the new subscription\n", + "apimClientTool.initialize()" + ] + }, + { + "cell_type": "markdown", + "id": "8a9ca03f", + "metadata": {}, + "source": [ + "### 4️⃣ Retrieve API Key from Access Contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9be6a72d", + "metadata": {}, + "outputs": [], + "source": [ + "subscription_name = f\"{product_id}-SUB-01\"\n", + "\n", + "api_key = None\n", + "for sub in apimClientTool.apim_subscriptions:\n", + " if subscription_name.lower() in sub.get('name', '').lower():\n", + " api_key = sub.get('key')\n", + " utils.print_ok(f\"Found API key from subscription: {sub.get('name')}\")\n", + " break\n", + "\n", + "if not api_key:\n", + " raise Exception(f\"No API key found for subscription '{subscription_name}'. Ensure the access contract was deployed successfully.\")\n", + "\n", + "# Build endpoint URLs for the operations exercised by this notebook (all served by the Universal LLM API).\n", + "base_url = azure_endpoint.rstrip('/')\n", + "list_models_url = f\"{base_url}/models/models\"\n", + "chat_url = f\"{base_url}/models/chat/completions?api-version={inference_api_version}\"\n", + "embeddings_url = f\"{base_url}/models/embeddings?api-version={inference_api_version}\"\n", + "# Responses API endpoints (OpenAI v1 spec, served by the Universal LLM API)\n", + "responses_url = f\"{base_url}/models/responses\"\n", + "responses_get_url_tpl = f\"{base_url}/models/responses/{{response_id}}\"\n", + "responses_inputs_url_tpl = f\"{base_url}/models/responses/{{response_id}}/input_items?limit=20\"\n", + "\n", + "utils.print_info(f\"List models endpoint: {list_models_url}\")\n", + "utils.print_info(f\"Chat endpoint: {chat_url}\")\n", + "utils.print_info(f\"Embeddings endpoint: {embeddings_url}\")\n", + "utils.print_info(f\"Responses POST endpoint: {responses_url}\")\n", + "utils.print_info(f\"Responses GET endpoint: {responses_get_url_tpl}\")\n", + "utils.print_info(f\"Responses input items: {responses_inputs_url_tpl}\")\n", + "utils.print_ok(\"API key and endpoints configured successfully!\")" + ] + }, + { + "cell_type": "markdown", + "id": "110764ff", + "metadata": {}, + "source": [ + "### 5️⃣ Discover All Models via Universal LLM API\n", + "\n", + "Call `GET /models/models` to enumerate every model the gateway is configured to serve. Because the access contract was provisioned with `allowedModels = \"\"`, the response should reflect the **full** set of gateway-supported models (not a contract-filtered subset)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84420e9c", + "metadata": {}, + "outputs": [], + "source": [ + "discovered_models = []\n", + "\n", + "try:\n", + " response = requests.get(\n", + " list_models_url,\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " # OpenAI-compatible List Models response shape: { \"object\": \"list\", \"data\": [ { \"id\": \"...\", ... } ] }\n", + " # Fall back to `value` for resilience.\n", + " raw_items = data.get('data') or data.get('value') or []\n", + " for item in raw_items:\n", + " model_id = item.get('name') or item.get('id')\n", + " if model_id:\n", + " discovered_models.append(model_id)\n", + "\n", + " utils.print_ok(f\"Discovered {len(discovered_models)} model(s) via /models/models:\")\n", + " for m in discovered_models:\n", + " utils.print_info(f\" • {m}\")\n", + " else:\n", + " utils.print_error(f\"Error listing models: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "\n", + "# Apply optional cap for smoke testing\n", + "if max_models_to_test and max_models_to_test > 0:\n", + " discovered_models = discovered_models[:max_models_to_test]\n", + " utils.print_info(f\"max_models_to_test = {max_models_to_test}; testing only: {discovered_models}\")\n", + "\n", + "if not discovered_models:\n", + " utils.print_error(\"No models discovered – downstream tests will be skipped.\")" + ] + }, + { + "cell_type": "markdown", + "id": "76317d7b", + "metadata": {}, + "source": [ + "### 6️⃣ Classify Models and Plan Operations\n", + "\n", + "Each discovered model is classified by name into one or more operations:\n", + "\n", + "- name contains `embedding` → **embeddings**\n", + "- name contains `gpt` → **chat/completions** + **responses** (POST + GET + input_items)\n", + "- otherwise → **chat/completions**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09526936", + "metadata": {}, + "outputs": [], + "source": [ + "def classify_model(model_name: str) -> list:\n", + " \"\"\"Return the list of operations to test for a given model name.\"\"\"\n", + " name_lower = (model_name or \"\").lower()\n", + " if \"embedding\" in name_lower:\n", + " return [\"embeddings\"]\n", + " if \"gpt\" in name_lower:\n", + " return [\"chat\", \"responses\"]\n", + " return [\"chat\"]\n", + "\n", + "test_plan = [(m, classify_model(m)) for m in discovered_models]\n", + "\n", + "if test_plan:\n", + " utils.print_info(\"Planned operations per model:\")\n", + " for m, ops in test_plan:\n", + " utils.print_info(f\" • {m:40s} → {', '.join(ops)}\")\n", + "else:\n", + " utils.print_error(\"Test plan is empty – nothing to execute.\")" + ] + }, + { + "cell_type": "markdown", + "id": "a3884c0b", + "metadata": {}, + "source": [ + "### 7️⃣ Execute Per-Model Operations\n", + "\n", + "Run the planned operations against each discovered model and record the per-call result for the summary table at the end.\n", + "\n", + "For models matched as `responses`-capable (name contains `gpt`), the full Responses-API trio is exercised:\n", + "\n", + "1. `POST /models/responses` – create a response (captures the returned `id`)\n", + "2. `GET /models/responses/{response_id}` – fetch the response by id\n", + "3. `GET /models/responses/{response_id}/input_items?limit=20` – list the input items" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1e95db5", + "metadata": {}, + "outputs": [], + "source": [ + "results = [] # list of dicts: model, operation, status, ok, detail\n", + "\n", + "def record(model, operation, status, ok, detail):\n", + " results.append({\n", + " \"model\": model,\n", + " \"operation\": operation,\n", + " \"status\": status,\n", + " \"ok\": ok,\n", + " \"detail\": detail,\n", + " })\n", + "\n", + "def test_chat(model_name):\n", + " payload = {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"What is 2+2? Answer in one word.\"}\n", + " ]\n", + " }\n", + " try:\n", + " r = requests.post(chat_url, headers={'api-key': api_key}, json=payload, timeout=60)\n", + " if r.status_code == 200:\n", + " content = json.loads(r.text).get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " region = r.headers.get(\"x-ms-region\", \"N/A\")\n", + " utils.print_ok(f\" ✅ chat({model_name}) → 200 | region={region} | {content[:80]}\")\n", + " record(model_name, \"chat\", r.status_code, True, content[:120])\n", + " else:\n", + " utils.print_error(f\" ❌ chat({model_name}) → {r.status_code} | {r.text[:200]}\")\n", + " record(model_name, \"chat\", r.status_code, False, r.text[:200])\n", + " except Exception as e:\n", + " utils.print_error(f\" ❌ chat({model_name}) request failed: {e}\")\n", + " record(model_name, \"chat\", 0, False, str(e))\n", + "\n", + "def test_embeddings(model_name):\n", + " # Force float encoding (some backends default to base64) and JSON content-type so the\n", + " # response is always parseable JSON regardless of server defaults.\n", + " payload = {\n", + " \"model\": model_name,\n", + " \"input\": \"The quick brown fox jumps over the lazy dog.\",\n", + " \"encoding_format\": \"float\",\n", + " }\n", + " headers = {\n", + " 'api-key': api_key,\n", + " 'Content-Type': 'application/json',\n", + " 'Accept': 'application/json',\n", + " }\n", + " try:\n", + " r = requests.post(embeddings_url, headers=headers, json=payload, timeout=60)\n", + " except Exception as e:\n", + " utils.print_error(f\" ❌ embeddings({model_name}) request failed: {e}\")\n", + " record(model_name, \"embeddings\", 0, False, str(e))\n", + " return\n", + "\n", + " region = r.headers.get(\"x-ms-region\", \"N/A\")\n", + " content_type = r.headers.get(\"Content-Type\", \"\")\n", + "\n", + " if r.status_code != 200:\n", + " utils.print_error(f\" ❌ embeddings({model_name}) → {r.status_code} | ct={content_type} | {r.text[:200]}\")\n", + " record(model_name, \"embeddings\", r.status_code, False, r.text[:200])\n", + " return\n", + "\n", + " # Robust JSON parsing — surface body / headers when the body is not valid JSON\n", + " try:\n", + " data = r.json()\n", + " except ValueError as e:\n", + " body_preview = (r.text or \"\")[:200]\n", + " body_len = len(r.content) if r.content is not None else 0\n", + " utils.print_error(\n", + " f\" ❌ embeddings({model_name}) → 200 but body is not JSON: {e} | \"\n", + " f\"ct={content_type} | bytes={body_len} | preview={body_preview!r}\"\n", + " )\n", + " record(model_name, \"embeddings\", r.status_code, False, f\"non-json body ({content_type}, {body_len} bytes)\")\n", + " return\n", + "\n", + " items = data.get(\"data\") if isinstance(data, dict) else None\n", + " if not items:\n", + " utils.print_error(f\" ❌ embeddings({model_name}) → 200 but no 'data' field | payload={str(data)[:200]}\")\n", + " record(model_name, \"embeddings\", r.status_code, False, \"missing data field\")\n", + " return\n", + "\n", + " vec = items[0].get(\"embedding\", [])\n", + " dims = len(vec) if isinstance(vec, list) else 0\n", + " utils.print_ok(f\" ✅ embeddings({model_name}) → 200 | region={region} | dims={dims}\")\n", + " record(model_name, \"embeddings\", r.status_code, True, f\"dims={dims}\")\n", + "\n", + "def test_responses(model_name):\n", + " # Universal LLM API implements OpenAI v1 spec — including the Responses API.\n", + " # Exercises the full trio:\n", + " # 1. POST /models/responses\n", + " # 2. GET /models/responses/{response_id}\n", + " # 3. GET /models/responses/{response_id}/input_items?limit=20\n", + " payload = {\n", + " \"model\": model_name,\n", + " \"input\": \"What is 2+2? Answer in one word.\"\n", + " }\n", + "\n", + " # 1. POST /models/responses\n", + " response_id = None\n", + " try:\n", + " r = requests.post(responses_url, headers={'api-key': api_key}, json=payload, timeout=60)\n", + " if r.status_code == 200:\n", + " data = json.loads(r.text)\n", + " response_id = data.get(\"id\", \"\")\n", + " region = r.headers.get(\"x-ms-region\", \"N/A\")\n", + " utils.print_ok(f\" ✅ responses.create({model_name}) → 200 | region={region} | id={response_id}\")\n", + " record(model_name, \"responses.create\", r.status_code, True, f\"id={response_id}\")\n", + " else:\n", + " utils.print_error(f\" ❌ responses.create({model_name}) → {r.status_code} | {r.text[:200]}\")\n", + " record(model_name, \"responses.create\", r.status_code, False, r.text[:200])\n", + " return\n", + " except Exception as e:\n", + " utils.print_error(f\" ❌ responses.create({model_name}) request failed: {e}\")\n", + " record(model_name, \"responses.create\", 0, False, str(e))\n", + " return\n", + "\n", + " if not response_id:\n", + " utils.print_error(f\" ⚠️ responses({model_name}) — no response_id returned, skipping GET / input_items\")\n", + " return\n", + "\n", + " # Configurable delay to give the backend time to persist the response before\n", + " # the subsequent GETs are issued.\n", + " if responses_get_delay_seconds and responses_get_delay_seconds > 0:\n", + " utils.print_info(f\" ⏱️ Waiting {responses_get_delay_seconds}s before GET responses for id={response_id}...\")\n", + " time.sleep(responses_get_delay_seconds)\n", + "\n", + " # 2. GET /models/responses/{response_id}\n", + " get_url = responses_get_url_tpl.format(response_id=response_id)\n", + " try:\n", + " r = requests.get(get_url, headers={'api-key': api_key}, timeout=30)\n", + " if r.status_code == 200:\n", + " data = json.loads(r.text)\n", + " status = data.get(\"status\", \"unknown\")\n", + " utils.print_ok(f\" ✅ responses.get({model_name}) → 200 | status={status} | id={response_id}\")\n", + " record(model_name, \"responses.get\", r.status_code, True, f\"status={status}\")\n", + " else:\n", + " if r.status_code == 404:\n", + " utils.print_ok(f\" ✅ responses.get({model_name}) → 404 | not supported by model '{model_name}'\")\n", + " record(model_name, \"responses.get\", r.status_code, True, f\"not supported by model '{model_name}'\")\n", + " else:\n", + " utils.print_error(f\" ❌ responses.get({model_name}) → {r.status_code} | {r.text[:200]}\")\n", + " record(model_name, \"responses.get\", r.status_code, False, r.text[:200])\n", + " except Exception as e:\n", + " utils.print_error(f\" ❌ responses.get({model_name}) request failed: {e}\")\n", + " record(model_name, \"responses.get\", 0, False, str(e))\n", + "\n", + " # 3. GET /models/responses/{response_id}/input_items?limit=20\n", + " items_url = responses_inputs_url_tpl.format(response_id=response_id)\n", + " try:\n", + " r = requests.get(items_url, headers={'api-key': api_key}, timeout=30)\n", + " if r.status_code == 200:\n", + " data = json.loads(r.text)\n", + " items = data.get(\"data\") or data.get(\"value\") or []\n", + " utils.print_ok(f\" ✅ responses.input_items({model_name}) → 200 | count={len(items)}\")\n", + " record(model_name, \"responses.inputs\", r.status_code, True, f\"count={len(items)}\")\n", + " else:\n", + " if r.status_code == 404:\n", + " utils.print_ok(f\" ✅ responses.input_items({model_name}) → 404 | not supported by model '{model_name}'\")\n", + " record(model_name, \"responses.inputs\", r.status_code, True, f\"not supported by model '{model_name}'\")\n", + " else:\n", + " utils.print_error(f\" ❌ responses.input_items({model_name}) → {r.status_code} | {r.text[:200]}\")\n", + " record(model_name, \"responses.inputs\", r.status_code, True, r.text[:200])\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\" ❌ responses.input_items({model_name}) request failed: {e}\")\n", + " record(model_name, \"responses.inputs\", 0, False, str(e))\n", + "\n", + "op_dispatch = {\n", + " \"chat\": test_chat,\n", + " \"embeddings\": test_embeddings,\n", + " \"responses\": test_responses,\n", + "}\n", + "\n", + "for model_name, ops in test_plan:\n", + " utils.print_info(f\"\\n🔎 Testing model: {model_name} (ops: {', '.join(ops)})\")\n", + " for op in ops:\n", + " op_dispatch[op](model_name)" + ] + }, + { + "cell_type": "markdown", + "id": "476a0abc", + "metadata": {}, + "source": [ + "### 8️⃣ Confirm `allowedModels = \"\"` Behaviour\n", + "\n", + "Quick sanity check that an arbitrarily-named model that is *not* present in the gateway is rejected by the gateway itself (not by `validate-model-access`, which we deliberately disabled by leaving `allowedModels` empty). The expected outcome is a 4xx error from the backend resolution / routing layer, **not** a 200." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a47b4c70", + "metadata": {}, + "outputs": [], + "source": [ + "bogus_model = \"definitely-not-a-real-model-xyz\"\n", + "utils.print_info(f\"Calling chat/completions with bogus model: {bogus_model}\")\n", + "\n", + "try:\n", + " r = requests.post(\n", + " chat_url,\n", + " headers={'api-key': api_key},\n", + " json={\"model\": bogus_model, \"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + " )\n", + " if r.status_code >= 400:\n", + " utils.print_ok(f\"✅ Bogus model correctly rejected by gateway: {r.status_code}\")\n", + " try:\n", + " err = json.loads(r.text)\n", + " utils.print_info(f\" Error: {err.get('error', {}).get('message', r.text[:200])}\")\n", + " except json.JSONDecodeError:\n", + " utils.print_info(f\" Body: {r.text[:200]}\")\n", + " else:\n", + " utils.print_error(f\"❌ Unexpected success for bogus model: {r.status_code}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "15ce3351", + "metadata": {}, + "source": [ + "## 📊 Results Summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "165adf5e", + "metadata": {}, + "outputs": [], + "source": [ + "if not results:\n", + " utils.print_info(\"No results recorded.\")\n", + "else:\n", + " total = len(results)\n", + " successes = sum(1 for r in results if r['ok'])\n", + " failures = total - successes\n", + "\n", + " utils.print_ok(f\"Per-model operation results — total={total}, success={successes}, failure={failures}\")\n", + " print()\n", + " print(f\"{'Model':40s} {'Operation':20s} {'Status':8s} Result\")\n", + " print(\"-\" * 110)\n", + " for r in results:\n", + " marker = \"OK\" if r['ok'] else \"FAIL\"\n", + " print(f\"{r['model'][:40]:40s} {r['operation']:20s} {str(r['status']):8s} [{marker}] {r['detail'][:60]}\")\n", + "\n", + " print()\n", + " print(\"Aggregate by operation:\")\n", + " for op in [\"chat\", \"embeddings\", \"responses.create\", \"responses.get\", \"responses.inputs\"]:\n", + " op_results = [r for r in results if r['operation'] == op]\n", + " if op_results:\n", + " ok_count = sum(1 for r in op_results if r['ok'])\n", + " print(f\" {op:20s}: {ok_count}/{len(op_results)} succeeded\")" + ] + }, + { + "cell_type": "markdown", + "id": "27155be3", + "metadata": {}, + "source": [ + "---\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "Remove the test access contract from APIM created during this notebook session:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30cd77ed", + "metadata": {}, + "outputs": [], + "source": [ + "# Set to True to delete the access contract created in this session\n", + "cleanup_enabled = False\n", + "\n", + "if cleanup_enabled:\n", + " utils.print_info(f\"Deleting access contract product: {product_id}...\")\n", + "\n", + " prod_cmd = f\"az apim product delete --resource-group {governance_hub_resource_group} --service-name {apimClientTool.apim_resource_name} --product-id {product_id} --delete-subscriptions true --yes\"\n", + " utils.run(prod_cmd, f\"Deleted product {product_id}\", f\"Failed to delete product {product_id}\")\n", + "\n", + " utils.print_ok(\"Cleanup completed!\")\n", + "else:\n", + " utils.print_info(\"Cleanup is disabled. Set cleanup_enabled = True to remove test resources.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/3. citadel-access-contracts-tests.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/3. citadel-access-contracts-tests.ipynb new file mode 100644 index 000000000..43c30f628 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/3. citadel-access-contracts-tests.ipynb @@ -0,0 +1,1225 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4ad9ede2", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 📜 Citadel Access Contracts - Testing Center\n", + "\n", + "## Overview\n", + "\n", + "Provision and validate **multiple Access Contracts** with different configurations and targets, each\n", + "backed by a **dynamically generated APIM product policy** that enforces:\n", + "\n", + "- **Model-level RBAC** via the `allowedModels` variable (per-contract list of allowed model names)\n", + "- **Capacity allocation** via `llm-token-limit` (`tokens-per-minute`, `token-quota`, `token-quota-period`)\n", + "- **Optional Azure Key Vault integration** (resolve endpoint + API key secrets)\n", + "- **Optional Microsoft Foundry connection integration** (auto-create a Foundry project connection)\n", + "\n", + "| Access Contract | Configuration | Description | Target Runtime |\n", + "|---|---|---|---|\n", + "| Sales-Assistant | Key Vault integration only | Sales Assistant workload with secrets resolved from Key Vault. | Agents running on Azure (AKS, ACA, App Service) |\n", + "| HR-ChatAgent | Key Vault + Foundry connection integrations | HR chat agent flow with Foundry connection setup. | Foundry Agents |\n", + "| Support-Bot | Direct output (no Key Vault nor Foundry integration) | Support bot scenario using direct output without external integrations. | Custom hosted agents |\n", + "\n", + "## Expected outcomes\n", + "\n", + "- Three Access Contracts (APIM products + subscriptions) deployed via Bicep\n", + "- A per-contract APIM product policy (`ai-product-policy.xml`) generated **dynamically** from\n", + " the notebook variables (model RBAC + capacity)\n", + "- Foundry connection name(s) printed explicitly for use in downstream notebooks\n", + " (e.g. `citadel-agent-frameworks-tests.ipynb`)\n", + "- Validated request/response behaviour, throttling and token-bucket visualisation\n", + "\n", + "## Azure Prerequisites\n", + "\n", + "To take full advantage of this notebook, ensure you have the following Azure resources set up:\n", + "- An existing Citadel Governance Hub deployment (APIM + supporting resources)\n", + "- *(Optional)* An Azure Key Vault with secrets for API keys (if testing Key Vault integration)\n", + "- *(Optional)* A Microsoft Foundry account & project (if testing Foundry connection integration)\n", + "- Azure credentials with permissions to deploy at subscription scope and access the above resources\n", + "\n", + "> **Note:** This notebook assumes you have already deployed your Citadel Governance Hub. If you haven't done so, please refer to the [Citadel Access Contracts Guide](../guides/full-deployment-guide.md) before proceeding.\n" + ] + }, + { + "cell_type": "markdown", + "id": "0795546b", + "metadata": {}, + "source": [ + "\n", + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1728f849", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "env_keyvault_subscription_id = azd_get_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "env_keyvault_resource_group = azd_get_value(\"SPOKE_RESOURCE_GROUP\")\n", + "env_keyvault_name = azd_get_value(\"SPOKE_KEY_VAULT_NAME\") \n", + "\n", + "env_foundry_subscription_id = azd_get_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "env_foundry_resource_group = azd_get_value(\"SPOKE_RESOURCE_GROUP\")\n", + "env_foundry_account_name = azd_get_value(\"SPOKE_AI_FOUNDRY_ACCOUNT_NAME\")\n", + "env_foundry_project_name = azd_get_value(\"SPOKE_AI_FOUNDRY_PROJECT_NAME\")\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")\n", + "print(f\"Key Vault: {env_keyvault_name} (Resource Group: {env_keyvault_resource_group}, Subscription: {env_keyvault_subscription_id})\")\n", + "print(f\"Foundry: {env_foundry_account_name} (Project: {env_foundry_project_name}, Resource Group: {env_foundry_resource_group}, Subscription: {env_foundry_subscription_id})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44b63fcb", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "\n", + "# ============================================================================\n", + "# 🔧 GOVERNANCE HUB CONFIGURATION (REQUIRED)\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "# ============================================================================\n", + "# 🔧 API VERSION CONFIGURATION\n", + "# ============================================================================\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "openai_api_version = \"2024-12-01-preview\"\n", + "targetInferenceApi = \"models\" # 'models' = Universal LLM API | 'openai' = Azure OpenAI API\n", + "\n", + "# ============================================================================\n", + "# 🔐 KEY VAULT CONFIGURATION (optional - set use_keyvault_integration = True to enable)\n", + "# ============================================================================\n", + "use_keyvault_integration = True\n", + "keyvault_subscription_id = env_keyvault_subscription_id\n", + "keyvault_resource_group = env_keyvault_resource_group\n", + "keyvault_name = env_keyvault_name \n", + "\n", + "# ============================================================================\n", + "# 🤖 MICROSOFT FOUNDRY CONFIGURATION (optional - set use_foundry_integration = True to enable)\n", + "# ============================================================================\n", + "use_foundry_integration = True\n", + "foundry_subscription_id = env_foundry_subscription_id\n", + "foundry_resource_group = env_foundry_resource_group\n", + "foundry_account_name = env_foundry_account_name\n", + "foundry_project_name = env_foundry_project_name\n", + "\n", + "utils.print_ok(\"Notebook variables initialized!\")" + ] + }, + { + "cell_type": "markdown", + "id": "938c9b46", + "metadata": {}, + "source": [ + "\n", + "### 1️⃣ Verify Azure CLI and Connected Subscription\n", + "\n", + "Ensure Azure CLI is authenticated and connected to the correct subscription:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1071de77", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "53cf514c", + "metadata": {}, + "source": [ + "\n", + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "👉 An existing Citadel Governance Hub deployment is expected. Initialize the APIM client to interact with your deployment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68b8d986", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(\n", + " governance_hub_resource_group\n", + " )\n", + " apimClientTool.initialize()\n", + " apimClientTool.discover_api(targetInferenceApi)\n", + "\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " azure_endpoint = str(apimClientTool.azure_endpoint)\n", + " \n", + " # Get supported models from the policy fragment\n", + " supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_info(f\"Supported models in APIM policy fragment 'set-backend-pools': {supported_models}\")\n", + "\n", + " if targetInferenceApi == \"openai\":\n", + " chat_completions_url = f\"{azure_endpoint}openai/deployments/{{model_name}}/chat/completions?api-version={inference_api_version}\"\n", + " else: # models\n", + " chat_completions_url = f\"{azure_endpoint}models/chat/completions?api-version={inference_api_version}\"\n", + " utils.print_info(f\"Chat Completion Endpoint Template: {chat_completions_url}\")\n", + "\n", + " utils.print_info(f\"Using the following API: {apimClientTool.api_id}\")\n", + "\n", + " utils.print_ok(f\"Testing tool initialized successfully!\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client Tool: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e03101bd", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣ Define Access Contract Configurations\n", + "\n", + "We will create 3 different access contracts with varying default configurations:\n", + "1. **Sales-Assistant**: Key Vault integration only\n", + "2. **HR-ChatAgent**: Key Vault + Foundry connection integrations (if enabled)\n", + "3. **Support-Bot**: Direct output (no Key Vault nor Foundry integration)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "074b2bad", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the access contracts to create\n", + "# Each contract supports per-contract:\n", + "# • allowed_models: list of model names this contract is allowed to call (model-level RBAC)\n", + "# • tokens_per_minute: TPM throttle limit applied to the contract\n", + "# • token_quota / token_quota_period: long-term token budget (Hourly | Daily | Weekly | Monthly)\n", + "# These values are injected into a dynamically-generated APIM product policy\n", + "# (see next cell) so each contract gets its own scoped policy XML.\n", + "\n", + "timestamp = time.strftime('%Y%m%d%H%M%S')\n", + "\n", + "access_contracts = [\n", + " {\n", + " \"name\": f\"sales-assistant-contract-{timestamp}\",\n", + " \"business_unit\": \"Sales\",\n", + " \"use_case_name\": \"Assistant\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": True,\n", + " \"use_foundry\": False,\n", + " \"endpoint_secret\": \"SALES-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"SALES-LLM-KEY\",\n", + " \"description\": \"Sales Assistant - Key Vault only\",\n", + " # --- Model RBAC (allowed model names, comma-joined into the APIM policy)\n", + " \"allowed_models\": [\"gpt-4.1\", \"gpt-5.4-mini\", \"text-embedding-3-large\"],\n", + " # --- Capacity allocation (llm-token-limit policy)\n", + " \"tokens_per_minute\": 3000,\n", + " \"token_quota\": 100000,\n", + " \"token_quota_period\": \"Monthly\"\n", + " },\n", + " {\n", + " \"name\": f\"hr-chatagent-contract-{timestamp}\",\n", + " \"business_unit\": \"HR\",\n", + " \"use_case_name\": \"ChatAgent\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": True,\n", + " \"use_foundry\": True,\n", + " \"endpoint_secret\": \"HR-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"HR-LLM-KEY\",\n", + " \"description\": \"HR Chat Agent - Key Vault + Foundry (if enabled)\",\n", + " \"allowed_models\": [\"gpt-4.1\", \"gpt-5.4-mini\"],\n", + " \"tokens_per_minute\": 5000,\n", + " \"token_quota\": 500000,\n", + " \"token_quota_period\": \"Monthly\"\n", + " },\n", + " {\n", + " \"name\": f\"support-bot-contract-{timestamp}\",\n", + " \"business_unit\": \"Support\",\n", + " \"use_case_name\": \"Bot\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": False,\n", + " \"use_foundry\": False,\n", + " \"endpoint_secret\": \"SUPPORT-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"SUPPORT-LLM-KEY\",\n", + " \"description\": \"Support Bot - Direct output (no Key Vault nor Foundry connection integration)\",\n", + " \"allowed_models\": [\"gpt-4.1\",\"Mistral-Large-3\"],\n", + " \"tokens_per_minute\": 2000,\n", + " \"token_quota\": 50000,\n", + " \"token_quota_period\": \"Daily\"\n", + " }\n", + "]\n", + "\n", + "utils.print_info(f\"Defined {len(access_contracts)} access contracts to create:\")\n", + "for i, contract in enumerate(access_contracts, 1):\n", + " utils.print_info(f\" {i}. {contract['description']}\")\n", + " utils.print_info(f\" Product ID: LLM-{contract['business_unit']}-{contract['use_case_name']}-{contract['environment']}\")\n", + " utils.print_info(f\" Allowed models: {', '.join(contract['allowed_models'])}\")\n", + " utils.print_info(f\" Capacity: {contract['tokens_per_minute']} TPM, quota {contract['token_quota']} / {contract['token_quota_period']}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "34aea5a7", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣ Create Access Contract Parameter Files\n", + "\n", + "Generate Bicep parameter files (`.bicepparam`) for each access contract.\n", + "These files configure the APIM products, subscriptions, and optionally Key Vault secrets and Foundry connections." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0184e548", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "\n", + "bicep_dir = \"../bicep/infra/citadel-access-contracts\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "# Store generated parameter files for deployment\n", + "generated_param_files = []\n", + "\n", + "def build_product_policy_xml(contract):\n", + " \"\"\"Generate a per-contract APIM product policy XML applying:\n", + " - Model-level RBAC via the `allowedModels` variable + validate-model-access fragment\n", + " - Capacity allocation via llm-token-limit (tokens-per-minute + token-quota)\n", + " Mirrors the dynamic-policy approach used in citadel-unified-ai-api-tests.ipynb.\"\"\"\n", + " allowed_csv = \",\".join(contract[\"allowed_models\"])\n", + " return f'''\n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "\n", + "for i, contract in enumerate(access_contracts, 1):\n", + " utils.print_info(f\"\\n{'='*60}\")\n", + " utils.print_info(f\"Creating Parameter File {i}/{len(access_contracts)}: {contract['description']}\")\n", + " utils.print_info(f\"{'='*60}\")\n", + "\n", + " # Folder structure: contracts/[businessunit-usecase]/[environment]/\n", + " folder_name = f\"{contract['business_unit'].lower()}-{contract['use_case_name'].lower()}\"\n", + " environment_folder = contract['environment'].lower()\n", + " contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + " os.makedirs(contract_folder, exist_ok=True)\n", + " utils.print_info(f\"📁 Created folder: {contract_folder}\")\n", + "\n", + " # Generate per-contract policy XML (model RBAC + capacity)\n", + " policy_xml = build_product_policy_xml(contract)\n", + " policy_file_dest = os.path.join(contract_folder, \"ai-product-policy.xml\")\n", + " with open(policy_file_dest, \"w\") as f:\n", + " f.write(policy_xml)\n", + " utils.print_ok(f\"📋 Generated dynamic policy: {policy_file_dest}\")\n", + " utils.print_info(f\" • Allowed models : {', '.join(contract['allowed_models'])}\")\n", + " utils.print_info(f\" • TPM : {contract['tokens_per_minute']}\")\n", + " utils.print_info(f\" • Token quota : {contract['token_quota']} / {contract['token_quota_period']}\")\n", + "\n", + " # Path used by Bicep loadTextContent (relative to the .bicepparam file)\n", + " policy_relative_path = \"ai-product-policy.xml\"\n", + " params_file = os.path.join(contract_folder, \"main.bicepparam\")\n", + "\n", + " # Build Foundry configuration section\n", + " if contract['use_foundry']:\n", + " foundry_params = f\"\"\"\n", + "// Azure AI Foundry Integration\n", + "param useTargetFoundry = true\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '{foundry_subscription_id}'\n", + " resourceGroupName: '{foundry_resource_group}'\n", + " accountName: '{foundry_account_name}'\n", + " projectName: '{foundry_project_name}'\n", + "}}\n", + "\n", + "param foundryConfig = {{\n", + " connectionNamePrefix: ''\n", + " deploymentInPath: 'false'\n", + " isSharedToAll: false\n", + " inferenceAPIVersion: ''\n", + " deploymentAPIVersion: ''\n", + " staticModels: [\n", + " {{\n", + " name: 'gpt-4.1'\n", + " properties: {{\n", + " model: {{\n", + " name: 'gpt-4.1'\n", + " version: '2025-04-14'\n", + " format: 'OpenAI'\n", + " }}\n", + " }}\n", + " }}\n", + " {{\n", + " name: 'gpt-5.4-mini'\n", + " properties: {{\n", + " model: {{\n", + " name: 'gpt-5.4-mini'\n", + " version: '2025-04-14'\n", + " format: 'OpenAI'\n", + " }}\n", + " }}\n", + " }}\n", + " ]\n", + " listModelsEndpoint: ''\n", + " getModelEndpoint: ''\n", + " deploymentProvider: ''\n", + " customHeaders: {{}}\n", + " authConfig: {{}}\n", + "}}\n", + "\"\"\"\n", + " else:\n", + " foundry_params = \"\"\"\n", + "// Azure AI Foundry Integration (disabled)\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}\n", + "\"\"\"\n", + "\n", + " params_content = f\"\"\"using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// {contract['description']} - Generated from Notebook\n", + "// Dynamic policy attributes:\n", + "// allowedModels : {','.join(contract['allowed_models'])}\n", + "// tokens-per-minute : {contract['tokens_per_minute']}\n", + "// token-quota : {contract['token_quota']} / {contract['token_quota_period']}\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '{keyvault_subscription_id}'\n", + " resourceGroupName: '{keyvault_resource_group}'\n", + " name: '{keyvault_name}'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = {str(contract['use_keyvault']).lower()}\n", + "\n", + "param useCase = {{\n", + " businessUnit: '{contract['business_unit']}'\n", + " useCaseName: '{contract['use_case_name']}'\n", + " environment: '{contract['environment']}'\n", + "}}\n", + "\n", + "param apiNameMapping = {{\n", + " LLM: ['universal-llm-api', 'azure-openai-api', 'unified-ai-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: '{contract['endpoint_secret']}'\n", + " apiKeySecretName: '{contract['apikey_secret']}'\n", + " policyXml: loadTextContent('{policy_relative_path}')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'Access Contract created from testing notebook - {contract[\"description\"]}'\n", + "{foundry_params}\n", + "\"\"\"\n", + "\n", + " with open(params_file, \"w\") as f:\n", + " f.write(params_content)\n", + "\n", + " generated_param_files.append({\n", + " \"contract\": contract,\n", + " \"params_file\": params_file,\n", + " \"contract_folder\": contract_folder\n", + " })\n", + " utils.print_ok(f\"✅ Parameter file created: {params_file}\")\n", + "\n", + "utils.print_ok(f\"\\n📁 Created {len(generated_param_files)} parameter files ready for deployment!\")\n", + "utils.print_info(\"Each contract folder contains:\")\n", + "utils.print_info(\" • main.bicepparam - Bicep parameter file\")\n", + "utils.print_info(\" • ai-product-policy.xml - Dynamically generated APIM product policy (model RBAC + capacity)\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "7b7ad3e6", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.1 Deploy Access Contracts Using 🦾 Bicep\n", + "\n", + "Deploy each access contract using the generated parameter files.\n", + "This creates the APIM products, subscriptions, and optionally Key Vault secrets and Foundry connections in Azure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31e4ffc8", + "metadata": {}, + "outputs": [], + "source": [ + "# Store deployment results for later use\n", + "deployment_results = []\n", + "\n", + "for i, item in enumerate(generated_param_files, 1):\n", + " contract = item['contract']\n", + " params_file = item['params_file']\n", + " \n", + " utils.print_info(f\"\\n{'='*60}\")\n", + " utils.print_info(f\"Deploying Access Contract {i}/{len(generated_param_files)}: {contract['description']}\")\n", + " utils.print_info(f\"{'='*60}\")\n", + " \n", + " # Deploy the access contract\n", + " deployment_cmd = f\"az deployment sub create --name {contract['name']} --location {location} --template-file {template_file} --parameters {params_file}\"\n", + " \n", + " utils.print_info(f\"Deploying {contract['name']}...\")\n", + " output = utils.run(\n", + " deployment_cmd,\n", + " f\"Deployment '{contract['name']}' succeeded\",\n", + " f\"Deployment '{contract['name']}' failed\"\n", + " )\n", + "\n", + " if output.success:\n", + " # Deployment succeeded - try to get outputs if JSON data is available\n", + " outputs = {}\n", + " if output.json_data:\n", + " outputs = output.json_data.get('properties', {}).get('outputs', {})\n", + " \n", + " deployment_results.append({\n", + " \"contract\": contract,\n", + " \"outputs\": outputs,\n", + " \"success\": True\n", + " })\n", + " utils.print_ok(f\"✅ Access Contract {i} deployed successfully!\")\n", + " \n", + " # Show key outputs if available\n", + " if outputs:\n", + " for key, value in outputs.items():\n", + " masked_value = utils.mask_sensitive_values(value.get('value'))\n", + " utils.print_info(f\" {key}: {masked_value}\")\n", + " else:\n", + " utils.print_info(\" (No outputs returned - deployment completed)\")\n", + " else:\n", + " deployment_results.append({\n", + " \"contract\": contract,\n", + " \"outputs\": {},\n", + " \"success\": False\n", + " })\n", + " utils.print_error(f\"❌ Access Contract {i} deployment failed!\")\n", + "\n", + "# Re-initialize APIM client to pick up new subscriptions\n", + "apimClientTool.initialize()\n", + "utils.print_ok(f\"\\n🎉 Completed deploying {len([r for r in deployment_results if r['success']])} access contracts!\")" + ] + }, + { + "cell_type": "markdown", + "id": "foundryconn-md", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.2 Foundry Connection Names (for downstream notebooks)\n", + "\n", + "When `use_foundry = True` for an access contract, the deployment provisions a Microsoft Foundry\n", + "project connection that points back to the APIM gateway. **This connection name is what other\n", + "notebooks (e.g., `citadel-agent-frameworks-tests.ipynb`) use as `foundry_connection_name`.**\n", + "\n", + "**Naming convention** (see `bicep/infra/citadel-access-contracts/main.bicep`):\n", + "\n", + "```\n", + "-\n", + "```\n", + "\n", + "Where `connectionNamePrefix` defaults to `Hub---` when left\n", + "empty in `foundryConfig` (which is the default in this notebook).\n", + "\n", + "So the resulting connection names follow the pattern:\n", + "\n", + "```\n", + "Hub---- # e.g. Hub-HR-ChatAgent-DEV-LLM\n", + "```\n", + "\n", + "The next cell extracts the connection name(s) from the deployment outputs and prints them\n", + "explicitly so you can copy them into other notebooks.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "foundryconn-code", + "metadata": {}, + "outputs": [], + "source": [ + "# Extract and display the Foundry connection name(s) for each deployed access contract.\n", + "# Use these values as `foundry_connection_name` in the agent-frameworks notebook.\n", + "\n", + "foundry_connection_names = {}\n", + "\n", + "print(\"\\n\" + \"=\" * 70)\n", + "print(\"🔗 FOUNDRY CONNECTION NAMES (for downstream notebooks)\")\n", + "print(\"=\" * 70)\n", + "print(\"Naming convention: ---\")\n", + "print(\" (when foundryConfig.connectionNamePrefix is empty)\")\n", + "print(\"-\" * 70)\n", + "\n", + "for result in deployment_results:\n", + " contract = result[\"contract\"]\n", + " if not contract.get(\"use_foundry\"):\n", + " continue\n", + " if not result.get(\"success\"):\n", + " utils.print_warning(f\"⚠️ Skipping {contract['name']} (deployment failed)\")\n", + " continue\n", + "\n", + " # Prefer the value emitted by the Bicep `foundryConnections` output\n", + " outputs = result.get(\"outputs\") or {}\n", + " fc_output = outputs.get(\"foundryConnections\", {}).get(\"value\", [])\n", + " if fc_output:\n", + " for entry in fc_output:\n", + " conn_name = entry.get(\"connectionName\") if isinstance(entry, dict) else entry\n", + " if conn_name:\n", + " foundry_connection_names.setdefault(contract[\"name\"], []).append(conn_name)\n", + " else:\n", + " # Fallback: reconstruct from the naming convention\n", + " for svc_code in [\"LLM\"]:\n", + " conn_name = f\"{contract['business_unit']}-{contract['use_case_name']}-{contract['environment']}-{svc_code}\"\n", + " foundry_connection_names.setdefault(contract[\"name\"], []).append(conn_name)\n", + "\n", + " for cn in foundry_connection_names.get(contract[\"name\"], []):\n", + " utils.print_ok(f\"✅ {contract['description']}\")\n", + " print(f\" foundry_connection_name = \\\"{cn}\\\"\")\n", + "\n", + "if not foundry_connection_names:\n", + " utils.print_info(\"No Foundry-enabled contracts in this run.\")\n", + "else:\n", + " print(\"-\" * 70)\n", + " print(\"📋 Copy/paste-ready map:\")\n", + " print(json.dumps(foundry_connection_names, indent=2))\n", + "print(\"=\" * 70)\n" + ] + }, + { + "cell_type": "markdown", + "id": "63fdaca5", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣ Retrieve API Keys for Each Access Contract\n", + "\n", + "Get the subscription keys created for each access contract to use in API testing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "186ee333", + "metadata": {}, + "outputs": [], + "source": [ + "# Map contract names to their subscription keys\n", + "contract_keys = {}\n", + "\n", + "for result in deployment_results:\n", + " if not result['success']:\n", + " continue\n", + " \n", + " contract = result['contract']\n", + " product_id = f\"LLM-{contract['business_unit']}-{contract['use_case_name']}-{contract['environment']}\"\n", + " subscription_name = f\"{product_id}-SUB-01\"\n", + " \n", + " # Find the subscription key from APIM subscriptions\n", + " for sub in apimClientTool.apim_subscriptions:\n", + " if subscription_name.lower() in sub.get('name', '').lower():\n", + " contract_keys[product_id] = {\n", + " \"key\": sub.get('key'),\n", + " \"description\": contract['description'],\n", + " \"use_keyvault\": contract['use_keyvault'],\n", + " \"use_foundry\": contract['use_foundry']\n", + " }\n", + " utils.print_ok(f\"Found key for {product_id}\")\n", + " break\n", + " else:\n", + " # If not found in existing subscriptions, check outputs for direct credentials\n", + " if not contract['use_keyvault']:\n", + " endpoints = result['outputs'].get('endpoints', {}).get('value', [])\n", + " for ep in endpoints:\n", + " if ep.get('code') == 'LLM':\n", + " contract_keys[product_id] = {\n", + " \"key\": ep.get('apiKey'),\n", + " \"endpoint\": ep.get('endpoint'),\n", + " \"description\": contract['description'],\n", + " \"use_keyvault\": contract['use_keyvault'],\n", + " \"use_foundry\": contract['use_foundry']\n", + " }\n", + " utils.print_ok(f\"Found direct key for {product_id}\")\n", + " break\n", + "\n", + "utils.print_info(f\"\\nRetrieved keys for {len(contract_keys)} access contracts:\")\n", + "for product_id, info in contract_keys.items():\n", + " utils.print_info(f\" • {product_id}: {info['description']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4834b145", + "metadata": {}, + "source": [ + "\n", + "### 6️⃣ Test API Requests Across All Access Contracts\n", + "\n", + "Send test requests to each access contract and collect metrics for visualization." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "428cda0a", + "metadata": {}, + "outputs": [], + "source": [ + "model_name = \"gpt-5.4-mini\"\n", + "# if you want to dynamically select model from supported models, uncomment below\n", + "# model_name = supported_models[2] if len(supported_models) > 2 else supported_models[0]\n", + "utils.print_info(f\"Using model: {model_name}\")\n", + "\n", + "# Store results for each contract\n", + "test_results = {product_id: [] for product_id in contract_keys.keys()}\n", + "\n", + "messages = {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"What is 2+2?\"}\n", + " ]\n", + "}\n", + "\n", + "# Send a single test request to each contract\n", + "for product_id, info in contract_keys.items():\n", + " utils.print_info(f\"\\nTesting {product_id}...\")\n", + " \n", + " api_key = info.get('key')\n", + " if not api_key:\n", + " utils.print_error(f\"No API key found for {product_id}\")\n", + " continue\n", + " \n", + " try:\n", + " response = requests.post(\n", + " chat_completions_url,\n", + " headers={'api-key': api_key},\n", + " json=messages,\n", + " timeout=30\n", + " )\n", + " \n", + " utils.print_response_code(response)\n", + " \n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " utils.print_ok(f\"💬 Response: {content[:100]}...\" if len(content) > 100 else f\"💬 Response: {content}\")\n", + " utils.print_info(f\" Region: {response.headers.get('x-ms-region', 'N/A')}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:200]}\")\n", + " except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9ec5f3e2", + "metadata": {}, + "source": [ + "\n", + "### 7️⃣ Run Load Test Across All Access Contracts\n", + "\n", + "Send multiple requests to each contract over 30 seconds to test rate limiting and collect performance data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b172ebc", + "metadata": {}, + "outputs": [], + "source": [ + "import requests, json, time\n", + "from concurrent.futures import ThreadPoolExecutor\n", + "import threading\n", + "\n", + "# Run for 30 seconds per contract\n", + "test_duration = 30\n", + "all_api_runs = {product_id: [] for product_id in contract_keys.keys()}\n", + "\n", + "model_name = \"gpt-4.1\" # This is a common model that is allowed by default in the contracts, but you can change it as needed\n", + "utils.print_info(f\"Using model: {model_name}\")\n", + "\n", + "messages = {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Count from 1 to 10.\"}\n", + " ]\n", + "}\n", + "\n", + "def run_api_test(product_id, api_key, duration):\n", + " \"\"\"Run API calls for a specific contract.\"\"\"\n", + " runs = []\n", + " start_time = time.time()\n", + " run_count = 0\n", + " \n", + " while (time.time() - start_time) < duration:\n", + " run_count += 1\n", + " call_start_time = time.time()\n", + " \n", + " try:\n", + " response = requests.post(\n", + " chat_completions_url,\n", + " headers={'api-key': api_key},\n", + " json=messages,\n", + " timeout=30\n", + " )\n", + " \n", + " elapsed = time.time() - start_time\n", + " \n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " total_tokens = data.get(\"usage\", {}).get(\"total_tokens\", 0)\n", + " else:\n", + " total_tokens = 0\n", + " \n", + " runs.append((call_start_time, total_tokens, response.status_code, elapsed))\n", + " \n", + " except Exception as e:\n", + " runs.append((call_start_time, 0, 500, time.time() - start_time))\n", + " \n", + " time.sleep(0.2) # Small delay between requests\n", + " \n", + " return runs\n", + "\n", + "# Run tests for each contract sequentially\n", + "for product_id, info in contract_keys.items():\n", + " api_key = info.get('key')\n", + " if not api_key:\n", + " continue\n", + " \n", + " print(f\"\\n🕐 Testing {product_id} for {test_duration} seconds...\")\n", + " print(f\" {info['description']}\")\n", + " \n", + " runs = run_api_test(product_id, api_key, test_duration)\n", + " all_api_runs[product_id] = runs\n", + " \n", + " success = sum(1 for r in runs if r[2] == 200)\n", + " throttled = sum(1 for r in runs if r[2] == 429)\n", + " errors = sum(1 for r in runs if r[2] not in [200, 429])\n", + " \n", + " print(f\" ✅ Success: {success} | ⛔ Throttled: {throttled} | ❌ Errors: {errors}\")\n", + "\n", + "utils.print_ok(f\"\\n🏁 Load testing completed for all {len(contract_keys)} access contracts!\")" + ] + }, + { + "cell_type": "markdown", + "id": "574628e0", + "metadata": {}, + "source": [ + "\n", + "### 8️⃣ Visualize Results Across All Access Contracts\n", + "\n", + "Compare API usage, token consumption, and throttling behavior across all access contracts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bab7e230", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from matplotlib.patches import Patch\n", + "from matplotlib.lines import Line2D\n", + "import numpy as np\n", + "\n", + "# Check if we have data to plot\n", + "contracts_with_data = {k: v for k, v in all_api_runs.items() if v}\n", + "\n", + "if contracts_with_data:\n", + " # Print summary table\n", + " print(\"\\n\" + \"=\"*80)\n", + " print(\"📊 SUMMARY: Access Contracts Performance Comparison\")\n", + " print(\"=\"*80)\n", + " print(f\"{'Contract':<40} {'Calls':<8} {'Success':<10} {'Throttled':<10} {'Tokens':<10}\")\n", + " print(\"-\"*80)\n", + " \n", + " for product_id, runs in contracts_with_data.items():\n", + " success = sum(1 for r in runs if r[2] == 200)\n", + " throttled = sum(1 for r in runs if r[2] == 429)\n", + " total_tokens = sum(r[1] for r in runs)\n", + " print(f\"{product_id:<40} {len(runs):<8} {success:<10} {throttled:<10} {total_tokens:<10}\")\n", + " \n", + " print(\"=\"*80)\n", + " \n", + " num_contracts = len(contracts_with_data)\n", + " fig, axes = plt.subplots(num_contracts, 1, figsize=(14, 5 * num_contracts), squeeze=False)\n", + " \n", + " colors_map = {'success': 'tab:green', 'throttled': 'tab:red', 'error': 'tab:orange'}\n", + " \n", + " for idx, (product_id, runs) in enumerate(contracts_with_data.items()):\n", + " ax = axes[idx, 0]\n", + " \n", + " if not runs:\n", + " ax.text(0.5, 0.5, 'No data', ha='center', va='center')\n", + " ax.set_title(product_id)\n", + " continue\n", + " \n", + " # Process data\n", + " base_time = runs[0][0]\n", + " times = [r[3] for r in runs] # elapsed time\n", + " tokens = [r[1] for r in runs]\n", + " status_codes = [r[2] for r in runs]\n", + " \n", + " # Color bars based on status\n", + " colors = [\n", + " colors_map['success'] if code == 200 \n", + " else colors_map['throttled'] if code == 429 \n", + " else colors_map['error'] \n", + " for code in status_codes\n", + " ]\n", + " \n", + " # Create bar chart\n", + " ax.bar(times, tokens, color=colors, width=0.3, alpha=0.7)\n", + " \n", + " # Add throttled markers\n", + " throttled_times = [t for t, code in zip(times, status_codes) if code == 429]\n", + " if throttled_times:\n", + " max_tokens = max(tokens) if tokens else 1\n", + " ax.scatter(throttled_times, [max_tokens * 0.05] * len(throttled_times), \n", + " marker='x', s=50, color='darkred', zorder=5)\n", + " \n", + " # Calculate stats\n", + " success = sum(1 for code in status_codes if code == 200)\n", + " throttled = sum(1 for code in status_codes if code == 429)\n", + " total_tokens = sum(tokens)\n", + " \n", + " # Labels and title\n", + " info = contract_keys.get(product_id, {})\n", + " title = f\"{product_id}\\n{info.get('description', '')}\"\n", + " ax.set_title(title, fontsize=11, fontweight='bold')\n", + " ax.set_xlabel('Time (seconds)')\n", + " ax.set_ylabel('Tokens per call')\n", + " \n", + " # Add stats annotation\n", + " stats_text = f\"Total: {len(runs)} calls | Success: {success} | Throttled: {throttled} | Tokens: {total_tokens}\"\n", + " ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, fontsize=9,\n", + " verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))\n", + " \n", + " # Add shared legend\n", + " legend_items = [\n", + " Patch(facecolor='tab:green', alpha=0.7, label='Success (200)'),\n", + " Patch(facecolor='tab:red', alpha=0.7, label='Throttled (429)'),\n", + " Patch(facecolor='tab:orange', alpha=0.7, label='Error'),\n", + " Line2D([0], [0], marker='x', color='darkred', markersize=8, linestyle='None', label='Throttle point')\n", + " ]\n", + " fig.legend(handles=legend_items, loc='upper right', bbox_to_anchor=(0.98, 0.99))\n", + " \n", + " plt.tight_layout()\n", + " plt.subplots_adjust(top=0.95)\n", + " plt.show()\n", + " \n", + "else:\n", + " print('No API test data available. Run the load test first to capture data.')" + ] + }, + { + "cell_type": "markdown", + "id": "f3b3d870", + "metadata": {}, + "source": [ + "\n", + "### 9️⃣ Compare Token Bucket Behavior\n", + "\n", + "Visualize the token bucket algorithm behavior for each access contract." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4d398ae", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from matplotlib.patches import Patch\n", + "from matplotlib.lines import Line2D\n", + "\n", + "contracts_with_data = {k: v for k, v in all_api_runs.items() if v}\n", + "\n", + "if contracts_with_data:\n", + " # Token bucket parameters (default policy: 400 tokens/min)\n", + " capacity = 400\n", + " refill = capacity / 60 # tokens per second\n", + " \n", + " fig, axes = plt.subplots(len(contracts_with_data), 1, figsize=(14, 6 * len(contracts_with_data)), squeeze=False)\n", + " \n", + " for idx, (product_id, runs) in enumerate(contracts_with_data.items()):\n", + " ax1 = axes[idx, 0]\n", + " ax2 = ax1.twinx()\n", + " \n", + " # Process data for token bucket simulation\n", + " calls = [(r[3], r[1] or 0, r[2]) for r in runs] # (elapsed_time, tokens, status)\n", + " \n", + " bucket = capacity\n", + " last_time = 0.0\n", + " times, usage, status_codes, levels = [], [], [], []\n", + " \n", + " for call_time, tokens, status in calls:\n", + " # Refill bucket\n", + " bucket = min(capacity, bucket + (call_time - last_time) * refill)\n", + " levels.append(bucket)\n", + " times.append(call_time)\n", + " usage.append(tokens)\n", + " status_codes.append(status)\n", + " # Consume tokens\n", + " bucket = max(0, bucket - tokens)\n", + " last_time = call_time\n", + " \n", + " # Colors based on status\n", + " colors = ['tab:green' if code == 200 else 'tab:red' if code == 429 else 'tab:orange' for code in status_codes]\n", + " \n", + " # Plot bars for token usage\n", + " ax1.bar(times, usage, color=colors, width=0.35, alpha=0.7)\n", + " \n", + " # Plot bucket level\n", + " ax2.plot(times, levels, color='purple', linewidth=2)\n", + " ax2.axhline(capacity, color='purple', linestyle='--', alpha=0.6)\n", + " \n", + " # Mark throttled points\n", + " throttled_times = [t for t, code in zip(times, status_codes) if code == 429]\n", + " throttled_usage = [u for u, code in zip(usage, status_codes) if code == 429]\n", + " if throttled_times:\n", + " max_usage = max(usage) if usage else 0\n", + " throttled_marker_heights = [u + max_usage * 0.01 for u in throttled_usage]\n", + " ax1.scatter(throttled_times, throttled_marker_heights, marker='o', s=20, \n", + " color='darkred', edgecolors='white', linewidth=0.4, zorder=6)\n", + " \n", + " # Labels\n", + " ax1.set_xlabel('Seconds')\n", + " ax1.set_ylabel('Tokens per call')\n", + " ax2.set_ylabel('Tokens in bucket', color='purple')\n", + " ax2.tick_params(axis='y', labelcolor='purple')\n", + " \n", + " info = contract_keys.get(product_id, {})\n", + " ax1.set_title(f'Token Bucket Behavior: {product_id}\\n{info.get(\"description\", \"\")}')\n", + " \n", + " # Stats\n", + " success = sum(code == 200 for code in status_codes)\n", + " throttled = sum(code == 429 for code in status_codes)\n", + " print(f\"{product_id}: Calls: {len(status_codes)} | Success: {success} | Throttled: {throttled}\")\n", + " \n", + " # Add legend to first subplot\n", + " legend_items = [\n", + " Patch(facecolor='tab:green', alpha=0.7, label='Success (200)'),\n", + " Line2D([0], [0], color='purple', linewidth=2, label='Bucket level'),\n", + " Line2D([0], [0], color='purple', linestyle='--', label='Capacity'),\n", + " Line2D([0], [0], marker='o', color='darkred', markersize=8, linestyle='None',\n", + " markerfacecolor='darkred', markeredgecolor='white', label='Throttled (429)')\n", + " ]\n", + " axes[0, 0].legend(handles=legend_items, loc='upper right', bbox_to_anchor=(0.98, 0.85), framealpha=0.9)\n", + " \n", + " plt.tight_layout()\n", + " plt.show()\n", + "else:\n", + " print('Run the load test first to capture api_runs data.')" + ] + }, + { + "cell_type": "markdown", + "id": "477d85d7", + "metadata": {}, + "source": [ + "---\n", + "## 📊 Results Summary\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8a2695e", + "metadata": {}, + "outputs": [], + "source": [ + "# Overall summary\n", + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"📊 ACCESS CONTRACTS TEST SUMMARY\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "\n", + "utils.print_info(f\"\\n📜 Access Contracts Tested: {len(contract_keys)}\")\n", + "for product_id, info in contract_keys.items():\n", + " utils.print_info(f\" • {product_id}: {info['description']}\")\n", + "\n", + "if contracts_with_data:\n", + " total_calls = sum(len(runs) for runs in contracts_with_data.values())\n", + " total_success = sum(sum(1 for r in runs if r[2] == 200) for runs in contracts_with_data.values())\n", + " total_throttled = sum(sum(1 for r in runs if r[2] == 429) for runs in contracts_with_data.values())\n", + " total_tokens_all = sum(sum(r[1] for r in runs) for runs in contracts_with_data.values())\n", + "\n", + " utils.print_info(f\"\\n📈 Load Test Results:\")\n", + " utils.print_info(f\" Total API calls: {total_calls}\")\n", + " utils.print_ok(f\" Successful: {total_success}\")\n", + " if total_throttled > 0:\n", + " utils.print_info(f\" Throttled: {total_throttled}\")\n", + " utils.print_info(f\" Total tokens consumed: {total_tokens_all}\")\n", + "\n", + " if total_success == total_calls:\n", + " utils.print_ok(f\"\\n✅ All tests passed!\")\n", + " else:\n", + " utils.print_info(f\"\\n📊 Pass rate: {total_success}/{total_calls}\")\n", + "else:\n", + " utils.print_info(\"\\n⚠️ No load test data available. Run the load test cells first.\")" + ] + }, + { + "cell_type": "markdown", + "id": "05dc92e1", + "metadata": {}, + "source": [ + "\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "Remove the test access contracts from APIM created during this notebook session.\n", + "\n", + "> **Note:** This will not delete any created secrets in Azure Key Vault or Microsoft Foundry connections." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e0c116a", + "metadata": {}, + "outputs": [], + "source": [ + "# Set to True to delete the access contracts created in this session\n", + "cleanup_enabled = False\n", + "\n", + "if cleanup_enabled:\n", + " for result in deployment_results:\n", + " if not result['success']:\n", + " continue\n", + " \n", + " contract = result['contract']\n", + " product_id = f\"LLM-{contract['business_unit']}-{contract['use_case_name']}-{contract['environment']}\"\n", + " subscription_name = f\"{product_id}-SUB-01\"\n", + " \n", + " utils.print_info(f\"Deleting {product_id}...\")\n", + " \n", + " # Delete product and its associated subscriptions\n", + " prod_cmd = f\"az apim product delete --resource-group {governance_hub_resource_group} --service-name {apimClientTool.apim_resource_name} --product-id {product_id} --delete-subscriptions true --yes\"\n", + " utils.run(prod_cmd, f\"Deleted product {product_id}\", f\"Failed to delete product\")\n", + " \n", + " utils.print_ok(\"Cleanup completed!\")\n", + "else:\n", + " utils.print_info(\"Cleanup is disabled. Set cleanup_enabled = True to remove test resources.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/4. citadel-agent-frameworks-tests.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/4. citadel-agent-frameworks-tests.ipynb new file mode 100644 index 000000000..8f3524afc --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/4. citadel-agent-frameworks-tests.ipynb @@ -0,0 +1,1543 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4efd86dc", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🤖 Citadel Agent Frameworks - Testing Center\n", + "\n", + "## Overview\n", + "\n", + "This notebook tests the Citadel Access Contracts using different agent frameworks to simulate multi-turn conversations:\n", + "\n", + "| Access Contract | Agent Framework | Integration Type | Target Model |\n", + "|----------------|-----------------|------------------|--------------|\n", + "| **Sales-Assistant** | Microsoft Agent Framework | Azure Key Vault (endpoint + key) | gpt-4.1 |\n", + "| **HR-ChatAgent** | Microsoft Foundry Agent SDK | Foundry Project Connection | gpt-5.4-mini |\n", + "| **Support-Bot** | LangChain | Local (direct endpoint + key) | Mistral-Large-3 |\n", + "\n", + "Each agent will simulate a conversation with a user around the topic of its access contract title.\n", + "\n", + "> **Note:** This notebook assumes you have already deployed your Citadel Governance Hub. If you haven't done so, please refer to the [Citadel Access Contracts Guide](../guides/full-deployment-guide.md) before proceeding.\n", + "\n", + "## Azure Prerequisites\n", + "\n", + "- Citadel Governance Hub deployed with access contracts\n", + "- Run `citadel-access-contracts-tests.ipynb` first to create the test contracts\n", + "- Python packages: `agent-framework`, `azure-ai-projects`, `langchain`, `langchain-openai` among others (see step 4)\n", + "- Have both Azure Key Vault and Foundry target resources provisioned and accessible (permissions and network)" + ] + }, + { + "cell_type": "markdown", + "id": "ea31a224", + "metadata": {}, + "source": [ + "\n", + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b8dd421", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "env_keyvault_subscription_id = azd_get_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "env_keyvault_resource_group = azd_get_value(\"SPOKE_RESOURCE_GROUP\")\n", + "env_keyvault_name = azd_get_value(\"SPOKE_KEY_VAULT_NAME\") \n", + "\n", + "env_foundry_subscription_id = azd_get_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "env_foundry_resource_group = azd_get_value(\"SPOKE_RESOURCE_GROUP\")\n", + "env_foundry_account_name = azd_get_value(\"SPOKE_AI_FOUNDRY_ACCOUNT_NAME\")\n", + "env_foundry_project_name = azd_get_value(\"SPOKE_AI_FOUNDRY_PROJECT_NAME\")\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")\n", + "print(f\"Key Vault: {env_keyvault_name} (Resource Group: {env_keyvault_resource_group}, Subscription: {env_keyvault_subscription_id})\")\n", + "print(f\"Foundry: {env_foundry_account_name} (Project: {env_foundry_project_name}, Resource Group: {env_foundry_resource_group}, Subscription: {env_foundry_subscription_id})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1df5e42c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "\n", + "# ============================================================================\n", + "# 🔧 GOVERNANCE HUB CONFIGURATION (REQUIRED)\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "# ============================================================================\n", + "# 🔧 API VERSION CONFIGURATION\n", + "# ============================================================================\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "openai_api_version = \"2024-12-01-preview\"\n", + "targetInferenceApi = \"models\" # 'models' = Universal LLM API | 'openai' = Azure OpenAI API\n", + "\n", + "# ============================================================================\n", + "# 🔐 KEY VAULT CONFIGURATION (optional - set use_keyvault_integration = True to enable)\n", + "# ============================================================================\n", + "use_keyvault_integration = True\n", + "keyvault_subscription_id = env_keyvault_subscription_id\n", + "keyvault_resource_group = env_keyvault_resource_group\n", + "keyvault_name = env_keyvault_name\n", + "\n", + "# ============================================================================\n", + "# 🤖 MICROSOFT FOUNDRY CONFIGURATION (optional - set use_foundry_integration = True to enable)\n", + "# ============================================================================\n", + "use_foundry_integration = True\n", + "foundry_subscription_id = env_foundry_subscription_id\n", + "foundry_resource_group = env_foundry_resource_group\n", + "foundry_account_name = env_foundry_account_name\n", + "foundry_project_name = env_foundry_project_name\n", + "# Foundry connection name created by the access contract.\n", + "# Convention: Hub----\n", + "# (printed explicitly by citadel-access-contracts-tests.ipynb step 4.2)\n", + "# It can also be viewed in the Foundry portal under \"Connected Resources\" of the project details.\n", + "foundry_connection_name = \"Hub-HR-ChatAgent-DEV-LLM\"\n", + "\n", + "# ============================================================================\n", + "# 🧠 PER-AGENT MODEL CONFIGURATION\n", + "# ============================================================================\n", + "sales_model_name = \"gpt-4.1\" # Sales-Assistant (Microsoft Agent Framework)\n", + "hr_model_name = \"gpt-5.4-mini\" # HR-ChatAgent (Foundry Agent SDK)\n", + "support_model_name = \"Mistral-Large-3\" # Support-Bot (LangChain)\n", + "\n", + "# ============================================================================\n", + "# 🔁 RETRY CONFIGURATION\n", + "# ============================================================================\n", + "import asyncio, nest_asyncio\n", + "nest_asyncio.apply()\n", + "\n", + "MAX_RETRIES = 2 # Maximum number of retries per call\n", + "RETRY_DELAY_BASE = 2 # Base delay in seconds (exponential backoff: base * 2^attempt)\n", + "\n", + "agent_metrics = {\n", + " \"MS Agent Framework (Sales)\": {\"total_tokens\": 0, \"prompt_tokens\": 0, \"completion_tokens\": 0, \"calls\": 0, \"retries\": 0},\n", + " \"Foundry SDK (HR)\": {\"total_tokens\": 0, \"prompt_tokens\": 0, \"completion_tokens\": 0, \"calls\": 0, \"retries\": 0},\n", + " \"LangChain (Support)\": {\"total_tokens\": 0, \"prompt_tokens\": 0, \"completion_tokens\": 0, \"calls\": 0, \"retries\": 0},\n", + "}\n", + "\n", + "\n", + "utils.print_ok(\"Notebook variables initialized!\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "1ce85794", + "metadata": {}, + "source": [ + "\n", + "### 1️⃣ Verify Azure CLI and Connected Subscription\n", + "\n", + "Ensure Azure CLI is authenticated and connected to the correct subscription:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07425bbe", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "c567ec76", + "metadata": {}, + "source": [ + "\n", + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "👉 An existing Citadel Governance Hub deployment is expected. Initialize the APIM client to interact with your deployment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a322c0f6", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(governance_hub_resource_group)\n", + " apimClientTool.initialize()\n", + " apimClientTool.discover_api(targetInferenceApi)\n", + "\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " azure_endpoint = str(apimClientTool.azure_endpoint)\n", + " \n", + " # Get supported models\n", + " supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_info(f\"Supported models: {supported_models}\")\n", + "\n", + " if targetInferenceApi == \"openai\":\n", + " chat_completions_url = f\"{azure_endpoint}openai/deployments/{{model_name}}/chat/completions?api-version={inference_api_version}\"\n", + " else:\n", + " chat_completions_url = f\"{azure_endpoint}models/chat/completions?api-version={inference_api_version}\"\n", + " \n", + " utils.print_info(f\"Chat Completion Endpoint: {chat_completions_url}\")\n", + " utils.print_ok(f\"APIM Client initialized!\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "13f97b88", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣ Retrieve Access Contract API Keys\n", + "\n", + "Get the API keys for each access contract to test with different agent frameworks." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ee3f3c9", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the access contracts to test (matching those from citadel-access-contracts-tests.ipynb)\n", + "access_contract_configs = [\n", + " {\n", + " \"name\": \"Sales-Assistant\",\n", + " \"business_unit\": \"Sales\",\n", + " \"use_case_name\": \"Assistant\",\n", + " \"environment\": \"DEV\",\n", + " \"agent_framework\": \"Microsoft Agent Framework\",\n", + " \"endpoint_secret\": \"SALES-LLM-ENDPOINT\", # Key Vault secret names from access contract\n", + " \"apikey_secret\": \"SALES-LLM-KEY\",\n", + " \"description\": \"Sales Assistant - Helps with sales inquiries, product information, and pricing\"\n", + " },\n", + " {\n", + " \"name\": \"HR-ChatAgent\",\n", + " \"business_unit\": \"HR\",\n", + " \"use_case_name\": \"ChatAgent\",\n", + " \"environment\": \"DEV\",\n", + " \"agent_framework\": \"Foundry SDK\",\n", + " \"foundry_connection\": foundry_connection_name, # Foundry connection created by access contract\n", + " \"description\": \"HR Chat Agent - Assists with HR policies, benefits, and employee questions\"\n", + " },\n", + " {\n", + " \"name\": \"Support-Bot\",\n", + " \"business_unit\": \"Support\",\n", + " \"use_case_name\": \"Bot\",\n", + " \"environment\": \"DEV\",\n", + " \"agent_framework\": \"LangChain\",\n", + " \"description\": \"Support Bot - Provides technical support and troubleshooting assistance\"\n", + " }\n", + "]\n", + "\n", + "# Map contracts to their subscription keys\n", + "contract_keys = {}\n", + "\n", + "for config in access_contract_configs:\n", + " product_id = f\"LLM-{config['business_unit']}-{config['use_case_name']}-{config['environment']}\"\n", + " subscription_name = f\"{product_id}-SUB-01\"\n", + " \n", + " for sub in apimClientTool.apim_subscriptions:\n", + " if subscription_name.lower() in sub.get('name', '').lower():\n", + " contract_keys[config['name']] = {\n", + " \"key\": sub.get('key'),\n", + " \"product_id\": product_id,\n", + " \"agent_framework\": config['agent_framework'],\n", + " \"description\": config['description'],\n", + " \"config\": config # Store full config for later use\n", + " }\n", + " utils.print_ok(f\"Found key for {config['name']} ({config['agent_framework']})\")\n", + " break\n", + " else:\n", + " utils.print_warning(f\"No key found for {config['name']} - run citadel-access-contracts-tests.ipynb first\")\n", + "\n", + "utils.print_info(f\"\\nRetrieved keys for {len(contract_keys)} access contracts\")" + ] + }, + { + "cell_type": "markdown", + "id": "c57dac01", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣ Install Required Agent Frameworks\n", + "\n", + "Install the necessary Python packages for each agent framework.\n", + "\n", + "> **Tip:** Set `install_agent_frameworks = False` at the top of the cell below to skip installation on repeated runs (verification still executes so you can confirm package availability)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "013dc271", + "metadata": {}, + "outputs": [], + "source": [ + "# Install and verify agent framework packages in the ACTIVE notebook kernel\n", + "# ---------------------------------------------------------------------------\n", + "# Toggle: set to False to skip package installation on repeated notebook runs\n", + "# (e.g. once the kernel already has the required packages installed). The\n", + "# verification step at the end always runs so you still see the active versions.\n", + "install_agent_frameworks = False\n", + "# ---------------------------------------------------------------------------\n", + "\n", + "import sys\n", + "import importlib\n", + "import importlib.metadata as md\n", + "\n", + "utils.print_info(f\"Python executable in use: {sys.executable}\")\n", + "\n", + "if install_agent_frameworks:\n", + " utils.print_info(\"Upgrading packaging tools in this kernel...\")\n", + " %pip install -q --upgrade pip setuptools wheel\n", + "\n", + " # Avoid the agent-framework meta package here. It pulls optional wheels that\n", + " # currently overwrite agent_framework/__init__.py in this environment.\n", + " utils.print_info(\"Installing Microsoft Agent Framework core packages...\")\n", + " %pip install -q -U \"agent-framework-core>=1.2.0\" \"agent-framework-openai>=1.2.0\"\n", + "\n", + " packages = [\n", + " \"azure-ai-projects>=2.0.0b4\", # Microsoft Foundry Agent SDK (PromptAgentDefinition support)\n", + " \"openai>=1.0.0\", # OpenAI-compatible client used by Foundry sample pattern\n", + " \"langchain>=0.2.0\", # LangChain\n", + " \"langchain-openai>=0.1.0\", # LangChain OpenAI integration\n", + " \"azure-identity\", # Azure authentication\n", + " \"azure-keyvault-secrets\", # Azure Key Vault for Sales-Assistant\n", + " \"nest-asyncio\", # For running async in Jupyter\n", + " \"pywin32\", # For Windows users to avoid event loop issues\n", + " \"matplotlib>=3.9\" # For visualizations (>=3.9 required for matplotlib.backends.registry)\n", + " ]\n", + "\n", + " utils.print_info(\"Installing/upgrading remaining dependencies...\")\n", + " for package in packages:\n", + " try:\n", + " %pip install -q --upgrade {package}\n", + " utils.print_ok(f\"Installed/updated {package}\")\n", + " except Exception as e:\n", + " utils.print_warning(f\"Could not install {package}: {e}\")\n", + "\n", + " # Validate semantic-conventions compatibility; fallback pin only if required attrs are missing\n", + " try:\n", + " semconv_module = importlib.import_module(\"opentelemetry.semconv_ai\")\n", + " SpanAttributes = getattr(semconv_module, \"SpanAttributes\")\n", + " has_llm_system = hasattr(SpanAttributes, \"LLM_SYSTEM\")\n", + " has_llm_request_max_tokens = hasattr(SpanAttributes, \"LLM_REQUEST_MAX_TOKENS\")\n", + " utils.print_info(\n", + " f\"Pre-check SpanAttributes compatibility: LLM_SYSTEM={has_llm_system}, \"\n", + " f\"LLM_REQUEST_MAX_TOKENS={has_llm_request_max_tokens}\"\n", + " )\n", + "\n", + " if not (has_llm_system and has_llm_request_max_tokens):\n", + " utils.print_warning(\n", + " \"Detected incompatible opentelemetry semantic conventions for current agent-framework build. \"\n", + " \"Applying fallback pin: opentelemetry-semantic-conventions-ai==0.4.13\"\n", + " )\n", + " %pip install -q --upgrade --force-reinstall \"opentelemetry-semantic-conventions-ai==0.4.13\"\n", + " utils.print_ok(\"Pinned opentelemetry-semantic-conventions-ai==0.4.13\")\n", + " except Exception as e:\n", + " utils.print_warning(f\"Could not pre-check SpanAttributes compatibility: {e}\")\n", + "else:\n", + " utils.print_info(\"Skipping agent framework installation (install_agent_frameworks=False). Verifying existing packages only.\")\n", + "\n", + "# Verify versions and runtime availability from the same interpreter\n", + "utils.print_info(\"\\nVerifying installed package versions in current kernel...\")\n", + "for pkg in [\n", + " \"agent-framework-core\",\n", + " \"agent-framework-openai\",\n", + " \"opentelemetry-semantic-conventions-ai\",\n", + " \"azure-ai-projects\",\n", + " \"azure-identity\",\n", + " \"openai\",\n", + " \"langchain\",\n", + " \"langchain-openai\",\n", + " \"matplotlib\",\n", + "]:\n", + " try:\n", + " version = md.version(pkg)\n", + " utils.print_ok(f\"{pkg}: {version}\")\n", + " except Exception:\n", + " utils.print_warning(f\"{pkg}: not found\")\n", + "\n", + "try:\n", + " agent_framework_module = importlib.import_module(\"agent_framework\")\n", + " utils.print_ok(f\"agent_framework.__version__ available: {hasattr(agent_framework_module, '__version__')}\")\n", + "except Exception as e:\n", + " utils.print_warning(f\"Could not validate agent_framework package import: {e}\")\n", + "\n", + "try:\n", + " semconv_module = importlib.import_module(\"opentelemetry.semconv_ai\")\n", + " SpanAttributes = getattr(semconv_module, \"SpanAttributes\")\n", + " utils.print_ok(f\"SpanAttributes.LLM_SYSTEM available: {hasattr(SpanAttributes, 'LLM_SYSTEM')}\")\n", + " utils.print_ok(f\"SpanAttributes.LLM_REQUEST_MAX_TOKENS available: {hasattr(SpanAttributes, 'LLM_REQUEST_MAX_TOKENS')}\")\n", + "except Exception as e:\n", + " utils.print_warning(f\"Could not validate opentelemetry.semconv_ai SpanAttributes: {e}\")\n", + "\n", + "try:\n", + " models_module = importlib.import_module(\"azure.ai.projects.models\")\n", + " PromptAgentDefinition = getattr(models_module, \"PromptAgentDefinition\")\n", + " utils.print_ok(f\"PromptAgentDefinition is available: {PromptAgentDefinition}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"PromptAgentDefinition is NOT available in active kernel: {e}\")\n", + " utils.print_info(\"If this fails, restart the kernel, then re-run cells 1 to 4 before running Foundry cells.\")\n", + "\n", + "utils.print_ok(\"Package installation and verification complete!\")" + ] + }, + { + "cell_type": "markdown", + "id": "527925b5", + "metadata": {}, + "source": [ + "---\n", + "## 🧠 Agent Framework Implementations\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "dae6712f", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣ Microsoft Agent Framework (Sales-Assistant)\n", + "\n", + "Create a Sales Assistant agent using **Microsoft Agent Framework**.\n", + "This agent retrieves its endpoint and API key from **Azure Key Vault** based on the access contract parameters.\n", + "\n", + "> **Key Vault Secrets:**\n", + "> - `SALES-LLM-ENDPOINT`: The LLM endpoint URL\n", + "> - `SALES-LLM-KEY`: The API key for authentication" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55a1f0a2", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.identity import DefaultAzureCredential\n", + "from azure.keyvault.secrets import SecretClient\n", + "from datetime import datetime\n", + "\n", + "class MSAgentFrameworkSalesAgent:\n", + " \"\"\"Sales Assistant Agent using Microsoft Agent Framework with Azure Key Vault integration.\n", + " \n", + " Uses OpenAIChatClient.as_agent configured with custom headers to work with \n", + " Azure API Management gateways that expect the 'api-key' header.\n", + " \"\"\"\n", + " \n", + " def __init__(self, keyvault_name: str, endpoint_secret_name: str, apikey_secret_name: str, model_name: str):\n", + " self.keyvault_name = keyvault_name\n", + " self.endpoint_secret_name = endpoint_secret_name\n", + " self.apikey_secret_name = apikey_secret_name\n", + " self.model_name = model_name\n", + " self.conversation_history = []\n", + " self.total_tokens = 0\n", + " self.prompt_tokens = 0\n", + " self.completion_tokens = 0\n", + " self.calls = 0\n", + " self.retries = 0\n", + " self.agent = None\n", + " self.session = None # AgentSession for multi-turn conversation context\n", + " \n", + " # Retrieve endpoint and API key from Azure Key Vault\n", + " self._init_from_keyvault()\n", + " \n", + " # System prompt for Sales Assistant\n", + " self.system_prompt = f\"\"\"You are a Sales Assistant AI agent. Your role is to:\n", + "- Answer questions about products and services\n", + "- Provide pricing information and discounts\n", + "- Help customers find the right solutions for their needs\n", + "- Handle sales inquiries professionally and helpfully\n", + "\n", + "Be concise, professional, and always try to guide customers toward a purchase decision.\n", + "Current date: {datetime.now().strftime(\"%Y-%m-%d\")}\"\"\"\n", + " \n", + " # Initialize Microsoft Agent Framework agent\n", + " self._init_agent()\n", + " \n", + " def _init_from_keyvault(self):\n", + " \"\"\"Retrieve endpoint and API key from Azure Key Vault.\"\"\"\n", + " try:\n", + " credential = DefaultAzureCredential()\n", + " keyvault_uri = f\"https://{self.keyvault_name}.vault.azure.net\"\n", + " client = SecretClient(vault_url=keyvault_uri, credential=credential)\n", + " \n", + " # Get endpoint from Key Vault\n", + " endpoint_secret = client.get_secret(self.endpoint_secret_name)\n", + " self.endpoint = endpoint_secret.value\n", + " utils.print_ok(f\"Retrieved endpoint: ({self.endpoint}) from Key Vault secret: {self.endpoint_secret_name}\")\n", + " \n", + " # Get API key from Key Vault\n", + " apikey_secret = client.get_secret(self.apikey_secret_name)\n", + " self.api_key = apikey_secret.value\n", + " utils.print_ok(f\"Retrieved API key ({self.api_key[:4]}****) from Key Vault secret: {self.apikey_secret_name}\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Failed to retrieve secrets from Key Vault: {e}\")\n", + " raise\n", + " \n", + " def _init_agent(self):\n", + " \"\"\"Initialize the Microsoft Agent Framework agent using OpenAIChatClient.as_agent.\n", + " \n", + " Uses OpenAIChatClient with custom default_headers to send 'api-key' header\n", + " that Azure API Management expects, while using standard OpenAI URL patterns.\n", + " \"\"\"\n", + " try:\n", + " # Compatibility shim for agent-framework + opentelemetry-semconv-ai drift\n", + " from opentelemetry.semconv_ai import SpanAttributes\n", + "\n", + " compatibility_mappings = {\n", + " \"LLM_SYSTEM\": [\"GEN_AI_SYSTEM\"],\n", + " \"LLM_REQUEST_MAX_TOKENS\": [\"LLM_MAX_NEW_TOKENS\", \"GEN_AI_MAX_TOKENS\"],\n", + " \"LLM_REQUEST_TEMPERATURE\": [\"LLM_TEMPERATURE\", \"GEN_AI_TEMPERATURE\"],\n", + " \"LLM_REQUEST_TOP_P\": [\"LLM_TOP_P\", \"GEN_AI_TOP_P\"],\n", + " \"LLM_REQUEST_MODEL\": [\"LLM_MODEL_NAME\", \"GEN_AI_REQUEST_MODEL\"],\n", + " \"LLM_RESPONSE_MODEL\": [\"LLM_MODEL_NAME\", \"GEN_AI_RESPONSE_MODEL\"],\n", + " \"LLM_TOKEN_TYPE\": [\"GEN_AI_TOKEN_TYPE\"],\n", + " }\n", + "\n", + " for legacy_attr, candidates in compatibility_mappings.items():\n", + " if not hasattr(SpanAttributes, legacy_attr):\n", + " mapped = False\n", + " for candidate in candidates:\n", + " if hasattr(SpanAttributes, candidate):\n", + " setattr(SpanAttributes, legacy_attr, getattr(SpanAttributes, candidate))\n", + " mapped = True\n", + " break\n", + " if not mapped:\n", + " fallback_key = legacy_attr.lower().replace(\"_\", \".\")\n", + " setattr(SpanAttributes, legacy_attr, fallback_key)\n", + "\n", + " utils.print_info(\n", + " \"SpanAttributes compatibility:\"\n", + " f\" LLM_SYSTEM={hasattr(SpanAttributes, 'LLM_SYSTEM')},\"\n", + " f\" LLM_REQUEST_MAX_TOKENS={hasattr(SpanAttributes, 'LLM_REQUEST_MAX_TOKENS')},\"\n", + " f\" LLM_REQUEST_TEMPERATURE={hasattr(SpanAttributes, 'LLM_REQUEST_TEMPERATURE')},\"\n", + " f\" LLM_REQUEST_TOP_P={hasattr(SpanAttributes, 'LLM_REQUEST_TOP_P')},\"\n", + " f\" LLM_REQUEST_MODEL={hasattr(SpanAttributes, 'LLM_REQUEST_MODEL')}\"\n", + " )\n", + " \n", + " from agent_framework._sessions import AgentSession\n", + " from agent_framework.openai import OpenAIChatClient\n", + " \n", + " # Create OpenAI Chat Client with custom headers for APIM authentication\n", + " chat_client = OpenAIChatClient(\n", + " base_url=self.endpoint,\n", + " api_key=\"placeholder\",\n", + " model=self.model_name,\n", + " default_headers={\n", + " \"api-key\": self.api_key\n", + " }\n", + " )\n", + " \n", + " # Latest API shape in installed pre-release: create agent via chat client\n", + " self.agent = chat_client.as_agent(\n", + " name=\"SalesAssistant\",\n", + " instructions=self.system_prompt,\n", + " )\n", + " \n", + " # Create a persistent session for multi-turn conversations\n", + " self.session = AgentSession()\n", + " \n", + " utils.print_ok(\"Microsoft Agent Framework agent initialized successfully (using OpenAIChatClient.as_agent + AgentSession)\")\n", + " \n", + " except ImportError as e:\n", + " error_msg = (\n", + " f\"❌ Failed to import Microsoft Agent Framework: {e}\\n\"\n", + " \" → Restart the kernel and re-run cells 1 to 5\\n\"\n", + " \" → If you reinstall packages, use agent-framework-core and agent-framework-openai only\\n\"\n", + " \" → Avoid the agent-framework meta package in this notebook environment\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " raise RuntimeError(error_msg) from e\n", + " except Exception as e:\n", + " error_msg = (\n", + " f\"❌ Failed to initialize Microsoft Agent Framework agent: {e}\\n\"\n", + " \" → Verify the endpoint from Key Vault is correct and accessible\\n\"\n", + " \" → Verify the API key from Key Vault is valid\\n\"\n", + " \" → Check that the APIM gateway is running and the subscription is active\\n\"\n", + " f\" → Endpoint: {self.endpoint}\\n\"\n", + " f\" → Model: {self.model_name}\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " raise RuntimeError(error_msg) from e\n", + " \n", + " async def chat_async(self, user_message: str) -> str:\n", + " \"\"\"Send a message and get a response using Microsoft Agent Framework with retry logic.\"\"\"\n", + " self.conversation_history.append({\"role\": \"user\", \"content\": user_message})\n", + " \n", + " last_error = None\n", + " for attempt in range(MAX_RETRIES + 1):\n", + " try:\n", + " # Use Microsoft Agent Framework with persistent session for conversation context\n", + " result = await self.agent.run(user_message, session=self.session)\n", + " content = result.text if hasattr(result, 'text') else str(result)\n", + " \n", + " # Track metrics (Microsoft Agent Framework may not expose token counts directly)\n", + " estimated_tokens = len(content.split()) * 1.3 # rough estimate\n", + " self.completion_tokens += int(estimated_tokens)\n", + " self.prompt_tokens += len(user_message.split())\n", + " self.total_tokens = self.prompt_tokens + self.completion_tokens\n", + " self.calls += 1\n", + " \n", + " if attempt > 0:\n", + " utils.print_info(f\" ✅ Succeeded after {attempt} retry(ies)\")\n", + " \n", + " self.conversation_history.append({\"role\": \"assistant\", \"content\": content})\n", + " return content\n", + " \n", + " except Exception as e:\n", + " last_error = e\n", + " if attempt < MAX_RETRIES:\n", + " self.retries += 1\n", + " delay = RETRY_DELAY_BASE * (2 ** attempt)\n", + " utils.print_warning(f\" ⚠️ Attempt {attempt + 1}/{MAX_RETRIES + 1} failed: {e}\")\n", + " utils.print_info(f\" ⏳ Retrying in {delay}s...\")\n", + " await asyncio.sleep(delay)\n", + " else:\n", + " error_msg = (\n", + " f\"❌ Microsoft Agent Framework call failed after {MAX_RETRIES + 1} attempts.\\n\"\n", + " f\" Last error: {last_error}\\n\"\n", + " f\" → Check that the APIM gateway is healthy and not throttling requests\\n\"\n", + " f\" → Verify the Sales-Assistant access contract rate limits\\n\"\n", + " f\" → Ensure the model '{self.model_name}' is available in the backend pool\\n\"\n", + " f\" → Endpoint: {self.endpoint}\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " self.conversation_history.append({\"role\": \"assistant\", \"content\": f\"[ERROR] {error_msg}\"})\n", + " return f\"[ERROR] {error_msg}\"\n", + " \n", + " def chat(self, user_message: str) -> str:\n", + " \"\"\"Synchronous wrapper for chat_async.\"\"\"\n", + " return asyncio.get_event_loop().run_until_complete(self.chat_async(user_message))\n", + " \n", + " def get_metrics(self) -> dict:\n", + " \"\"\"Return token usage metrics including retries.\"\"\"\n", + " return {\n", + " \"total_tokens\": self.total_tokens,\n", + " \"prompt_tokens\": self.prompt_tokens,\n", + " \"completion_tokens\": self.completion_tokens,\n", + " \"calls\": self.calls,\n", + " \"retries\": self.retries\n", + " }\n", + "\n", + "utils.print_ok(\"Microsoft Agent Framework Sales Agent class defined (using OpenAIChatClient.as_agent + AgentSession)!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e03c8bfc", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the Microsoft Agent Framework Sales Agent with Key Vault integration\n", + "if \"Sales-Assistant\" in contract_keys:\n", + " config = contract_keys[\"Sales-Assistant\"][\"config\"]\n", + " \n", + " utils.print_info(\"🤖 Starting Microsoft Agent Framework Sales Agent conversation...\")\n", + " utils.print_info(\"=\"*60)\n", + " utils.print_info(f\"📌 Access Contract: {config['description']}\")\n", + " utils.print_info(f\"📌 Model: {sales_model_name}\")\n", + " utils.print_info(f\"📦 Retrieving credentials from Azure Key Vault: {keyvault_name}\")\n", + " \n", + " try:\n", + " sales_agent = MSAgentFrameworkSalesAgent(\n", + " keyvault_name=keyvault_name,\n", + " endpoint_secret_name=config.get(\"endpoint_secret\", \"SALES-LLM-ENDPOINT\"),\n", + " apikey_secret_name=config.get(\"apikey_secret\", \"SALES-LLM-KEY\"),\n", + " model_name=sales_model_name\n", + " )\n", + " \n", + " # Simulate a sales conversation\n", + " sales_conversation = [\n", + " \"Hi, I'm looking for an enterprise AI solution for my company.\",\n", + " \"What features do you offer for data analytics?\",\n", + " \"How much does the enterprise plan cost?\",\n", + " \"Do you offer any discounts for annual subscriptions?\",\n", + " \"Can you help me set up a demo?\"\n", + " ]\n", + " \n", + " for i, user_msg in enumerate(sales_conversation, 1):\n", + " print(f\"\\n👤 User ({i}/{len(sales_conversation)}): {user_msg}\")\n", + " response = sales_agent.chat(user_msg)\n", + " print(f\"🤖 Sales Agent: {response[:300]}{'...' if len(response) > 300 else ''}\")\n", + " time.sleep(1) # Rate limiting\n", + " \n", + " # Store metrics\n", + " agent_metrics[\"MS Agent Framework (Sales)\"] = sales_agent.get_metrics()\n", + " utils.print_ok(f\"\\n📊 Sales Agent Metrics: {agent_metrics['MS Agent Framework (Sales)']}\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Failed to initialize Sales Agent: {e}\")\n", + " utils.print_info(\"Make sure Key Vault secrets are configured and you have access permissions.\")\n", + "else:\n", + " utils.print_warning(\"Sales-Assistant contract not found. Skipping Microsoft Agent Framework test.\")" + ] + }, + { + "cell_type": "markdown", + "id": "07f97a9b", + "metadata": {}, + "source": [ + "\n", + "### 6️⃣ Microsoft Foundry Agent SDK (HR-ChatAgent)\n", + "\n", + "Create an HR Chat Agent using the **Microsoft Foundry Agent SDK** with the official **Prompt Agent** pattern.\n", + "\n", + "> **Foundry Integration (based on official sample):**\n", + "> - Uses `PromptAgentDefinition` to create a versioned agent\n", + "> - Uses `conversations` API for multi-turn dialogue\n", + "> - Uses `responses` API to get agent responses\n", + "> - Deployment pattern: `connection_name/model_name` routes through Citadel\n", + "> - Authenticates using Azure DefaultAzureCredential\n", + ">\n", + "> **Reference:** [sample_agent_basic.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4305b9a", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.identity import DefaultAzureCredential\n", + "from datetime import datetime\n", + "\n", + "class FoundryHRAgent:\n", + " \"\"\"HR Chat Agent using Microsoft Foundry Agent SDK with Prompt Agent pattern.\n", + " \n", + " Uses the Azure AI Projects SDK following the official sample pattern:\n", + " - Creates a versioned Prompt Agent using PromptAgentDefinition\n", + " - Uses conversations API for multi-turn dialogue\n", + " - Uses responses API for agent responses\n", + " - Uses get_openai_client() as context manager per the official SDK pattern\n", + " \n", + " Based on: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/samples/agents/sample_agent_basic.py\n", + " \"\"\"\n", + " \n", + " def __init__(self, foundry_account_name: str, foundry_project_name: str, \n", + " connection_name: str, model_name: str):\n", + " self.foundry_account_name = foundry_account_name\n", + " self.foundry_project_name = foundry_project_name\n", + " self.connection_name = connection_name\n", + " self.model_name = model_name\n", + " \n", + " # Deployment name pattern: connection_name/model_name\n", + " # This routes through the Foundry connection to Citadel governance hub\n", + " self.deployment_name = f\"{connection_name}/{model_name}\"\n", + " \n", + " # Metrics tracking\n", + " self.total_tokens = 0\n", + " self.prompt_tokens = 0\n", + " self.completion_tokens = 0\n", + " self.calls = 0\n", + " self.retries = 0\n", + " \n", + " # SDK clients and agent references\n", + " self.credential = None\n", + " self.project_client = None\n", + " self.agent = None\n", + " self.conversation_id = None # Track conversation ID for multi-turn\n", + " \n", + " # System prompt for HR Assistant\n", + " self.system_prompt = f\"\"\"You are an HR Chat Agent AI assistant. Your role is to:\n", + "- Answer questions about company HR policies\n", + "- Explain employee benefits and compensation packages\n", + "- Help with onboarding and offboarding procedures\n", + "- Provide guidance on workplace conduct and compliance\n", + "- Assist with leave requests and time-off policies\n", + "\n", + "Be empathetic, professional, and ensure you protect employee privacy.\n", + "Always recommend consulting with HR directly for sensitive matters.\n", + "Current date: {datetime.now().strftime(\"%Y-%m-%d\")}\"\"\"\n", + " \n", + " # Initialize Foundry client and create agent\n", + " self._init_foundry_client()\n", + " \n", + " def _init_foundry_client(self):\n", + " \"\"\"Initialize Azure AI Foundry project client and create Prompt Agent.\"\"\"\n", + " try:\n", + " from azure.ai.projects import AIProjectClient\n", + " from azure.ai.projects.models import PromptAgentDefinition\n", + " \n", + " # Build the endpoint for the Foundry project\n", + " # Format: https://{account}.services.ai.azure.com/api/projects/{project}\n", + " endpoint = f\"https://{self.foundry_account_name}.services.ai.azure.com/api/projects/{self.foundry_project_name}\"\n", + " \n", + " utils.print_info(f\"Connecting to Foundry endpoint: {endpoint}\")\n", + " \n", + " # Create credential and project client\n", + " self.credential = DefaultAzureCredential()\n", + " self.project_client = AIProjectClient(\n", + " endpoint=endpoint,\n", + " credential=self.credential\n", + " )\n", + " utils.print_ok(f\"Foundry project client initialized for: {self.foundry_project_name}\")\n", + " \n", + " # Create a versioned Prompt Agent using the official pattern\n", + " self.agent = self.project_client.agents.create_version(\n", + " agent_name=\"HR-ChatAgent\",\n", + " definition=PromptAgentDefinition(\n", + " model=self.deployment_name, # connection_name/model_name routes through Citadel\n", + " instructions=self.system_prompt,\n", + " ),\n", + " )\n", + " utils.print_ok(f\"Agent created (id: {self.agent.id}, name: {self.agent.name}, version: {self.agent.version})\")\n", + " utils.print_info(f\"Using deployment: {self.deployment_name}\")\n", + " \n", + " except ImportError as e:\n", + " error_msg = (\n", + " f\"❌ Import error - SDK version may not support Agents API: {e}\\n\"\n", + " \" → Fix: install from this kernel with: %pip install --upgrade \\\"azure-ai-projects>=2.0.0b4\\\"\\n\"\n", + " \" → Then restart kernel and re-run setup cells to refresh imports\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " raise RuntimeError(error_msg) from e\n", + " except Exception as e:\n", + " error_msg = (\n", + " f\"❌ Failed to initialize Foundry agent: {e}\\n\"\n", + " f\" → Verify the Foundry account name: {self.foundry_account_name}\\n\"\n", + " f\" → Verify the Foundry project name: {self.foundry_project_name}\\n\"\n", + " f\" → Ensure the Foundry connection '{self.connection_name}' was created by the access contract\\n\"\n", + " f\" → Check that DefaultAzureCredential has access to the Foundry project\\n\"\n", + " f\" → Run 'az login' and ensure the correct subscription is selected\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " raise RuntimeError(error_msg) from e\n", + " \n", + " def chat(self, user_message: str) -> str:\n", + " \"\"\"Send a message and get a response using Foundry Agents SDK with retry logic.\n", + " \n", + " Uses the conversations and responses API pattern from the official sample.\n", + " Uses get_openai_client() as a context manager per the SDK pattern.\n", + " \"\"\"\n", + " last_error = None\n", + " for attempt in range(MAX_RETRIES + 1):\n", + " try:\n", + " return self._chat_with_agent(user_message)\n", + " except Exception as e:\n", + " last_error = e\n", + " if attempt < MAX_RETRIES:\n", + " self.retries += 1\n", + " delay = RETRY_DELAY_BASE * (2 ** attempt)\n", + " utils.print_warning(f\" ⚠️ Attempt {attempt + 1}/{MAX_RETRIES + 1} failed: {e}\")\n", + " utils.print_info(f\" ⏳ Retrying in {delay}s...\")\n", + " time.sleep(delay)\n", + " else:\n", + " error_msg = (\n", + " f\"❌ Foundry Agent call failed after {MAX_RETRIES + 1} attempts.\\n\"\n", + " f\" Last error: {last_error}\\n\"\n", + " f\" → Check that the Foundry project '{self.foundry_project_name}' is accessible\\n\"\n", + " f\" → Verify the connection '{self.connection_name}' routes correctly to Citadel\\n\"\n", + " f\" → Ensure the model '{self.model_name}' is available via the connection\\n\"\n", + " f\" → Check the HR-ChatAgent access contract rate limits and quotas\\n\"\n", + " f\" → Deployment: {self.deployment_name}\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " return f\"[ERROR] {error_msg}\"\n", + " \n", + " def _chat_with_agent(self, user_message: str) -> str:\n", + " \"\"\"Chat using the Prompt Agent with conversations and responses API.\n", + " \n", + " Uses get_openai_client() as context manager, matching the official SDK sample.\n", + " \"\"\"\n", + " with self.project_client.get_openai_client() as openai_client:\n", + " if self.conversation_id is None:\n", + " # Create a new conversation with the first user message\n", + " conversation = openai_client.conversations.create(\n", + " items=[{\"type\": \"message\", \"role\": \"user\", \"content\": user_message}],\n", + " )\n", + " self.conversation_id = conversation.id\n", + " utils.print_info(f\"Created conversation (id: {self.conversation_id})\")\n", + " else:\n", + " # Add user message to existing conversation\n", + " openai_client.conversations.items.create(\n", + " conversation_id=self.conversation_id,\n", + " items=[{\"type\": \"message\", \"role\": \"user\", \"content\": user_message}],\n", + " )\n", + " \n", + " # Get response from the agent (official sample uses 'agent_reference')\n", + " response = openai_client.responses.create(\n", + " conversation=self.conversation_id,\n", + " extra_body={\"agent_reference\": {\"name\": self.agent.name, \"type\": \"agent_reference\"}},\n", + " )\n", + " \n", + " content = response.output_text if hasattr(response, 'output_text') else str(response)\n", + " \n", + " # Track metrics - estimate tokens since responses API may not return usage\n", + " estimated_prompt_tokens = len(user_message.split())\n", + " estimated_completion_tokens = int(len(content.split()) * 1.3)\n", + " self.prompt_tokens += estimated_prompt_tokens\n", + " self.completion_tokens += estimated_completion_tokens\n", + " self.total_tokens = self.prompt_tokens + self.completion_tokens\n", + " self.calls += 1\n", + " \n", + " return content\n", + " \n", + " def get_metrics(self) -> dict:\n", + " \"\"\"Return token usage metrics including retries.\"\"\"\n", + " return {\n", + " \"total_tokens\": self.total_tokens,\n", + " \"prompt_tokens\": self.prompt_tokens,\n", + " \"completion_tokens\": self.completion_tokens,\n", + " \"calls\": self.calls,\n", + " \"retries\": self.retries\n", + " }\n", + " \n", + " def close(self, delete_version: bool = False):\n", + " \"\"\"Clean up resources.\n", + " \n", + " Args:\n", + " delete_version: If True, deletes the agent version from Foundry. Default is False.\n", + " \"\"\"\n", + " if delete_version:\n", + " try:\n", + " if self.agent and self.project_client:\n", + " self.project_client.agents.delete_version(\n", + " agent_name=self.agent.name,\n", + " agent_version=self.agent.version\n", + " )\n", + " utils.print_ok(f\"Agent version deleted (name: {self.agent.name}, version: {self.agent.version})\")\n", + " except Exception as e:\n", + " utils.print_warning(f\"Failed to delete agent version: {e}\")\n", + " else:\n", + " if self.agent:\n", + " utils.print_info(f\"Agent version retained (name: {self.agent.name}, version: {self.agent.version}). Pass delete_version=True to remove it.\")\n", + "\n", + "utils.print_ok(\"Foundry HR Agent class defined (aligned with sample Prompt Agent pattern)!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f936a34", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the Foundry SDK HR Agent with project connection\n", + "if \"HR-ChatAgent\" in contract_keys:\n", + " config = contract_keys[\"HR-ChatAgent\"][\"config\"]\n", + " \n", + " utils.print_info(\"🏢 Starting Microsoft Foundry SDK HR Agent conversation...\")\n", + " utils.print_info(\"=\"*60)\n", + " utils.print_info(f\"📦 Connecting to Foundry project: {foundry_project_name}\")\n", + " utils.print_info(f\"🔗 Using connection: {config.get('foundry_connection', foundry_connection_name)}\")\n", + " utils.print_info(f\"📌 Access Contract: {config['description']}\")\n", + " utils.print_info(f\"📌 Model: {hr_model_name}\")\n", + " \n", + " try:\n", + " hr_agent = FoundryHRAgent(\n", + " foundry_account_name=foundry_account_name,\n", + " foundry_project_name=foundry_project_name,\n", + " connection_name=config.get(\"foundry_connection\", foundry_connection_name),\n", + " model_name=hr_model_name\n", + " )\n", + " \n", + " # Simulate an HR conversation\n", + " hr_conversation = [\n", + " \"Hello, I'm a new employee. Can you tell me about the benefits package?\",\n", + " \"What is the vacation policy? How many days do I get?\",\n", + " \"How do I request time off for a medical appointment?\",\n", + " \"What's the process for reporting workplace issues?\",\n", + " \"Thank you for your help! One more question - when is the next open enrollment period?\"\n", + " ]\n", + " \n", + " for i, user_msg in enumerate(hr_conversation, 1):\n", + " print(f\"\\n👤 User ({i}/{len(hr_conversation)}): {user_msg}\")\n", + " response = hr_agent.chat(user_msg)\n", + " print(f\"🤖 HR Agent: {response[:300]}{'...' if len(response) > 300 else ''}\")\n", + " time.sleep(1) # Rate limiting\n", + " \n", + " # Store metrics\n", + " agent_metrics[\"Foundry SDK (HR)\"] = hr_agent.get_metrics()\n", + " utils.print_ok(f\"\\n📊 HR Agent Metrics: {agent_metrics['Foundry SDK (HR)']}\")\n", + " \n", + " # Clean up (set delete_version=True to remove the agent version from Foundry)\n", + " hr_agent.close(delete_version=False)\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Failed to initialize HR Agent: {e}\")\n", + " utils.print_info(\"Make sure the Foundry connection is created by the access contract deployment.\")\n", + "else:\n", + " utils.print_warning(\"HR-ChatAgent contract not found. Skipping Foundry SDK test.\")" + ] + }, + { + "cell_type": "markdown", + "id": "5324b7be", + "metadata": {}, + "source": [ + "\n", + "### 7️⃣ LangChain Agent (Support-Bot)\n", + "\n", + "Create a Support Bot agent using the **LangChain** framework.\n", + "This is a local agent that uses variables for the LLM endpoint, key, and model name.\n", + "\n", + "> **Local Configuration:**\n", + "> - Endpoint: Retrieved from APIM client\n", + "> - API Key: Retrieved from APIM subscription\n", + "> - Model: Specified in request body" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6aaa2a62", + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "\n", + "class LangChainSupportAgent:\n", + " \"\"\"Support Bot Agent using LangChain framework with local configuration.\"\"\"\n", + " \n", + " def __init__(self, llm_endpoint: str, api_key: str, model_name: str):\n", + " \"\"\"\n", + " Initialize LangChain Support Agent with direct endpoint configuration.\n", + " \n", + " Args:\n", + " llm_endpoint: The LLM API endpoint URL\n", + " api_key: The API key for authentication\n", + " model_name: The model name to include in request body\n", + " \"\"\"\n", + " self.llm_endpoint = llm_endpoint\n", + " self.api_key = api_key\n", + " self.model_name = model_name\n", + " self.messages = []\n", + " self.total_tokens = 0\n", + " self.prompt_tokens = 0\n", + " self.completion_tokens = 0\n", + " self.calls = 0\n", + " self.retries = 0\n", + " \n", + " # System prompt for Support Bot\n", + " self.system_prompt = f\"\"\"You are a Technical Support Bot AI assistant. Your role is to:\n", + "- Help users troubleshoot technical issues\n", + "- Provide step-by-step guidance for common problems\n", + "- Explain technical concepts in simple terms\n", + "- Escalate complex issues to human support when needed\n", + "- Log and track support tickets\n", + "\n", + "Be patient, thorough, and always verify that the user's issue is resolved.\n", + "Ask clarifying questions when needed to diagnose problems accurately.\n", + "Current date: {datetime.now().strftime(\"%Y-%m-%d\")}\"\"\"\n", + " \n", + " self.messages.append({\"role\": \"system\", \"content\": self.system_prompt})\n", + " \n", + " utils.print_ok(f\"LangChain Support Agent initialized\")\n", + " utils.print_info(f\"Endpoint: {self.llm_endpoint}\")\n", + " utils.print_info(f\"Model: {self.model_name}\")\n", + " \n", + " def chat(self, user_message: str) -> str:\n", + " \"\"\"\n", + " Send a message and get a response using LangChain patterns with retry logic.\n", + " Uses direct API calls with endpoint, key, and model in body.\n", + " \"\"\"\n", + " self.messages.append({\"role\": \"user\", \"content\": user_message})\n", + " \n", + " # LangChain-style payload with model in body\n", + " payload = {\n", + " \"model\": self.model_name, # Model name in body as per LangChain pattern\n", + " \"messages\": self.messages,\n", + " \"max_tokens\": 500,\n", + " \"temperature\": 0.7\n", + " }\n", + " \n", + " last_error = None\n", + " for attempt in range(MAX_RETRIES + 1):\n", + " try:\n", + " # Direct API call using configured endpoint and key\n", + " response = requests.post(\n", + " self.llm_endpoint,\n", + " headers={\n", + " \"api-key\": self.api_key,\n", + " \"Content-Type\": \"application/json\"\n", + " },\n", + " json=payload,\n", + " timeout=60\n", + " )\n", + " \n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " \n", + " # Track token usage\n", + " usage = data.get(\"usage\", {})\n", + " self.total_tokens += usage.get(\"total_tokens\", 0)\n", + " self.prompt_tokens += usage.get(\"prompt_tokens\", 0)\n", + " self.completion_tokens += usage.get(\"completion_tokens\", 0)\n", + " self.calls += 1\n", + " \n", + " if attempt > 0:\n", + " utils.print_info(f\" ✅ Succeeded after {attempt} retry(ies)\")\n", + " \n", + " self.messages.append({\"role\": \"assistant\", \"content\": content})\n", + " return content\n", + " elif response.status_code == 429:\n", + " # Rate limited — retry with backoff\n", + " retry_after = int(response.headers.get(\"Retry-After\", RETRY_DELAY_BASE * (2 ** attempt)))\n", + " raise Exception(f\"Rate limited (429). Retry-After: {retry_after}s. Body: {response.text[:200]}\")\n", + " else:\n", + " raise Exception(f\"HTTP {response.status_code}: {response.text[:200]}\")\n", + " \n", + " except Exception as e:\n", + " last_error = e\n", + " if attempt < MAX_RETRIES:\n", + " self.retries += 1\n", + " delay = RETRY_DELAY_BASE * (2 ** attempt)\n", + " utils.print_warning(f\" ⚠️ Attempt {attempt + 1}/{MAX_RETRIES + 1} failed: {e}\")\n", + " utils.print_info(f\" ⏳ Retrying in {delay}s...\")\n", + " time.sleep(delay)\n", + " else:\n", + " error_msg = (\n", + " f\"❌ LangChain Support Agent call failed after {MAX_RETRIES + 1} attempts.\\n\"\n", + " f\" Last error: {last_error}\\n\"\n", + " f\" → Check that the APIM gateway is healthy and not throttling\\n\"\n", + " f\" → Verify the Support-Bot access contract rate limits and quotas\\n\"\n", + " f\" → Ensure the model '{self.model_name}' is available in the backend pool\\n\"\n", + " f\" → Endpoint: {self.llm_endpoint}\"\n", + " )\n", + " utils.print_error(error_msg)\n", + " # Remove the user message that failed so conversation stays consistent\n", + " self.messages.pop()\n", + " return f\"[ERROR] {error_msg}\"\n", + " \n", + " def get_metrics(self) -> dict:\n", + " \"\"\"Return token usage metrics including retries.\"\"\"\n", + " return {\n", + " \"total_tokens\": self.total_tokens,\n", + " \"prompt_tokens\": self.prompt_tokens,\n", + " \"completion_tokens\": self.completion_tokens,\n", + " \"calls\": self.calls,\n", + " \"retries\": self.retries\n", + " }\n", + "\n", + "utils.print_ok(\"LangChain Support Agent class defined!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d92df338", + "metadata": {}, + "outputs": [], + "source": [ + "# Test the LangChain Support Agent with local configuration\n", + "if \"Support-Bot\" in contract_keys:\n", + " support_key = contract_keys[\"Support-Bot\"][\"key\"]\n", + " \n", + " utils.print_info(\"🦜 Starting LangChain Support Agent conversation...\")\n", + " utils.print_info(\"=\"*60)\n", + " utils.print_info(f\"📦 Using local configuration with APIM endpoint\")\n", + " utils.print_info(f\"📌 Access Contract: {contract_keys['Support-Bot']['config']['description']}\")\n", + " utils.print_info(f\"📌 Model: {support_model_name}\")\n", + " \n", + " try:\n", + " support_agent = LangChainSupportAgent(\n", + " llm_endpoint=chat_completions_url, # Local endpoint variable\n", + " api_key=support_key, # Local API key variable\n", + " model_name=support_model_name # Model name in body\n", + " )\n", + " \n", + " # Simulate a support conversation\n", + " support_conversation = [\n", + " \"Hi, I'm having trouble connecting to the VPN. It keeps timing out.\",\n", + " \"I'm using Windows 11 and the company VPN client version 3.2.\",\n", + " \"I've tried restarting but the issue persists. What else can I try?\",\n", + " \"The network adapter shows it's connected but no internet access through VPN.\",\n", + " \"That worked! The DNS settings were incorrect. Thank you for your help!\"\n", + " ]\n", + " \n", + " for i, user_msg in enumerate(support_conversation, 1):\n", + " print(f\"\\n👤 User ({i}/{len(support_conversation)}): {user_msg}\")\n", + " response = support_agent.chat(user_msg)\n", + " print(f\"🤖 Support Agent: {response[:300]}{'...' if len(response) > 300 else ''}\")\n", + " time.sleep(1) # Rate limiting\n", + " \n", + " # Store metrics\n", + " agent_metrics[\"LangChain (Support)\"] = support_agent.get_metrics()\n", + " utils.print_ok(f\"\\n📊 Support Agent Metrics: {agent_metrics['LangChain (Support)']}\")\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"Failed to initialize Support Agent: {e}\")\n", + "else:\n", + " utils.print_warning(\"Support-Bot contract not found. Skipping LangChain test.\")" + ] + }, + { + "cell_type": "markdown", + "id": "024b7d8b", + "metadata": {}, + "source": [ + "---\n", + "## 📊 Agent Performance Comparison\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "7e060de2", + "metadata": {}, + "source": [ + "\n", + "### 8️⃣ Display Agent Statistics and Comparison\n", + "\n", + "Visualize token consumption across all agent frameworks using pie charts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "504d204f", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "# Filter agents with data\n", + "active_agents = {k: v for k, v in agent_metrics.items() if v.get('total_tokens', 0) > 0}\n", + "\n", + "if active_agents:\n", + " # Print summary table\n", + " print(\"\\n\" + \"=\"*90)\n", + " print(\"📊 AGENT FRAMEWORKS PERFORMANCE SUMMARY\")\n", + " print(\"=\"*90)\n", + " print(f\"{'Agent Framework':<30} {'Calls':<8} {'Retries':<10} {'Prompt':<12} {'Completion':<12} {'Total':<12}\")\n", + " print(\"-\"*90)\n", + " \n", + " total_all_tokens = 0\n", + " total_all_retries = 0\n", + " for agent_name, metrics in active_agents.items():\n", + " retries = metrics.get('retries', 0)\n", + " print(f\"{agent_name:<30} {metrics['calls']:<8} {retries:<10} {metrics['prompt_tokens']:<12} {metrics['completion_tokens']:<12} {metrics['total_tokens']:<12}\")\n", + " total_all_tokens += metrics['total_tokens']\n", + " total_all_retries += retries\n", + " \n", + " print(\"-\"*90)\n", + " print(f\"{'TOTAL':<30} {'':<8} {total_all_retries:<10} {'':<12} {'':<12} {total_all_tokens:<12}\")\n", + " print(\"=\"*90)\n", + " \n", + " # Create visualization\n", + " fig, axes = plt.subplots(1, 4, figsize=(20, 5))\n", + " \n", + " # Color scheme\n", + " colors = ['#2E86AB', '#A23B72', '#F18F01']\n", + " \n", + " # 1. Total Tokens Pie Chart\n", + " labels = list(active_agents.keys())\n", + " total_tokens = [m['total_tokens'] for m in active_agents.values()]\n", + " \n", + " axes[0].pie(total_tokens, labels=labels, autopct='%1.1f%%', colors=colors[:len(labels)], \n", + " startangle=90, explode=[0.02]*len(labels))\n", + " axes[0].set_title('Total Tokens by Agent', fontsize=12, fontweight='bold')\n", + " \n", + " # 2. Prompt vs Completion Tokens (Stacked Bar)\n", + " x = np.arange(len(labels))\n", + " prompt_tokens = [m['prompt_tokens'] for m in active_agents.values()]\n", + " completion_tokens = [m['completion_tokens'] for m in active_agents.values()]\n", + " \n", + " axes[1].bar(x, prompt_tokens, label='Prompt Tokens', color='#2E86AB', alpha=0.8)\n", + " axes[1].bar(x, completion_tokens, bottom=prompt_tokens, label='Completion Tokens', color='#F18F01', alpha=0.8)\n", + " axes[1].set_xticks(x)\n", + " axes[1].set_xticklabels([l.split('(')[0].strip() for l in labels], rotation=15, ha='right')\n", + " axes[1].set_ylabel('Tokens')\n", + " axes[1].set_title('Token Distribution by Agent', fontsize=12, fontweight='bold')\n", + " axes[1].legend()\n", + " \n", + " # 3. API Calls vs Retries (Grouped Bar)\n", + " calls = [m['calls'] for m in active_agents.values()]\n", + " retries = [m.get('retries', 0) for m in active_agents.values()]\n", + " \n", + " bar_width = 0.35\n", + " axes[2].bar(x - bar_width/2, calls, bar_width, label='Successful Calls', color='#2E86AB', alpha=0.8)\n", + " axes[2].bar(x + bar_width/2, retries, bar_width, label='Retries', color='#E84855', alpha=0.8)\n", + " axes[2].set_xticks(x)\n", + " axes[2].set_xticklabels([l.split('(')[0].strip() for l in labels], rotation=15, ha='right')\n", + " axes[2].set_ylabel('Count')\n", + " axes[2].set_title('Calls vs Retries by Agent', fontsize=12, fontweight='bold')\n", + " axes[2].legend()\n", + " \n", + " # Add value labels on bars\n", + " for i, (call, retry) in enumerate(zip(calls, retries)):\n", + " axes[2].text(i - bar_width/2, call + 0.1, str(call), ha='center', va='bottom', fontweight='bold', fontsize=9)\n", + " if retry > 0:\n", + " axes[2].text(i + bar_width/2, retry + 0.1, str(retry), ha='center', va='bottom', fontweight='bold', fontsize=9, color='#E84855')\n", + " \n", + " # 4. Retry Rate (% of total attempts that were retries)\n", + " retry_rates = []\n", + " for m in active_agents.values():\n", + " total_attempts = m['calls'] + m.get('retries', 0)\n", + " rate = (m.get('retries', 0) / total_attempts * 100) if total_attempts > 0 else 0\n", + " retry_rates.append(rate)\n", + " \n", + " bar_colors = ['#4CAF50' if r == 0 else '#FF9800' if r < 30 else '#E84855' for r in retry_rates]\n", + " bars = axes[3].bar(x, retry_rates, color=bar_colors, alpha=0.8)\n", + " axes[3].set_xticks(x)\n", + " axes[3].set_xticklabels([l.split('(')[0].strip() for l in labels], rotation=15, ha='right')\n", + " axes[3].set_ylabel('Retry Rate (%)')\n", + " axes[3].set_title('Retry Rate by Agent', fontsize=12, fontweight='bold')\n", + " axes[3].set_ylim(0, max(max(retry_rates) * 1.3, 10))\n", + " \n", + " # Add value labels\n", + " for bar, rate in zip(bars, retry_rates):\n", + " axes[3].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3, \n", + " f'{rate:.1f}%', ha='center', va='bottom', fontweight='bold', fontsize=9)\n", + " \n", + " plt.tight_layout()\n", + " plt.savefig('agent_metrics_comparison.png', dpi=150, bbox_inches='tight')\n", + " plt.show()\n", + " \n", + " utils.print_ok(\"Visualization saved to 'agent_metrics_comparison.png'\")\n", + "else:\n", + " utils.print_warning(\"No agent metrics available. Run the agent tests first.\")" + ] + }, + { + "cell_type": "markdown", + "id": "15bd8e3e", + "metadata": {}, + "source": [ + "\n", + "### 9️⃣ Token Efficiency Analysis\n", + "\n", + "Analyze the token efficiency (completion tokens per call) for each agent." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b33f0d4e", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "active_agents = {k: v for k, v in agent_metrics.items() if v.get('total_tokens', 0) > 0}\n", + "\n", + "if active_agents:\n", + " # Calculate efficiency metrics\n", + " efficiency_data = []\n", + " for agent_name, metrics in active_agents.items():\n", + " calls = metrics['calls']\n", + " retries = metrics.get('retries', 0)\n", + " if calls > 0:\n", + " avg_total = metrics['total_tokens'] / calls\n", + " avg_prompt = metrics['prompt_tokens'] / calls\n", + " avg_completion = metrics['completion_tokens'] / calls\n", + " efficiency = metrics['completion_tokens'] / metrics['total_tokens'] * 100 if metrics['total_tokens'] > 0 else 0\n", + " total_attempts = calls + retries\n", + " reliability = (calls / total_attempts * 100) if total_attempts > 0 else 100.0\n", + " \n", + " efficiency_data.append({\n", + " 'name': agent_name,\n", + " 'avg_total': avg_total,\n", + " 'avg_prompt': avg_prompt,\n", + " 'avg_completion': avg_completion,\n", + " 'efficiency': efficiency,\n", + " 'retries': retries,\n", + " 'reliability': reliability\n", + " })\n", + " \n", + " # Print efficiency table\n", + " print(\"\\n\" + \"=\"*95)\n", + " print(\"📈 TOKEN EFFICIENCY & RELIABILITY ANALYSIS\")\n", + " print(\"=\"*95)\n", + " print(f\"{'Agent Framework':<30} {'Avg Total':<12} {'Avg Prompt':<12} {'Avg Compl.':<12} {'Efficiency':<12} {'Retries':<10} {'Reliability':<12}\")\n", + " print(\"-\"*95)\n", + " \n", + " for data in efficiency_data:\n", + " print(f\"{data['name']:<30} {data['avg_total']:<12.1f} {data['avg_prompt']:<12.1f} {data['avg_completion']:<12.1f} {data['efficiency']:<11.1f}% {data['retries']:<10} {data['reliability']:<11.1f}%\")\n", + " \n", + " print(\"=\"*95)\n", + " print(\"\\n💡 Efficiency = (Completion Tokens / Total Tokens) × 100\")\n", + " print(\" Higher efficiency means more output for less input (context)\")\n", + " print(\"🔄 Reliability = (Successful Calls / Total Attempts) × 100\")\n", + " print(\" Higher reliability means fewer retries needed\")\n", + " \n", + " # Create side-by-side visualization: Efficiency vs Reliability\n", + " fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))\n", + " \n", + " labels = [d['name'].split('(')[0].strip() for d in efficiency_data]\n", + " efficiencies = [d['efficiency'] for d in efficiency_data]\n", + " reliabilities = [d['reliability'] for d in efficiency_data]\n", + " retries_list = [d['retries'] for d in efficiency_data]\n", + " colors = ['#2E86AB', '#A23B72', '#F18F01']\n", + " \n", + " # Left: Token Efficiency\n", + " bars1 = ax1.barh(labels, efficiencies, color=colors[:len(labels)], alpha=0.8)\n", + " for bar, eff in zip(bars1, efficiencies):\n", + " ax1.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2, \n", + " f'{eff:.1f}%', va='center', fontweight='bold')\n", + " ax1.set_xlabel('Efficiency (%)')\n", + " ax1.set_title('Token Efficiency by Agent', fontsize=14, fontweight='bold')\n", + " ax1.set_xlim(0, max(efficiencies) * 1.2)\n", + " \n", + " # Right: Reliability (with retry count annotation)\n", + " reliability_colors = ['#4CAF50' if r == 100 else '#FF9800' if r > 80 else '#E84855' for r in reliabilities]\n", + " bars2 = ax2.barh(labels, reliabilities, color=reliability_colors, alpha=0.8)\n", + " for bar, rel, retries in zip(bars2, reliabilities, retries_list):\n", + " label = f'{rel:.1f}%'\n", + " if retries > 0:\n", + " label += f' ({retries} retries)'\n", + " ax2.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2, \n", + " label, va='center', fontweight='bold')\n", + " ax2.set_xlabel('Reliability (%)')\n", + " ax2.set_title('Call Reliability by Agent', fontsize=14, fontweight='bold')\n", + " ax2.set_xlim(0, 120)\n", + " ax2.axvline(x=100, color='#4CAF50', linestyle='--', alpha=0.3, label='Perfect reliability')\n", + " ax2.legend(loc='lower right')\n", + " \n", + " plt.tight_layout()\n", + " plt.show()\n", + " \n", + "else:\n", + " utils.print_warning(\"No agent metrics available for efficiency analysis.\")" + ] + }, + { + "cell_type": "markdown", + "id": "cb748120", + "metadata": {}, + "source": [ + "---\n", + "## 📊 Results Summary\n", + "---\n", + "\n", + "This notebook tested the following access contracts with different agent frameworks:\n", + "\n", + "| Access Contract | Agent Framework | Use Case | Integration | Target Model |\n", + "|----------------|-----------------|----------|-------------|--------------|\n", + "| Sales-Assistant | Microsoft Agent Framework | Sales inquiries & pricing | Azure Key Vault (endpoint + key) | gpt-4.1 |\n", + "| HR-ChatAgent | Microsoft Foundry Agent SDK | HR policies & benefits | Foundry Project Connection | gpt-4.1 |\n", + "| Support-Bot | LangChain | Technical support | Local (direct endpoint + key) | phi-4 |\n", + "\n", + "Each agent simulated a multi-turn conversation relevant to its business domain.\n", + "The metrics collected help compare token consumption across different frameworks.\n", + "\n", + "**Integration Patterns:**\n", + "- **Microsoft Agent Framework (Sales)**: Retrieves endpoint and API key from Azure Key Vault secrets, uses `AzureOpenAIChatClient.as_agent()`\n", + "- **Foundry SDK (HR)**: Uses `connection_name/model_name` deployment pattern via Foundry project\n", + "- **LangChain (Support)**: Local configuration with endpoint, key, and model in request body" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ce719c6", + "metadata": {}, + "outputs": [], + "source": [ + "# Final summary\n", + "utils.print_info(\"\\n\" + \"=\"*60)\n", + "utils.print_info(\"🎉 AGENT FRAMEWORKS TESTING COMPLETE!\")\n", + "utils.print_info(\"=\"*60)\n", + "\n", + "total_retries = 0\n", + "for agent_name, metrics in agent_metrics.items():\n", + " retries = metrics.get('retries', 0)\n", + " total_retries += retries\n", + " if metrics.get('total_tokens', 0) > 0:\n", + " retry_info = f\", {retries} retries\" if retries > 0 else \"\"\n", + " utils.print_ok(f\"✅ {agent_name}: {metrics['total_tokens']} total tokens, {metrics['calls']} calls{retry_info}\")\n", + " else:\n", + " utils.print_warning(f\"⚠️ {agent_name}: No data (contract may not be deployed)\")\n", + "\n", + "if total_retries > 0:\n", + " utils.print_warning(f\"\\n🔄 Total retries across all agents: {total_retries}\")\n", + " utils.print_info(\" Consider reviewing rate limits or backend pool capacity if retries are high\")\n", + "\n", + "utils.print_info(\"\\n📌 Next Steps:\")\n", + "utils.print_info(\" 1. Review token consumption patterns and retry rates\")\n", + "utils.print_info(\" 2. Adjust rate limits in access contracts if retries are frequent\")\n", + "utils.print_info(\" 3. Implement production-ready error handling\")\n", + "utils.print_info(\" 4. Add monitoring and alerting for each agent type\")" + ] + }, + { + "cell_type": "markdown", + "id": "4b266016", + "metadata": {}, + "source": [ + "\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "The access contracts and APIM resources used by this notebook are managed by `citadel-access-contracts-tests.ipynb`.\n", + "Run the cleanup cell in that notebook to remove the test access contracts.\n", + "\n", + "> **Note:** This notebook does not create any APIM resources directly. The Foundry agent versions created during testing can be deleted via the Foundry portal if needed." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.13)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/5. citadel-pii-processing-tests.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/5. citadel-pii-processing-tests.ipynb new file mode 100644 index 000000000..6a61cebcb --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/5. citadel-pii-processing-tests.ipynb @@ -0,0 +1,2178 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e6191cca", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🛡️ Citadel PII Processing - Testing Center\n", + "\n", + "## Test PII Anonymization, Deanonymization, and Blocking capabilities!\n", + "\n", + "Use this Jupyter notebook to verify PII processing in Citadel Access Contracts, including:\n", + "- **Use Case 1**: PII Anonymization/Deanonymization with state saving\n", + "- **Use Case 2**: PII Blocking with detection reporting\n", + "- Testing with various PII types (names, emails, phone numbers, credit cards, IBANs, etc.)\n", + "- Validating custom regex pattern detection\n", + "- Verifying Event Hub logging for PII state saving\n", + "\n", + "> **Note:** This notebook assumes you have already deployed your Citadel Governance Hub with PII processing capabilities enabled. \n", + "> Ensure Azure AI Language Service is configured and the PII policy fragments are deployed.\n", + "\n", + "## Azure Prerequisites\n", + "\n", + "- An Azure AI Language Service instance with PII detection enabled\n", + "- Event Hub configured for PII state logging (if testing state saving)\n", + "- APIM policy fragments deployed: `pii-anonymization`, `pii-deanonymization`, `pii-state-saving`\n", + "- Managed Identity configured with access to Language Service" + ] + }, + { + "cell_type": "markdown", + "id": "4977a44d", + "metadata": {}, + "source": [ + "\n", + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01b9b9f5", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "cosmosdb_account_cmd = f\"az cosmosdb list --resource-group {env_governance_hub_resource_group} --query \\\"[0].name\\\" -o tsv\"\n", + "cosmosdb_account_output = utils.run(cosmosdb_account_cmd, 'Resolved Cosmos DB account', 'Failed to resolve Cosmos DB account')\n", + "cosmosdb_account = (cosmosdb_account_output.text or '').strip() if cosmosdb_account_output.success else ''\n", + "\n", + "cosmosdb_endpoint_cmd = f\"az cosmosdb show --name {cosmosdb_account} --resource-group {env_governance_hub_resource_group} --query \\\"documentEndpoint\\\" -o tsv\"\n", + "cosmosdb_endpoint_output = utils.run(cosmosdb_endpoint_cmd, 'Resolved Cosmos DB endpoint', 'Failed to resolve Cosmos DB endpoint') if cosmosdb_account else None\n", + "env_cosmosdb_endpoint = (cosmosdb_endpoint_output.text or '').strip() if cosmosdb_endpoint_output and cosmosdb_endpoint_output.success else ''\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")\n", + "print(f\"Cosmos DB Account: {cosmosdb_account or 'Not found'}\")\n", + "print(f\"Cosmos DB Endpoint: {env_cosmosdb_endpoint or 'Not found'}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fed58a51", + "metadata": {}, + "outputs": [], + "source": [ + "# ============================================================================\n", + "# 🔧 GOVERNANCE HUB CONFIGURATION (REQUIRED)\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "# ============================================================================\n", + "# 🔧 API VERSION CONFIGURATION\n", + "# ============================================================================\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "openai_api_version = \"2024-12-01-preview\"\n", + "targetInferenceApi = \"models\" # 'models' = Universal LLM API | 'openai' = Azure OpenAI API\n", + "\n", + "# ============================================================================\n", + "# 🧠 MODEL CONFIGURATION\n", + "# ============================================================================\n", + "model_name = \"gpt-4.1\"\n", + "\n", + "# ============================================================================\n", + "# 🗄️ COSMOS DB CONFIGURATION (PII Usage Telemetry)\n", + "# ============================================================================\n", + "cosmos_account_endpoint = env_cosmosdb_endpoint\n", + "cosmos_database_name = \"ai-usage-db\"\n", + "cosmos_container_name = \"pii-usage-container\"\n", + "cosmos_auth_mode = \"aad\" # \"aad\" (DefaultAzureCredential) or \"key\"\n", + "cosmos_account_key = \"\" # only required when cosmos_auth_mode == \"key\"\n", + "\n", + "# How many of the most recent PII processing records to load\n", + "top_n_records = 20\n", + "\n", + "# Optional filters (set to None to disable)\n", + "filter_product_id = None # e.g. \"LLM-HR-PIIMasking-DEV\"\n", + "filter_subscription_id = None # e.g. \"LLM-HR-PIIMasking-DEV-SUB-01\"\n", + "\n", + "utils.print_ok(\"Notebook variables initialized!\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b0aa2e44", + "metadata": {}, + "source": [ + "\n", + "### 1️⃣ Verify Azure CLI and Connected Subscription\n", + "\n", + "Ensure Azure CLI is authenticated and connected to the correct subscription:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "380dc998", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c3f681b", + "metadata": {}, + "source": [ + "\n", + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "👉 An existing Citadel Governance Hub deployment is expected with PII policy fragments deployed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70458a31", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(\n", + " governance_hub_resource_group\n", + " )\n", + " apimClientTool.initialize()\n", + " apimClientTool.discover_api(targetInferenceApi)\n", + "\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " azure_endpoint = str(apimClientTool.azure_endpoint)\n", + " \n", + " # Get supported models from the policy fragment\n", + " supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_info(f\"Supported models in APIM policy fragment 'set-backend-pools': {supported_models}\")\n", + "\n", + " if targetInferenceApi == \"openai\":\n", + " chat_completions_url = f\"{azure_endpoint}openai/deployments/{{model_name}}/chat/completions?api-version={inference_api_version}\"\n", + " else: # models\n", + " chat_completions_url = f\"{azure_endpoint}models/chat/completions?api-version={inference_api_version}\"\n", + " utils.print_info(f\"Chat Completion Endpoint Template: {chat_completions_url}\")\n", + "\n", + " utils.print_info(f\"Using the following API: {apimClientTool.api_id}\")\n", + "\n", + " utils.print_ok(f\"Testing tool initialized successfully!\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client Tool: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "aa35a8ce", + "metadata": {}, + "source": [ + "---\n", + "## 🔐 Use Case 1: PII Anonymization & Deanonymization\n", + "\n", + "This use case tests PII masking where:\n", + "- Inbound requests have PII replaced with placeholders (e.g., ``, ``)\n", + "- Outbound responses have placeholders restored with original PII values\n", + "- State saving logs all PII processing to Event Hub for auditing\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "6aed1d72", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣.1 Define PII Masking Access Contract\n", + "\n", + "Create an access contract with PII anonymization/deanonymization enabled and state saving turned on." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "327e377b", + "metadata": {}, + "outputs": [], + "source": [ + "timestamp = time.strftime('%Y%m%d%H%M%S')\n", + "\n", + "# PII Masking Use Case Configuration\n", + "pii_masking_contract = {\n", + " \"name\": f\"pii-masking-contract-{timestamp}\",\n", + " \"business_unit\": \"HR\",\n", + " \"use_case_name\": \"PIIMasking\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": False,\n", + " \"endpoint_secret\": \"HR-PII-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"HR-PII-LLM-KEY\",\n", + " \"description\": \"HR PII Masking - Anonymization/Deanonymization with State Saving\",\n", + " \"pii_config\": {\n", + " \"mode\": \"anonymization\",\n", + " \"confidence_threshold\": \"0.8\",\n", + " \"entity_exclusions\": \"PersonType\",\n", + " \"detection_language\": \"en\",\n", + " \"state_saving_enabled\": True\n", + " }\n", + "}\n", + "\n", + "utils.print_info(f\"PII Masking Contract Configuration:\")\n", + "utils.print_info(f\" Business Unit: {pii_masking_contract['business_unit']}\")\n", + "utils.print_info(f\" Use Case: {pii_masking_contract['use_case_name']}\")\n", + "utils.print_info(f\" PII Mode: {pii_masking_contract['pii_config']['mode']}\")\n", + "utils.print_info(f\" State Saving: {pii_masking_contract['pii_config']['state_saving_enabled']}\")\n", + "utils.print_info(f\" Product ID: LLM-{pii_masking_contract['business_unit']}-{pii_masking_contract['use_case_name']}-{pii_masking_contract['environment']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9e9d4ae3", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣.2 Create PII Masking Product Policy\n", + "\n", + "Generate a custom product policy XML that enables PII anonymization/deanonymization with custom regex patterns and state saving." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5212af0", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "\n", + "bicep_dir = \"../bicep/infra/citadel-access-contracts\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "# Create folder structure for PII Masking contract\n", + "contract = pii_masking_contract\n", + "folder_name = f\"{contract['business_unit'].lower()}-{contract['use_case_name'].lower()}\"\n", + "environment_folder = contract['environment'].lower()\n", + "masking_contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + "os.makedirs(masking_contract_folder, exist_ok=True)\n", + "utils.print_info(f\"📁 Created folder: {masking_contract_folder}\")\n", + "\n", + "# Create PII Masking Policy XML\n", + "pii_masking_policy = '''\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " (\\\"piiAnonymizationEnabled\\\") == \\\"true\\\")\">\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " (preserveContent: true))\" />\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " @(context.Variables.GetValueOrDefault(\"piiAnonymizedContent\"))\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " (preserveContent: true))\" />\n", + " \n", + " \n", + " (\\\"piiAnonymizationEnabled\\\") == \\\"true\\\" && \n", + " context.Variables.ContainsKey(\\\"piiMappings\\\"))\">\n", + " \n", + " \n", + " (\\\"responseBodyContent\\\"))\" />\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " (\\\"piiInputContent\\\"))\" />\n", + " (\\\"responseBodyContent\\\"))\" />\n", + " \n", + " \n", + " \n", + " @(context.Variables.GetValueOrDefault(\"piiDeanonymizedContentOutput\"))\n", + " \n", + " \n", + " \n", + " @(context.Variables.GetValueOrDefault(\"responseBodyContent\"))\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "# Write the policy file\n", + "masking_policy_file = os.path.join(masking_contract_folder, \"ai-product-policy.xml\")\n", + "with open(masking_policy_file, 'w', encoding='utf-8') as f:\n", + " f.write(pii_masking_policy)\n", + "utils.print_ok(f\"✅ PII Masking policy file created: {masking_policy_file}\")" + ] + }, + { + "cell_type": "markdown", + "id": "609d68ae", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣.3 Create Parameter File and Deploy PII Masking Contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1febe7d2", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate parameter file for PII Masking contract\n", + "masking_params_file = os.path.join(masking_contract_folder, \"main.bicepparam\")\n", + "policy_relative_path = \"ai-product-policy.xml\"\n", + "\n", + "masking_params_content = f'''using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// {contract['description']} - Generated from PII Testing Notebook\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000' // Not used since use_keyvault is false, but required by the template\n", + " resourceGroupName: 'placeholder'\n", + " name: 'placeholder'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = false\n", + "\n", + "param useCase = {{\n", + " businessUnit: '{contract['business_unit']}'\n", + " useCaseName: '{contract['use_case_name']}'\n", + " environment: '{contract['environment']}'\n", + "}}\n", + "\n", + "param apiNameMapping = {{\n", + " LLM: ['universal-llm-api', 'azure-openai-api', 'unified-ai-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: '{contract['endpoint_secret']}'\n", + " apiKeySecretName: '{contract['apikey_secret']}'\n", + " policyXml: loadTextContent('{policy_relative_path}')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'PII Masking Access Contract - {contract[\"description\"]}'\n", + "\n", + "// Azure AI Foundry Integration (disabled)\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}}\n", + "'''\n", + "\n", + "with open(masking_params_file, 'w', encoding='utf-8') as f:\n", + " f.write(masking_params_content)\n", + "utils.print_ok(f\"✅ Parameter file created: {masking_params_file}\")\n", + "\n", + "# Deploy the PII Masking access contract\n", + "utils.print_info(f\"\\n{'='*60}\")\n", + "utils.print_info(f\"Deploying PII Masking Access Contract...\")\n", + "utils.print_info(f\"{'='*60}\")\n", + "\n", + "deployment_cmd = f\"az deployment sub create --name {contract['name']} --location {location} --template-file {template_file} --parameters {masking_params_file}\"\n", + "\n", + "masking_deployment_output = utils.run(\n", + " deployment_cmd,\n", + " f\"Deployment '{contract['name']}' succeeded\",\n", + " f\"Deployment '{contract['name']}' failed\"\n", + ")\n", + "\n", + "if masking_deployment_output.success:\n", + " utils.print_ok(f\"✅ PII Masking Access Contract deployed successfully!\")\n", + "else:\n", + " utils.print_error(f\"❌ PII Masking Access Contract deployment failed!\")" + ] + }, + { + "cell_type": "markdown", + "id": "8375a9d0", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣.4 Retrieve API Key for PII Masking Contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0743c580", + "metadata": {}, + "outputs": [], + "source": [ + "# Re-initialize APIM client to pick up new subscriptions\n", + "apimClientTool.initialize()\n", + "\n", + "masking_product_id = f\"LLM-{pii_masking_contract['business_unit']}-{pii_masking_contract['use_case_name']}-{pii_masking_contract['environment']}\"\n", + "masking_subscription_name = f\"{masking_product_id}-SUB-01\"\n", + "masking_api_key = None\n", + "\n", + "for sub in apimClientTool.apim_subscriptions:\n", + " if masking_subscription_name.lower() in sub.get('name', '').lower():\n", + " masking_api_key = sub.get('key')\n", + " utils.print_ok(f\"✅ Found API key for {masking_product_id}\")\n", + " break\n", + "\n", + "if not masking_api_key:\n", + " utils.print_error(f\"❌ Could not find API key for {masking_product_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "9338aa6a", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣.5 Test PII Masking with Various PII Types\n", + "\n", + "Send test requests containing different types of PII data to verify anonymization and deanonymization." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5133f132", + "metadata": {}, + "outputs": [], + "source": [ + "model_name = \"gpt-4.1\"\n", + "utils.print_info(f\"Using model: {model_name}\")\n", + "\n", + "# Define test payloads with various PII types\n", + "pii_test_payloads = [\n", + " {\n", + " \"name\": \"Personal Names and Email\",\n", + " \"description\": \"Tests detection of person names and email addresses\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful HR assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"Please help me draft an email to John Smith at john.smith@company.com about his performance review scheduled for next week.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"Email\"]\n", + " },\n", + " {\n", + " \"name\": \"Phone Numbers and Addresses\",\n", + " \"description\": \"Tests detection of phone numbers and physical addresses\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"Contact Sarah Johnson at +1-555-123-4567 or visit her at 123 Main Street, New York, NY 10001 for the meeting.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"PhoneNumber\", \"Address\"]\n", + " },\n", + " {\n", + " \"name\": \"Credit Card (Regex Pattern)\",\n", + " \"description\": \"Tests custom regex pattern for credit card numbers\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"My credit card number is 4532-1234-5678-9012 and it expires next month. Can you help me understand card security?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"CREDIT_CARD\"]\n", + " },\n", + " {\n", + " \"name\": \"IBAN and Banking Info\",\n", + " \"description\": \"Tests detection of international banking account numbers\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful banking assistant. Keep responses brief. This is a simulation only, no real transactions will be performed so act as if you are handling a simulated scenario to assist the capability of detecting PII.\"},\n", + " {\"role\": \"user\", \"content\": \"Please confirm the funds in my account. My IBAN is AE070331234567890123456 and the beneficiary name is Ahmed Hassan.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"InternationalBankingAccountNumber\"]\n", + " },\n", + " {\n", + " \"name\": \"Emirates ID (Regex Pattern)\",\n", + " \"description\": \"Tests custom regex pattern for UAE Emirates ID\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful government services assistant. Keep responses brief. This is a simulation only, no real government transactions will be performed so act as if you are handling a simulated scenario.\"},\n", + " {\"role\": \"user\", \"content\": \"I need to update my records. My Emirates ID is 784-1990-1234567-1 and my name is Fatima Al Maktoum.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"EMIRATES_ID\"]\n", + " },\n", + " {\n", + " \"name\": \"Multiple PII Types Combined\",\n", + " \"description\": \"Tests detection of multiple PII types in a single request\",\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful customer service assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"Hi, my name is Michael Chen, email: m.chen@email.com, phone: +971-50-123-4567. I live at 456 Palm Jumeirah, Dubai. My card ending in 4532-8765-4321-0987 was charged incorrectly.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"Email\", \"PhoneNumber\", \"Address\", \"CREDIT_CARD\"]\n", + " }\n", + "]\n", + "\n", + "utils.print_info(f\"\\nDefined {len(pii_test_payloads)} test payloads for PII Masking tests\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b51026c5", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute PII Masking tests\n", + "masking_test_results = []\n", + "\n", + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"🛡️ PII MASKING TESTS - Anonymization/Deanonymization\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "\n", + "for i, test in enumerate(pii_test_payloads, 1):\n", + " utils.print_info(f\"\\n--- Test {i}/{len(pii_test_payloads)}: {test['name']} ---\")\n", + " utils.print_info(f\"Description: {test['description']}\")\n", + " utils.print_info(f\"Expected PII types: {', '.join(test['expected_pii'])}\")\n", + " \n", + " # Extract user message for display\n", + " user_message = next((m['content'] for m in test['payload']['messages'] if m['role'] == 'user'), '')\n", + " utils.print_info(f\"User message: {user_message[:100]}...\" if len(user_message) > 100 else f\"User message: {user_message}\")\n", + " \n", + " try:\n", + " start_time = time.time()\n", + " response = requests.post(\n", + " chat_completions_url,\n", + " headers={'api-key': masking_api_key},\n", + " json=test['payload'],\n", + " timeout=60\n", + " )\n", + " elapsed_time = time.time() - start_time\n", + " \n", + " utils.print_response_code(response)\n", + " \n", + " result = {\n", + " \"test_name\": test['name'],\n", + " \"status_code\": response.status_code,\n", + " \"elapsed_time\": elapsed_time,\n", + " \"success\": response.status_code == 200\n", + " }\n", + " \n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " result[\"response_content\"] = content\n", + " utils.print_ok(f\"✅ Response received ({elapsed_time:.2f}s)\")\n", + " utils.print_info(f\"💬 Response: {content[:150]}...\" if len(content) > 150 else f\"💬 Response: {content}\")\n", + " \n", + " # Check if response contains any of the original PII (it shouldn't after deanonymization)\n", + " # The LLM should respond with the original PII restored\n", + " utils.print_ok(f\"🔄 PII should be deanonymized in response\")\n", + " else:\n", + " result[\"error\"] = response.text[:200]\n", + " utils.print_error(f\"❌ Error: {response.text[:600]}\")\n", + " \n", + " masking_test_results.append(result)\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"❌ Request failed: {e}\")\n", + " masking_test_results.append({\n", + " \"test_name\": test['name'],\n", + " \"status_code\": 0,\n", + " \"elapsed_time\": 0,\n", + " \"success\": False,\n", + " \"error\": str(e)\n", + " })\n", + " \n", + " time.sleep(1) # Small delay between tests\n", + "\n", + "# Summary\n", + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"📊 PII MASKING TEST SUMMARY\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "successful = sum(1 for r in masking_test_results if r['success'])\n", + "utils.print_info(f\"Total tests: {len(masking_test_results)}\")\n", + "utils.print_ok(f\"Successful: {successful}\")\n", + "utils.print_error(f\"Failed: {len(masking_test_results) - successful}\")" + ] + }, + { + "cell_type": "markdown", + "id": "84a6f4da", + "metadata": {}, + "source": [ + "---\n", + "## 🚫 Use Case 2: PII Blocking\n", + "\n", + "This use case tests PII blocking where:\n", + "- Requests containing PII are rejected with HTTP 400 Bad Request\n", + "- Detected PII categories are reported in the error response\n", + "- No PII data reaches the backend LLM service\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "c4d78e0b", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.1 Define PII Blocking Access Contract\n", + "\n", + "Create an access contract that blocks any requests containing PII data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4062f7a4", + "metadata": {}, + "outputs": [], + "source": [ + "# PII Blocking Use Case Configuration\n", + "pii_blocking_contract = {\n", + " \"name\": f\"pii-blocking-contract-{timestamp}\",\n", + " \"business_unit\": \"Compliance\",\n", + " \"use_case_name\": \"PIIBlocking\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": False,\n", + " \"endpoint_secret\": \"COMPLIANCE-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"COMPLIANCE-LLM-KEY\",\n", + " \"description\": \"Compliance PII Blocking - Reject requests containing PII\",\n", + " \"pii_config\": {\n", + " \"mode\": \"blocking\",\n", + " \"confidence_threshold\": \"0.75\",\n", + " \"entity_exclusions\": \"PersonType\",\n", + " \"detection_language\": \"en\"\n", + " }\n", + "}\n", + "\n", + "utils.print_info(f\"PII Blocking Contract Configuration:\")\n", + "utils.print_info(f\" Business Unit: {pii_blocking_contract['business_unit']}\")\n", + "utils.print_info(f\" Use Case: {pii_blocking_contract['use_case_name']}\")\n", + "utils.print_info(f\" PII Mode: {pii_blocking_contract['pii_config']['mode']}\")\n", + "utils.print_info(f\" Product ID: LLM-{pii_blocking_contract['business_unit']}-{pii_blocking_contract['use_case_name']}-{pii_blocking_contract['environment']}\")" + ] + }, + { + "cell_type": "markdown", + "id": "eb78fa34", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.2 Create PII Blocking Product Policy\n", + "\n", + "Generate a custom product policy XML that detects PII and blocks requests containing sensitive data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f14b085", + "metadata": {}, + "outputs": [], + "source": [ + "# Create folder structure for PII Blocking contract\n", + "contract = pii_blocking_contract\n", + "folder_name = f\"{contract['business_unit'].lower()}-{contract['use_case_name'].lower()}\"\n", + "environment_folder = contract['environment'].lower()\n", + "blocking_contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + "os.makedirs(blocking_contract_folder, exist_ok=True)\n", + "utils.print_info(f\"📁 Created folder: {blocking_contract_folder}\")\n", + "\n", + "# Create PII Blocking Policy XML\n", + "# Note: This policy uses pii-anonymization for detection but blocks instead of anonymizing\n", + "pii_blocking_policy = '''\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " (\\\"piiBlockingEnabled\\\") == \\\"true\\\")\">\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " (preserveContent: true))\" />\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " (\\\"piiMappings\\\", \\\"[]\\\");\n", + " var mappingsArray = JArray.Parse(mappings);\n", + " return mappingsArray.Count > 0;\n", + " }\">\n", + " \n", + " \n", + " \n", + " application/json\n", + " \n", + " @{\n", + " var mappings = JArray.Parse(context.Variables.GetValueOrDefault(\"piiMappings\", \"[]\"));\n", + " var categories = new HashSet();\n", + " foreach (var mapping in mappings) {\n", + " var placeholder = mapping[\"placeholder\"].ToString();\n", + " var category = placeholder.TrimStart('<').Split('_')[0];\n", + " categories.Add(category);\n", + " }\n", + " return new JObject(\n", + " new JProperty(\"error\", new JObject(\n", + " new JProperty(\"code\", \"PII_DETECTED\"),\n", + " new JProperty(\"message\", \"Request blocked: Personal Identifiable Information (PII) detected in the request.\"),\n", + " new JProperty(\"detectedCategories\", string.Join(\", \", categories)),\n", + " new JProperty(\"entityCount\", mappings.Count)\n", + " ))\n", + " ).ToString();\n", + " }\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "# Write the policy file\n", + "blocking_policy_file = os.path.join(blocking_contract_folder, \"ai-product-policy.xml\")\n", + "with open(blocking_policy_file, 'w', encoding='utf-8') as f:\n", + " f.write(pii_blocking_policy)\n", + "utils.print_ok(f\"✅ PII Blocking policy file created: {blocking_policy_file}\")" + ] + }, + { + "cell_type": "markdown", + "id": "a152d686", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.3 Create Parameter File and Deploy PII Blocking Contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47457af0", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate parameter file for PII Blocking contract\n", + "blocking_params_file = os.path.join(blocking_contract_folder, \"main.bicepparam\")\n", + "policy_relative_path = \"ai-product-policy.xml\"\n", + "\n", + "blocking_params_content = f'''using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// {contract['description']} - Generated from PII Testing Notebook\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000' // Not used since use_keyvault is false, but required by the template\n", + " resourceGroupName: 'placeholder'\n", + " name: 'placeholder'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = false\n", + "param useCase = {{\n", + " businessUnit: '{contract['business_unit']}'\n", + " useCaseName: '{contract['use_case_name']}'\n", + " environment: '{contract['environment']}'\n", + "}}\n", + "\n", + "param apiNameMapping = {{\n", + " LLM: ['universal-llm-api', 'azure-openai-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: '{contract['endpoint_secret']}'\n", + " apiKeySecretName: '{contract['apikey_secret']}'\n", + " policyXml: loadTextContent('{policy_relative_path}')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'PII Blocking Access Contract - {contract[\"description\"]}'\n", + "\n", + "// Azure AI Foundry Integration (disabled)\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}}\n", + "'''\n", + "\n", + "with open(blocking_params_file, 'w', encoding='utf-8') as f:\n", + " f.write(blocking_params_content)\n", + "utils.print_ok(f\"✅ Parameter file created: {blocking_params_file}\")\n", + "\n", + "# Deploy the PII Blocking access contract\n", + "utils.print_info(f\"\\n{'='*60}\")\n", + "utils.print_info(f\"Deploying PII Blocking Access Contract...\")\n", + "utils.print_info(f\"{'='*60}\")\n", + "\n", + "deployment_cmd = f\"az deployment sub create --name {contract['name']} --location {location} --template-file {template_file} --parameters {blocking_params_file}\"\n", + "\n", + "blocking_deployment_output = utils.run(\n", + " deployment_cmd,\n", + " f\"Deployment '{contract['name']}' succeeded\",\n", + " f\"Deployment '{contract['name']}' failed\"\n", + ")\n", + "\n", + "if blocking_deployment_output.success:\n", + " utils.print_ok(f\"✅ PII Blocking Access Contract deployed successfully!\")\n", + "else:\n", + " utils.print_error(f\"❌ PII Blocking Access Contract deployment failed!\")" + ] + }, + { + "cell_type": "markdown", + "id": "ca0412c1", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.4 Retrieve API Key for PII Blocking Contract" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9464e16", + "metadata": {}, + "outputs": [], + "source": [ + "# Re-initialize APIM client to pick up new subscriptions\n", + "apimClientTool.initialize()\n", + "\n", + "blocking_product_id = f\"LLM-{pii_blocking_contract['business_unit']}-{pii_blocking_contract['use_case_name']}-{pii_blocking_contract['environment']}\"\n", + "blocking_subscription_name = f\"{blocking_product_id}-SUB-01\"\n", + "blocking_api_key = None\n", + "\n", + "for sub in apimClientTool.apim_subscriptions:\n", + " if blocking_subscription_name.lower() in sub.get('name', '').lower():\n", + " blocking_api_key = sub.get('key')\n", + " utils.print_ok(f\"✅ Found API key for {blocking_product_id}\")\n", + " break\n", + "\n", + "if not blocking_api_key:\n", + " utils.print_error(f\"❌ Could not find API key for {blocking_product_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "81fe412b", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣.5 Test PII Blocking with Various PII Types\n", + "\n", + "Send test requests containing PII data to verify they are blocked, and requests without PII are allowed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4edd96c", + "metadata": {}, + "outputs": [], + "source": [ + "# Define test payloads for PII Blocking - mix of requests with and without PII\n", + "pii_blocking_test_payloads = [\n", + " {\n", + " \"name\": \"Request WITH PII - Should be BLOCKED\",\n", + " \"description\": \"Request containing email and name - should return 400\",\n", + " \"should_block\": True,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Send an email to John Smith at john.smith@company.com about the project update.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"Email\"]\n", + " },\n", + " {\n", + " \"name\": \"Request WITHOUT PII - Should be ALLOWED\",\n", + " \"description\": \"Generic request without any PII - should return 200\",\n", + " \"should_block\": False,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"What is the capital of France?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": []\n", + " },\n", + " {\n", + " \"name\": \"Request WITH Credit Card - Should be BLOCKED\",\n", + " \"description\": \"Request containing credit card number - should return 400\",\n", + " \"should_block\": True,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"My card number is 4532-1234-5678-9012. Why was I charged twice?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"CREDIT_CARD\"]\n", + " },\n", + " {\n", + " \"name\": \"Request WITHOUT PII - Technical Question\",\n", + " \"description\": \"Technical question without PII - should return 200\",\n", + " \"should_block\": False,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful coding assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"How do I create a REST API in Python using Flask?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": []\n", + " },\n", + " {\n", + " \"name\": \"Request WITH Phone Number - Should be BLOCKED\",\n", + " \"description\": \"Request containing phone number - should return 400\",\n", + " \"should_block\": True,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Please call me back at +1-555-123-4567 to discuss the issue.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"PhoneNumber\"]\n", + " },\n", + " {\n", + " \"name\": \"Request WITH Emirates ID - Should be BLOCKED\",\n", + " \"description\": \"Request containing Emirates ID (custom regex) - should return 400\",\n", + " \"should_block\": True,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"My Emirates ID is 784-1990-1234567-1. Can you help me with my visa application?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"EMIRATES_ID\"]\n", + " },\n", + " {\n", + " \"name\": \"Request WITHOUT PII - Math Question\",\n", + " \"description\": \"Simple math question without PII - should return 200\",\n", + " \"should_block\": False,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful math tutor.\"},\n", + " {\"role\": \"user\", \"content\": \"What is 25 multiplied by 4?\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": []\n", + " },\n", + " {\n", + " \"name\": \"Request WITH Multiple PII Types - Should be BLOCKED\",\n", + " \"description\": \"Request with multiple PII types - should return 400 with all categories\",\n", + " \"should_block\": True,\n", + " \"payload\": {\n", + " \"model\": model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Hi, I'm Sarah Johnson (sarah.j@email.com, +971-50-987-6543). My IBAN is AE070331234567890123456. Please help me.\"}\n", + " ]\n", + " },\n", + " \"expected_pii\": [\"Person\", \"Email\", \"PhoneNumber\", \"InternationalBankingAccountNumber\"]\n", + " }\n", + "]\n", + "\n", + "utils.print_info(f\"\\nDefined {len(pii_blocking_test_payloads)} test payloads for PII Blocking tests\")\n", + "utils.print_info(f\" - Requests that should be BLOCKED: {sum(1 for t in pii_blocking_test_payloads if t['should_block'])}\")\n", + "utils.print_info(f\" - Requests that should be ALLOWED: {sum(1 for t in pii_blocking_test_payloads if not t['should_block'])}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63240b45", + "metadata": {}, + "outputs": [], + "source": [ + "# Execute PII Blocking tests\n", + "blocking_test_results = []\n", + "\n", + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"🚫 PII BLOCKING TESTS - Request Rejection\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "\n", + "for i, test in enumerate(pii_blocking_test_payloads, 1):\n", + " utils.print_info(f\"\\n--- Test {i}/{len(pii_blocking_test_payloads)}: {test['name']} ---\")\n", + " utils.print_info(f\"Description: {test['description']}\")\n", + " utils.print_info(f\"Expected outcome: {'BLOCKED (400)' if test['should_block'] else 'ALLOWED (200)'}\")\n", + " if test['expected_pii']:\n", + " utils.print_info(f\"Expected PII types: {', '.join(test['expected_pii'])}\")\n", + " \n", + " # Extract user message for display\n", + " user_message = next((m['content'] for m in test['payload']['messages'] if m['role'] == 'user'), '')\n", + " utils.print_info(f\"User message: {user_message[:80]}...\" if len(user_message) > 80 else f\"User message: {user_message}\")\n", + " \n", + " try:\n", + " start_time = time.time()\n", + " response = requests.post(\n", + " chat_completions_url,\n", + " headers={'api-key': blocking_api_key},\n", + " json=test['payload'],\n", + " timeout=60\n", + " )\n", + " elapsed_time = time.time() - start_time\n", + " \n", + " # Determine if test passed based on expected outcome\n", + " if test['should_block']:\n", + " test_passed = response.status_code == 400\n", + " else:\n", + " test_passed = response.status_code == 200\n", + " \n", + " result = {\n", + " \"test_name\": test['name'],\n", + " \"status_code\": response.status_code,\n", + " \"elapsed_time\": elapsed_time,\n", + " \"should_block\": test['should_block'],\n", + " \"test_passed\": test_passed\n", + " }\n", + " \n", + " if test_passed:\n", + " utils.print_ok(f\"✅ TEST PASSED - Status: {response.status_code} ({elapsed_time:.2f}s)\")\n", + " else:\n", + " utils.print_error(f\"❌ TEST FAILED - Expected: {'400' if test['should_block'] else '200'}, Got: {response.status_code}\")\n", + " \n", + " if response.status_code == 400:\n", + " try:\n", + " error_data = response.json()\n", + " error_info = error_data.get('error', {})\n", + " result[\"detected_categories\"] = error_info.get('detectedCategories', '')\n", + " result[\"entity_count\"] = error_info.get('entityCount', 0)\n", + " utils.print_info(f\" 🔍 Detected PII categories: {error_info.get('detectedCategories', 'N/A')}\")\n", + " utils.print_info(f\" 📊 Entity count: {error_info.get('entityCount', 'N/A')}\")\n", + " except:\n", + " utils.print_info(f\" Response: {response.text[:200]}\")\n", + " elif response.status_code == 200:\n", + " data = response.json()\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " result[\"response_content\"] = content\n", + " utils.print_info(f\" 💬 Response: {content[:100]}...\" if len(content) > 100 else f\" 💬 Response: {content}\")\n", + " else:\n", + " result[\"error\"] = response.text[:200]\n", + " utils.print_error(f\" Unexpected response: {response.text[:200]}\")\n", + " \n", + " blocking_test_results.append(result)\n", + " \n", + " except Exception as e:\n", + " utils.print_error(f\"❌ Request failed: {e}\")\n", + " blocking_test_results.append({\n", + " \"test_name\": test['name'],\n", + " \"status_code\": 0,\n", + " \"elapsed_time\": 0,\n", + " \"should_block\": test['should_block'],\n", + " \"test_passed\": False,\n", + " \"error\": str(e)\n", + " })\n", + " \n", + " time.sleep(1) # Small delay between tests\n", + "\n", + "# Summary\n", + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"📊 PII BLOCKING TEST SUMMARY\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "total_tests = len(blocking_test_results)\n", + "passed_tests = sum(1 for r in blocking_test_results if r['test_passed'])\n", + "blocked_correctly = sum(1 for r in blocking_test_results if r['should_block'] and r['status_code'] == 400)\n", + "allowed_correctly = sum(1 for r in blocking_test_results if not r['should_block'] and r['status_code'] == 200)\n", + "\n", + "utils.print_info(f\"Total tests: {total_tests}\")\n", + "utils.print_ok(f\"Tests passed: {passed_tests}/{total_tests}\")\n", + "utils.print_info(f\" - Correctly blocked (with PII): {blocked_correctly}\")\n", + "utils.print_info(f\" - Correctly allowed (no PII): {allowed_correctly}\")\n", + "if passed_tests < total_tests:\n", + " utils.print_error(f\"Tests failed: {total_tests - passed_tests}\")" + ] + }, + { + "cell_type": "markdown", + "id": "73469feb", + "metadata": {}, + "source": [ + "---\n", + "## 📈 Use Case 3: PII Processing Performance Analytics\n", + "\n", + "This section pulls **recent PII processing telemetry** from the gateway's Cosmos DB\n", + "(`pii-usage-container`) and provides:\n", + "\n", + "- 🔢 Quantitative report (records, entity totals, success rates, content size impact)\n", + "- 📊 Visualizations (entity-category mix, anonymization vs. deanonymization performance, content-length transformation, processing volume over time)\n", + "- 🤖 Optional **LLM-powered reasoning** over the analytics summary, sent through the **PII Masking Access Contract** so any residual PII in the summary is anonymized before reaching the model.\n", + "\n", + "> Make sure the gateway is configured to log PII processing events to a Cosmos DB container with documents shaped like the `PII_Processing` schema produced by the `pii-state-saving` policy fragment.\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "e12ae89d", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.1 Configure Cosmos DB Connection\n", + "\n", + "Configure the Cosmos DB account, database, and container that store the PII processing\n", + "telemetry, plus how many recent records to load.\n", + "\n", + "Authentication options:\n", + "- `aad` → uses your Azure AD identity via `DefaultAzureCredential` (recommended; requires the *Cosmos DB Built-in Data Reader* role on the account)\n", + "- `key` → uses the provided account key (set `cosmos_account_key`)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eab344e0", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"Cosmos endpoint : {cosmos_account_endpoint}\")\n", + "utils.print_info(f\"Database / container: {cosmos_database_name} / {cosmos_container_name}\")\n", + "utils.print_info(f\"Auth mode : {cosmos_auth_mode}\")\n", + "utils.print_info(f\"Top N records : {top_n_records}\")\n", + "if filter_product_id:\n", + " utils.print_info(f\"Filter product : {filter_product_id}\")\n", + "if filter_subscription_id:\n", + " utils.print_info(f\"Filter subscription : {filter_subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "a79e703c", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.1.1 Grant Current User Cosmos DB Data-Plane Access\n", + "\n", + "Cosmos DB **control-plane** roles (Owner, Contributor) do **not** grant data-plane (SQL) read/write access. To query items via AAD (`DefaultAzureCredential`), the signed-in user needs a SQL role assignment.\n", + "\n", + "This step uses the Azure CLI to assign the **Cosmos DB Built-in Data Reader** role (`00000000-0000-0000-0000-000000000001`) at the account scope (`/`). Switch to `00000000-0000-0000-0000-000000000002` (Built-in Data Contributor) if you also need writes.\n", + "\n", + "**Prerequisites for the caller:**\n", + "- `az login` already done (the same identity that will run the queries).\n", + "- Permission `Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments/write` on the Cosmos account (e.g. *Cosmos DB Operator*, *Owner*, or *User Access Administrator*). Otherwise the cell prints the exact CLI command an admin can run on your behalf.\n", + "\n", + "Role propagation can take **1–5 minutes** — if the next cell still 401s, wait briefly and re-run.\n", + "\n", + ">**NOTE**: if you get `Continuous access evaluation resulted in challenge`, try to run the following `az logout`, `az account clear` and `az login` again to refresh your credentials and ensure the new role assignment is recognized.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b48ecef", + "metadata": {}, + "outputs": [], + "source": [ + "# ============================================================================\n", + "# 🔐 GRANT CURRENT USER COSMOS DB SQL DATA-PLANE ACCESS (via Azure CLI)\n", + "# ----------------------------------------------------------------------------\n", + "# Assigns the Cosmos DB Built-in Data Reader role at account scope to the\n", + "# currently signed-in AAD principal so the notebook can query items via\n", + "# DefaultAzureCredential.\n", + "# ============================================================================\n", + "from urllib.parse import urlparse\n", + "import json as _json\n", + "\n", + "# --- Resolve account name from endpoint and pick resource group --------------\n", + "cosmos_account_name = urlparse(cosmos_account_endpoint).hostname.split('.')[0]\n", + "cosmos_resource_group = governance_hub_resource_group # default to hub RG\n", + "\n", + "# Built-in role definition IDs (well-known, same in every Cosmos account)\n", + "COSMOS_DATA_READER = '00000000-0000-0000-0000-000000000001'\n", + "COSMOS_DATA_CONTRIBUTOR = '00000000-0000-0000-0000-000000000002'\n", + "cosmos_data_role_id = COSMOS_DATA_READER # change to COSMOS_DATA_CONTRIBUTOR if writes are needed\n", + "\n", + "utils.print_info(f\"Cosmos account : {cosmos_account_name}\")\n", + "utils.print_info(f\"Resource group : {governance_hub_resource_group}\")\n", + "utils.print_info(f\"Role definition: {cosmos_data_role_id} (Data Reader)\")\n", + "\n", + "# --- Resolve signed-in principal objectId ------------------------------------\n", + "principal_object_id = None\n", + "who = utils.run(\"az ad signed-in-user show --query id -o tsv\",\n", + " 'Resolved signed-in user', 'Failed to resolve signed-in user')\n", + "if who.success:\n", + " principal_object_id = (who.text or '').strip()\n", + "\n", + "if not principal_object_id:\n", + " utils.print_error('Could not resolve signed-in user objectId. Run `az login` and re-run this cell.')\n", + "else:\n", + " utils.print_info(f\"Principal id : {principal_object_id}\")\n", + "\n", + " # --- Idempotency: skip if an assignment for this principal already exists -\n", + " list_cmd = (f\"az cosmosdb sql role assignment list \"\n", + " f\"--account-name {cosmos_account_name} \"\n", + " f\"--resource-group {governance_hub_resource_group} -o json\")\n", + " existing = utils.run(list_cmd, 'Listed existing role assignments',\n", + " 'Failed to list role assignments (need Cosmos DB Operator/Owner)')\n", + " already_assigned = False\n", + " if existing.success and existing.text:\n", + " try:\n", + " for a in _json.loads(existing.text):\n", + " if (a.get('principalId','').lower() == principal_object_id.lower()\n", + " and a.get('roleDefinitionId','').endswith(cosmos_data_role_id)):\n", + " already_assigned = True\n", + " break\n", + " except Exception as ex:\n", + " utils.print_error(f\"Could not parse existing assignments: {ex}\")\n", + "\n", + " create_cmd = (f\"az cosmosdb sql role assignment create \"\n", + " f\"--account-name {cosmos_account_name} \"\n", + " f\"--resource-group {governance_hub_resource_group} \"\n", + " f\"--scope \\\"/\\\" \"\n", + " f\"--principal-id {principal_object_id} \"\n", + " f\"--role-definition-id {cosmos_data_role_id}\")\n", + "\n", + " if already_assigned:\n", + " utils.print_ok('Role assignment already exists — nothing to do.')\n", + " else:\n", + " result = utils.run(create_cmd, 'Granted Cosmos DB data-plane access',\n", + " 'Failed to create role assignment')\n", + " if not result.success:\n", + " utils.print_info('If you lack permission, ask an admin to run:')\n", + " print(' ' + create_cmd)\n", + " else:\n", + " utils.print_info('Role propagation can take 1–5 minutes before queries succeed.')\n" + ] + }, + { + "cell_type": "markdown", + "id": "43bf59ba", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.2 Query Recent PII Processing Records\n", + "\n", + "Loads the most recent `top_n_records` PII processing documents from Cosmos DB and\n", + "flattens them into a `pandas` DataFrame for analysis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06dd6fdd", + "metadata": {}, + "outputs": [], + "source": [ + "# Ensure azure-cosmos v4+ is installed (older v3.x ships without `CosmosClient`)\n", + "import importlib, subprocess\n", + "\n", + "def _ensure_cosmos_v4():\n", + " try:\n", + " mod = importlib.import_module(\"azure.cosmos\")\n", + " if hasattr(mod, \"CosmosClient\"):\n", + " return\n", + " utils.print_info(\"Detected old azure-cosmos package without CosmosClient — upgrading ...\")\n", + " except ImportError:\n", + " utils.print_info(\"Installing azure-cosmos ...\")\n", + " subprocess.check_call([\n", + " sys.executable, \"-m\", \"pip\", \"install\", \"--quiet\",\n", + " \"--upgrade\", \"--force-reinstall\", \"azure-cosmos>=4.5.0\"\n", + " ])\n", + " # Reload to pick up the upgraded package\n", + " if \"azure.cosmos\" in sys.modules:\n", + " for name in [n for n in list(sys.modules) if n == \"azure.cosmos\" or n.startswith(\"azure.cosmos.\")]:\n", + " del sys.modules[name]\n", + "\n", + "_ensure_cosmos_v4()\n", + "from azure.cosmos import CosmosClient\n", + "\n", + "import pandas as pd\n", + "from datetime import datetime, timezone\n", + "\n", + "# Build Cosmos client\n", + "if cosmos_auth_mode.lower() == \"aad\":\n", + " from azure.identity import DefaultAzureCredential\n", + " cosmos_client = CosmosClient(cosmos_account_endpoint, credential=DefaultAzureCredential())\n", + "else:\n", + " if not cosmos_account_key:\n", + " raise ValueError(\"cosmos_account_key is empty but cosmos_auth_mode == 'key'\")\n", + " cosmos_client = CosmosClient(cosmos_account_endpoint, credential=cosmos_account_key)\n", + "\n", + "cosmos_db = cosmos_client.get_database_client(cosmos_database_name)\n", + "cosmos_container = cosmos_db.get_container_client(cosmos_container_name)\n", + "\n", + "# Build query (only PII_Processing docs)\n", + "where_clauses = [\"c.type = 'PII_Processing'\"]\n", + "parameters = []\n", + "if filter_product_id:\n", + " where_clauses.append(\"c.context.product.id = @prodId\")\n", + " parameters.append({\"name\": \"@prodId\", \"value\": filter_product_id})\n", + "if filter_subscription_id:\n", + " where_clauses.append(\"c.context.subscription.id = @subId\")\n", + " parameters.append({\"name\": \"@subId\", \"value\": filter_subscription_id})\n", + "\n", + "query = (\n", + " f\"SELECT TOP {int(top_n_records)} * FROM c \"\n", + " f\"WHERE {' AND '.join(where_clauses)} \"\n", + " f\"ORDER BY c._ts DESC\"\n", + ")\n", + "utils.print_info(f\"Cosmos query: {query}\")\n", + "\n", + "start_time = time.time()\n", + "pii_records = list(cosmos_container.query_items(\n", + " query=query,\n", + " parameters=parameters,\n", + " enable_cross_partition_query=True\n", + "))\n", + "elapsed_time = time.time() - start_time\n", + "utils.print_ok(f\"✅ Retrieved {len(pii_records)} record(s) in {elapsed_time:.2f}s\")\n", + "\n", + "# Flatten into a DataFrame\n", + "def _flatten(rec):\n", + " ctx = rec.get(\"context\", {}) or {}\n", + " api = ctx.get(\"api\", {}) or {}\n", + " product = ctx.get(\"product\", {}) or {}\n", + " sub = ctx.get(\"subscription\", {}) or {}\n", + " process = rec.get(\"process\", {}) or {}\n", + " metrics = rec.get(\"metrics\", {}) or {}\n", + " flow = rec.get(\"processingFlow\", {}) or {}\n", + " deanon = rec.get(\"deanonymization\", {}) or {}\n", + " cats = rec.get(\"entityCategories\", {}) or {}\n", + " return {\n", + " \"id\" : rec.get(\"id\"),\n", + " \"timestamp\" : rec.get(\"timestamp\"),\n", + " \"ts\" : datetime.fromtimestamp(rec.get(\"_ts\", 0), tz=timezone.utc) if rec.get(\"_ts\") else None,\n", + " \"targetDeployment\" : rec.get(\"targetDeployment\"),\n", + " \"api_id\" : api.get(\"id\"),\n", + " \"operation\" : (api.get(\"operation\") or {}).get(\"id\"),\n", + " \"product_id\" : product.get(\"id\"),\n", + " \"subscription_id\" : sub.get(\"id\"),\n", + " \"anonymization_enabled\" : process.get(\"anonymizationEnabled\"),\n", + " \"confidence_threshold\" : process.get(\"confidenceThreshold\"),\n", + " \"entity_exclusions\" : process.get(\"entityCategoryExclusions\"),\n", + " \"deanonymization_performed\": process.get(\"deanonymizationPerformed\"),\n", + " \"entity_count\" : rec.get(\"entityCount\", 0) or 0,\n", + " \"entity_categories\" : cats,\n", + " \"input_len\" : metrics.get(\"inputContentLength\", 0) or 0,\n", + " \"anonymized_len\" : metrics.get(\"anonymizedContentLength\", 0) or 0,\n", + " \"deanon_input_len\" : metrics.get(\"deanonymizeInputLength\", 0) or 0,\n", + " \"deanon_output_len\" : metrics.get(\"deanonymizedOutputLength\", 0) or 0,\n", + " \"anon_performed\" : (flow.get(\"anonymization\") or {}).get(\"performed\", False),\n", + " \"deanon_performed\" : (flow.get(\"deanonymization\") or {}).get(\"performed\", False),\n", + " \"deanon_success\" : (flow.get(\"deanonymization\") or {}).get(\"success\", False),\n", + " \"deanon_status\" : deanon.get(\"status\"),\n", + " \"deanon_input_source\" : deanon.get(\"inputSource\"),\n", + " }\n", + "\n", + "pii_df = pd.DataFrame([_flatten(r) for r in pii_records])\n", + "if not pii_df.empty:\n", + " pii_df[\"ts\"] = pd.to_datetime(pii_df[\"ts\"], utc=True, errors=\"coerce\")\n", + " pii_df.sort_values(\"ts\", inplace=True)\n", + "\n", + "utils.print_info(f\"Loaded DataFrame shape: {pii_df.shape}\")\n", + "pii_df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "3beb1958", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.3 Quantitative Report & Visualizations\n", + "\n", + "Builds a quantitative summary of how the gateway performed on PII detection,\n", + "anonymization, and deanonymization, then renders charts highlighting:\n", + "\n", + "1. Detected entities by category\n", + "2. Anonymization vs. deanonymization performance\n", + "3. Content-length impact (original → anonymized → deanonymized)\n", + "4. Volume of PII processing over time" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26807a3b", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from collections import Counter\n", + "\n", + "pii_analytics_summary = {}\n", + "\n", + "if pii_df.empty:\n", + " utils.print_error(\"No PII processing records returned — skipping analytics.\")\n", + "else:\n", + " # ---------- Quantitative report ----------\n", + " total_records = len(pii_df)\n", + " total_entities = int(pii_df[\"entity_count\"].sum())\n", + " avg_entities = float(pii_df[\"entity_count\"].mean())\n", + " records_with_pii = int((pii_df[\"entity_count\"] > 0).sum())\n", + " anon_performed_rate = float(pii_df[\"anon_performed\"].mean()) * 100 if total_records else 0\n", + " deanon_performed_rate = float(pii_df[\"deanon_performed\"].mean()) * 100 if total_records else 0\n", + " deanon_success_rate = (\n", + " float(pii_df.loc[pii_df[\"deanon_performed\"], \"deanon_success\"].mean()) * 100\n", + " if pii_df[\"deanon_performed\"].any() else 0\n", + " )\n", + "\n", + " # Aggregate entity categories across all records\n", + " category_counter = Counter()\n", + " for cats in pii_df[\"entity_categories\"]:\n", + " if isinstance(cats, dict):\n", + " category_counter.update({k: int(v) for k, v in cats.items()})\n", + "\n", + " # Length deltas\n", + " length_df = pii_df[[\"input_len\", \"anonymized_len\", \"deanon_input_len\", \"deanon_output_len\"]].copy()\n", + " avg_anon_delta_pct = (\n", + " ((length_df[\"anonymized_len\"] - length_df[\"input_len\"]) / length_df[\"input_len\"].replace(0, pd.NA))\n", + " .dropna().mean() * 100\n", + " ) if not length_df.empty else 0\n", + " avg_deanon_delta_pct = (\n", + " ((length_df[\"deanon_output_len\"] - length_df[\"deanon_input_len\"]) / length_df[\"deanon_input_len\"].replace(0, pd.NA))\n", + " .dropna().mean() * 100\n", + " ) if not length_df.empty else 0\n", + "\n", + " pii_analytics_summary = {\n", + " \"total_records\" : total_records,\n", + " \"records_with_pii\" : records_with_pii,\n", + " \"records_without_pii\" : total_records - records_with_pii,\n", + " \"total_entities_detected\" : total_entities,\n", + " \"avg_entities_per_record\" : round(avg_entities, 2),\n", + " \"anonymization_performed_rate_pct\" : round(anon_performed_rate, 2),\n", + " \"deanonymization_performed_rate_pct\": round(deanon_performed_rate, 2),\n", + " \"deanonymization_success_rate_pct\" : round(deanon_success_rate, 2),\n", + " \"avg_content_length_change_anon_pct\" : round(float(avg_anon_delta_pct or 0), 2),\n", + " \"avg_content_length_change_deanon_pct\": round(float(avg_deanon_delta_pct or 0), 2),\n", + " \"entity_category_counts\" : dict(category_counter.most_common()),\n", + " \"top_products\" : pii_df[\"product_id\"].value_counts().head(5).to_dict(),\n", + " \"top_subscriptions\" : pii_df[\"subscription_id\"].value_counts().head(5).to_dict(),\n", + " \"time_window_utc\" : {\n", + " \"from\": str(pii_df[\"ts\"].min()),\n", + " \"to\" : str(pii_df[\"ts\"].max()),\n", + " },\n", + " }\n", + "\n", + " utils.print_info(f\"\\n{'='*80}\")\n", + " utils.print_info(f\"📊 PII PROCESSING QUANTITATIVE REPORT (top {total_records} records)\")\n", + " utils.print_info(f\"{'='*80}\")\n", + " utils.print_info(f\"Records analyzed : {total_records}\")\n", + " utils.print_info(f\"Records containing PII : {records_with_pii} ({records_with_pii/total_records*100:.1f}%)\")\n", + " utils.print_info(f\"Total entities detected : {total_entities}\")\n", + " utils.print_info(f\"Avg entities / record : {avg_entities:.2f}\")\n", + " utils.print_info(f\"Anonymization performed rate : {anon_performed_rate:.1f}%\")\n", + " utils.print_info(f\"Deanonymization performed rate: {deanon_performed_rate:.1f}%\")\n", + " utils.print_ok (f\"Deanonymization success rate : {deanon_success_rate:.1f}%\")\n", + " utils.print_info(f\"Avg content size change (anon) : {pii_analytics_summary['avg_content_length_change_anon_pct']}%\")\n", + " utils.print_info(f\"Avg content size change (deanon) : {pii_analytics_summary['avg_content_length_change_deanon_pct']}%\")\n", + " utils.print_info(f\"Top categories : {dict(category_counter.most_common(5))}\")\n", + "\n", + " # ---------- Visualizations ----------\n", + " fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n", + " fig.suptitle(\"PII Processing Performance — Gateway Telemetry\", fontsize=14, fontweight=\"bold\")\n", + "\n", + " # 1) Entities by category\n", + " ax = axes[0, 0]\n", + " if category_counter:\n", + " cats_sorted = category_counter.most_common()\n", + " ax.barh([c for c, _ in cats_sorted][::-1],\n", + " [v for _, v in cats_sorted][::-1],\n", + " color=\"#4C72B0\")\n", + " ax.set_title(\"Detected entities by category\")\n", + " ax.set_xlabel(\"Count\")\n", + " else:\n", + " ax.text(0.5, 0.5, \"No entity categories detected\", ha=\"center\", va=\"center\")\n", + " ax.set_axis_off()\n", + "\n", + " # 2) Anonymization vs Deanonymization performance\n", + " ax = axes[0, 1]\n", + " perf_labels = [\"Records w/ PII\", \"Anon performed\", \"Deanon performed\", \"Deanon success\"]\n", + " perf_values = [\n", + " records_with_pii,\n", + " int(pii_df[\"anon_performed\"].sum()),\n", + " int(pii_df[\"deanon_performed\"].sum()),\n", + " int(pii_df.loc[pii_df[\"deanon_performed\"], \"deanon_success\"].sum()),\n", + " ]\n", + " bars = ax.bar(perf_labels, perf_values, color=[\"#55A868\", \"#4C72B0\", \"#8172B2\", \"#C44E52\"])\n", + " ax.set_title(\"Anonymization / Deanonymization performance\")\n", + " ax.set_ylabel(\"Records\")\n", + " for b, v in zip(bars, perf_values):\n", + " ax.text(b.get_x() + b.get_width() / 2, b.get_height(), str(v),\n", + " ha=\"center\", va=\"bottom\", fontsize=9)\n", + " ax.tick_params(axis=\"x\", rotation=20)\n", + "\n", + " # 3) Content length transformation (avg)\n", + " ax = axes[1, 0]\n", + " avg_lengths = [\n", + " length_df[\"input_len\"].mean(),\n", + " length_df[\"anonymized_len\"].mean(),\n", + " length_df[\"deanon_input_len\"].mean(),\n", + " length_df[\"deanon_output_len\"].mean(),\n", + " ]\n", + " ax.bar([\"Input\", \"Anonymized\", \"Deanon Input\", \"Deanon Output\"],\n", + " avg_lengths, color=\"#937860\")\n", + " ax.set_title(\"Avg content length through PII pipeline\")\n", + " ax.set_ylabel(\"Characters\")\n", + "\n", + " # 4) Volume over time\n", + " ax = axes[1, 1]\n", + " if pii_df[\"ts\"].notna().any():\n", + " per_minute = (pii_df.set_index(\"ts\")\n", + " .assign(n=1)[\"n\"]\n", + " .resample(\"1min\").sum())\n", + " ax.plot(per_minute.index, per_minute.values, marker=\"o\", color=\"#4C72B0\")\n", + " ax.set_title(\"PII processing volume over time\")\n", + " ax.set_ylabel(\"Records / min\")\n", + " ax.tick_params(axis=\"x\", rotation=30)\n", + " else:\n", + " ax.text(0.5, 0.5, \"No timestamps available\", ha=\"center\", va=\"center\")\n", + " ax.set_axis_off()\n", + "\n", + " plt.tight_layout(rect=[0, 0, 1, 0.96])\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3b31dbf2", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.4 Define & Deploy PII Analytics Access Contract\n", + "\n", + "The previously created **PII Masking** and **PII Blocking** contracts have PII processing\n", + "policies enabled — sending the analytics payload (which itself contains anonymized PII\n", + "samples and entity categories) through them would re-trigger detection, double-mask\n", + "values, and pollute the telemetry we are analyzing.\n", + "\n", + "We therefore provision a **separate access contract** dedicated to PII analytics\n", + "reasoning. It has:\n", + "\n", + "- **No PII processing fragments** in its product policy.\n", + "- **Configurable model-level RBAC** via the `allowed_models` list.\n", + "- **Configurable capacity allocation** via `tokens_per_minute`, `token_quota`,\n", + " and `token_quota_period`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9d6684e", + "metadata": {}, + "outputs": [], + "source": [ + "# ============================================================================\n", + "# 📋 PII ANALYTICS ACCESS CONTRACT — configurable model RBAC + capacity\n", + "# ============================================================================\n", + "# Reuse the timestamp from the earlier contract definitions when available so\n", + "# all three contracts deployed by this notebook share the same suffix.\n", + "try:\n", + " timestamp\n", + "except NameError:\n", + " timestamp = time.strftime('%Y%m%d%H%M%S')\n", + "\n", + "pii_analytics_contract = {\n", + " \"name\": f\"pii-analytics-contract-{timestamp}\",\n", + " \"business_unit\": \"HR\",\n", + " \"use_case_name\": \"PIIAnalytics\",\n", + " \"environment\": \"DEV\",\n", + " \"use_keyvault\": False,\n", + " \"endpoint_secret\": \"HR-PII-LLM-ENDPOINT\",\n", + " \"apikey_secret\": \"HR-PII-LLM-KEY\",\n", + " \"description\": \"HR PII Analytics - LLM reasoning over PII processing telemetry (no PII fragments)\",\n", + "\n", + " # 🔐 Model-level RBAC — list of model names allowed through this contract\n", + " \"allowed_models\": [\"gpt-4.1\"],\n", + "\n", + " # 📊 Capacity allocation\n", + " \"tokens_per_minute\": 20000, # TPM throttle\n", + " \"token_quota\": 500000, # long-term token budget\n", + " \"token_quota_period\": \"Monthly\", # Hourly | Daily | Weekly | Monthly\n", + "}\n", + "\n", + "utils.print_info(\"PII Analytics Contract Configuration:\")\n", + "utils.print_info(f\" Business Unit : {pii_analytics_contract['business_unit']}\")\n", + "utils.print_info(f\" Use Case : {pii_analytics_contract['use_case_name']}\")\n", + "utils.print_info(f\" Allowed Models : {pii_analytics_contract['allowed_models']}\")\n", + "utils.print_info(f\" TPM Limit : {pii_analytics_contract['tokens_per_minute']:,}\")\n", + "utils.print_info(f\" Token Quota : {pii_analytics_contract['token_quota']:,} per {pii_analytics_contract['token_quota_period']}\")\n", + "utils.print_info(f\" Product ID : LLM-{pii_analytics_contract['business_unit']}-{pii_analytics_contract['use_case_name']}-{pii_analytics_contract['environment']}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "7dc7ffaa", + "metadata": {}, + "source": [ + "\n", + "#### 5️⃣.4.2 Build Analytics Product Policy (Model RBAC + Capacity)\n", + "\n", + "The policy uses two well-known APIM fragments shipped with this accelerator:\n", + "\n", + "- `set-llm-requested-model` — extracts the requested model name from the inbound payload.\n", + "- `validate-model-access` — denies the request if the requested model is **not** in the\n", + " `allowedModels` CSV variable.\n", + "\n", + "and the built-in `llm-token-limit` policy to enforce the per-minute and quota limits.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ccdb998", + "metadata": {}, + "outputs": [], + "source": [ + "# ============================================================================\n", + "# 🧱 BUILD PRODUCT POLICY — analytics contract (NO PII fragments)\n", + "# ============================================================================\n", + "import shutil\n", + "\n", + "bicep_dir = \"../bicep/infra/citadel-access-contracts\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "contract = pii_analytics_contract\n", + "folder_name = f\"{contract['business_unit'].lower()}-{contract['use_case_name'].lower()}\"\n", + "environment_folder = contract['environment'].lower()\n", + "analytics_contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + "os.makedirs(analytics_contract_folder, exist_ok=True)\n", + "utils.print_info(f\"📁 Created folder: {analytics_contract_folder}\")\n", + "\n", + "allowed_csv = \",\".join(contract[\"allowed_models\"])\n", + "\n", + "pii_analytics_policy = f'''\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "analytics_policy_file = os.path.join(analytics_contract_folder, \"ai-product-policy.xml\")\n", + "with open(analytics_policy_file, 'w', encoding='utf-8') as f:\n", + " f.write(pii_analytics_policy)\n", + "utils.print_ok(f\"✅ Analytics policy file created: {analytics_policy_file}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b5951e94", + "metadata": {}, + "source": [ + "\n", + "#### 5️⃣.4.3 Generate Parameter File and Deploy Analytics Contract\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f191532", + "metadata": {}, + "outputs": [], + "source": [ + "# ============================================================================\n", + "# 🚀 DEPLOY PII ANALYTICS ACCESS CONTRACT\n", + "# ============================================================================\n", + "analytics_params_file = os.path.join(analytics_contract_folder, \"main.bicepparam\")\n", + "policy_relative_path = \"ai-product-policy.xml\"\n", + "\n", + "analytics_params_content = f'''using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// {contract['description']} - Generated from PII Testing Notebook\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " name: 'placeholder'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = false\n", + "\n", + "param useCase = {{\n", + " businessUnit: '{contract['business_unit']}'\n", + " useCaseName: '{contract['use_case_name']}'\n", + " environment: '{contract['environment']}'\n", + "}}\n", + "\n", + "param apiNameMapping = {{\n", + " LLM: ['universal-llm-api', 'azure-openai-api', 'unified-ai-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: '{contract['endpoint_secret']}'\n", + " apiKeySecretName: '{contract['apikey_secret']}'\n", + " policyXml: loadTextContent('{policy_relative_path}')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'PII Analytics Access Contract - {contract[\"description\"]}'\n", + "\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}}\n", + "'''\n", + "\n", + "with open(analytics_params_file, 'w', encoding='utf-8') as f:\n", + " f.write(analytics_params_content)\n", + "utils.print_ok(f\"✅ Parameter file created: {analytics_params_file}\")\n", + "\n", + "utils.print_info(f\"\\n{'='*60}\")\n", + "utils.print_info(f\"Deploying PII Analytics Access Contract...\")\n", + "utils.print_info(f\"{'='*60}\")\n", + "\n", + "deployment_cmd = (\n", + " f\"az deployment sub create --name {contract['name']} \"\n", + " f\"--location {location} --template-file {template_file} \"\n", + " f\"--parameters {analytics_params_file}\"\n", + ")\n", + "\n", + "analytics_deployment_output = utils.run(\n", + " deployment_cmd,\n", + " f\"Deployment '{contract['name']}' succeeded\",\n", + " f\"Deployment '{contract['name']}' failed\",\n", + ")\n", + "\n", + "if analytics_deployment_output.success:\n", + " utils.print_ok(\"✅ PII Analytics Access Contract deployed successfully!\")\n", + "else:\n", + " utils.print_error(\"❌ PII Analytics Access Contract deployment failed!\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "daf46e9d", + "metadata": {}, + "source": [ + "\n", + "#### 5️⃣.4.4 Retrieve API Key for Analytics Contract\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3899c0c2", + "metadata": {}, + "outputs": [], + "source": [ + "# Re-initialize APIM client to pick up the new subscription\n", + "apimClientTool.initialize()\n", + "\n", + "analytics_product_id = f\"LLM-{pii_analytics_contract['business_unit']}-{pii_analytics_contract['use_case_name']}-{pii_analytics_contract['environment']}\"\n", + "analytics_subscription_name = f\"{analytics_product_id}-SUB-01\"\n", + "analytics_api_key = None\n", + "\n", + "for sub in apimClientTool.apim_subscriptions:\n", + " if analytics_subscription_name.lower() in sub.get('name', '').lower():\n", + " analytics_api_key = sub.get('key')\n", + " utils.print_ok(f\"✅ Found API key for {analytics_product_id}\")\n", + " break\n", + "\n", + "if not analytics_api_key:\n", + " utils.print_error(f\"❌ Could not find API key for {analytics_product_id}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b06d9b39", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣.5 Send Analytics Summary to LLM (via PII Analytics Access Contract)\n", + "\n", + "Hand the quantitative summary to a language model **through the dedicated PII\n", + "Analytics Access Contract** (`analytics_subscription_name`). Because that contract\n", + "has no PII processing fragments, the analytics payload — which contains anonymized\n", + "PII samples and entity categories — passes through unmodified and does not re-trigger\n", + "detection or double-anonymization.\n", + "\n", + "Set `send_to_llm = False` to skip the network call and just inspect the payload.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5510f798", + "metadata": {}, + "outputs": [], + "source": [ + "# Guard: ensure 5.4 PII Analytics contract was deployed and key resolved\n", + "if 'analytics_api_key' not in globals() or not analytics_api_key:\n", + " raise RuntimeError(\n", + " \"PII Analytics contract is not deployed yet. Run section 5.4 first \"\n", + " \"to provision and resolve the analytics contract API key.\"\n", + " )\n", + "\n", + "# Toggle to actually call the LLM\n", + "send_to_llm = True\n", + "\n", + "# Model to reason about the analytics\n", + "analytics_model_name = \"gpt-4.1\"\n", + "\n", + "if pii_df.empty or not pii_analytics_summary:\n", + " utils.print_error(\"No analytics summary to send (DataFrame is empty).\")\n", + "elif not send_to_llm:\n", + " utils.print_info(\"send_to_llm = False — skipping LLM reasoning step.\")\n", + "else:\n", + " # Compact summary payload\n", + " analytics_payload_for_llm = {\n", + " \"summary\" : pii_analytics_summary,\n", + " \"sample_records\" : [\n", + " {\n", + " \"id\" : r[\"id\"],\n", + " \"timestamp\" : r[\"timestamp\"],\n", + " \"product_id\" : r[\"product_id\"],\n", + " \"subscription_id\" : r[\"subscription_id\"],\n", + " \"entity_count\" : r[\"entity_count\"],\n", + " \"entity_categories\" : r[\"entity_categories\"],\n", + " \"anon_performed\" : r[\"anon_performed\"],\n", + " \"deanon_performed\" : r[\"deanon_performed\"],\n", + " \"deanon_success\" : r[\"deanon_success\"],\n", + " \"input_len\" : r[\"input_len\"],\n", + " \"anonymized_len\" : r[\"anonymized_len\"],\n", + " }\n", + " for r in pii_df.head(10).to_dict(orient=\"records\")\n", + " ],\n", + " }\n", + "\n", + " system_prompt = (\n", + " \"You are a senior AI Gateway reliability analyst. You will receive a JSON summary of \"\n", + " \"PII detection / anonymization / deanonymization telemetry from an Azure API Management \"\n", + " \"based AI gateway. Produce a concise report (markdown) that covers:\\n\"\n", + " \"1. Overall detection effectiveness (entities, categories, coverage).\\n\"\n", + " \"2. Anonymization & deanonymization success/failure patterns.\\n\"\n", + " \"3. Content-size impact of the PII pipeline.\\n\"\n", + " \"4. Notable per-product / per-subscription observations.\\n\"\n", + " \"5. Concrete recommendations to improve detection quality, reduce false positives, \"\n", + " \"or tune the confidence threshold / entity exclusions.\\n\"\n", + " \"Be quantitative and reference the numbers in the input.\"\n", + " )\n", + "\n", + " user_prompt = (\n", + " \"Here is the latest PII processing telemetry summary from the gateway. \"\n", + " \"Please analyze the gateway's PII detection performance.\\n\\n\"\n", + " f\"```json\\n{json.dumps(analytics_payload_for_llm, indent=2, default=str)}\\n```\"\n", + " )\n", + "\n", + " llm_payload = {\n", + " \"model\": analytics_model_name,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": system_prompt},\n", + " {\"role\": \"user\", \"content\": user_prompt},\n", + " ],\n", + " \"temperature\": 0.2,\n", + " }\n", + "\n", + " utils.print_info(f\"Sending PII analytics summary to LLM via PII Analytics contract: {analytics_subscription_name}\")\n", + " try:\n", + " start_time = time.time()\n", + " response = requests.post(\n", + " chat_completions_url,\n", + " headers={\"api-key\": analytics_api_key},\n", + " json=llm_payload,\n", + " timeout=120,\n", + " )\n", + " elapsed_time = time.time() - start_time\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = response.json()\n", + " llm_analysis = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " utils.print_ok(f\"✅ LLM analysis received in {elapsed_time:.2f}s\\n\")\n", + " try:\n", + " from IPython.display import Markdown, display\n", + " display(Markdown(llm_analysis))\n", + " except Exception:\n", + " print(llm_analysis)\n", + " else:\n", + " utils.print_error(f\"❌ LLM call failed: {response.text[:500]}\")\n", + " except Exception as e:\n", + " utils.print_error(f\"❌ Request to LLM failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "645ecde8", + "metadata": {}, + "source": [ + "---\n", + "## 📊 Results Summary\n", + "---" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d439bd44", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"\\n{'='*80}\")\n", + "utils.print_info(f\"📊 OVERALL PII PROCESSING TEST SUMMARY\")\n", + "utils.print_info(f\"{'='*80}\")\n", + "\n", + "# PII Masking Summary\n", + "utils.print_info(f\"\\n🛡️ PII Masking (Anonymization/Deanonymization):\")\n", + "masking_success = sum(1 for r in masking_test_results if r['success'])\n", + "utils.print_info(f\" Product: {masking_product_id}\")\n", + "utils.print_info(f\" Tests: {len(masking_test_results)} | Successful: {masking_success} | Failed: {len(masking_test_results) - masking_success}\")\n", + "\n", + "# PII Blocking Summary\n", + "utils.print_info(f\"\\n🚫 PII Blocking:\")\n", + "blocking_passed = sum(1 for r in blocking_test_results if r['test_passed'])\n", + "utils.print_info(f\" Product: {blocking_product_id}\")\n", + "utils.print_info(f\" Tests: {len(blocking_test_results)} | Passed: {blocking_passed} | Failed: {len(blocking_test_results) - blocking_passed}\")\n", + "\n", + "# Overall\n", + "total_all = len(masking_test_results) + len(blocking_test_results)\n", + "passed_all = masking_success + blocking_passed\n", + "utils.print_info(f\"\\n📈 Overall Results:\")\n", + "utils.print_info(f\" Total tests executed: {total_all}\")\n", + "if passed_all == total_all:\n", + " utils.print_ok(f\" ✅ All tests passed! ({passed_all}/{total_all})\")\n", + "else:\n", + " utils.print_info(f\" Passed: {passed_all}/{total_all}\")\n", + " utils.print_error(f\" Failed: {total_all - passed_all}/{total_all}\")" + ] + }, + { + "cell_type": "markdown", + "id": "0ba45523", + "metadata": {}, + "source": [ + "\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "Remove the test PII access contracts from APIM created during this notebook session.\n", + "\n", + "> **Note:** This will not delete any created secrets in Azure Key Vault." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17d244a1", + "metadata": {}, + "outputs": [], + "source": [ + "# Set to True to delete the PII access contracts created in this session\n", + "cleanup_enabled = False\n", + "\n", + "if cleanup_enabled:\n", + " contracts_to_cleanup = [\n", + " (masking_product_id, \"PII Masking\"),\n", + " (blocking_product_id, \"PII Blocking\")\n", + " ]\n", + " \n", + " for product_id, description in contracts_to_cleanup:\n", + " utils.print_info(f\"Deleting {description} product: {product_id}...\")\n", + " \n", + " # Delete product and its associated subscriptions\n", + " prod_cmd = f\"az apim product delete --resource-group {governance_hub_resource_group} --service-name {apimClientTool.apim_resource_name} --product-id {product_id} --delete-subscriptions true --yes\"\n", + " utils.run(prod_cmd, f\"Deleted product {product_id}\", f\"Failed to delete product {product_id}\")\n", + " \n", + " # Optionally clean up the generated contract folders\n", + " import shutil\n", + " for folder in [masking_contract_folder, blocking_contract_folder]:\n", + " if os.path.exists(folder):\n", + " shutil.rmtree(folder)\n", + " utils.print_info(f\"Removed folder: {folder}\")\n", + " \n", + " utils.print_ok(\"🧹 Cleanup completed!\")\n", + "else:\n", + " utils.print_info(\"Cleanup is disabled. Set cleanup_enabled = True to remove test resources.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/6. citadel-unified-ai-api-tests.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/6. citadel-unified-ai-api-tests.ipynb new file mode 100644 index 000000000..362d84874 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/6. citadel-unified-ai-api-tests.ipynb @@ -0,0 +1,1299 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6c585b3b", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🔀 Unified AI API - Validation Tests\n", + "\n", + "## Overview\n", + "\n", + "Validate the Unified AI Wildcard API (`/unified-ai`) across different model providers and API patterns.\n", + "\n", + "This notebook tests:\n", + "- **Access Contract Provisioning**: Deploy an access contract via Bicep to obtain API credentials\n", + "- **Model & Deployment Discovery**: List available models, get deployment by name, and validate 404 for non-existent models\n", + "- **Azure OpenAI Pattern**: `/unified-ai/openai/deployments/{model}/chat/completions`\n", + "- **Inference Pattern (Foundry)**: `/unified-ai/models/chat/completions`\n", + "- **Responses API Pattern**: `/unified-ai/openai/responses` (if supported)\n", + "- **Gemini OpenAI Pattern**: `/unified-ai/v1beta/openai/chat/completions`\n", + "- **Authentication Modes**: API Key validation (valid, missing, invalid) and cross-product isolation\n", + "- **Load Testing**: Concurrent requests with throttling visualization\n", + "- **UAIG Debug Headers**: Validate all `UAIG-*` response headers from gateway fragments\n", + "- **Blocked Path Rejection**: Verify unrecognized paths return 403 PathNotAllowed\n", + "- **Model Access Control**: Verify `allowedModels` enforcement from product policy\n", + "- **Streaming Requests**: Validate `stream: true` detection and SSE response handling\n", + "\n", + "> **Prerequisites:** An existing Citadel Governance Hub deployment with the Unified AI API enabled.\n", + "\n", + "> **Note:** For more information on Unified AI Gateway Design Pattern, look at https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management---unified-ai-gateway-design-pattern/4495436 and https://github.com/Azure-Samples/APIM-Unified-AI-Gateway-Sample" + ] + }, + { + "cell_type": "markdown", + "id": "b48bb2e5", + "metadata": {}, + "source": [ + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Configure the following variables according to your environment before running the notebook:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d3ef32a", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5ea54ca", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "from apimtools import APIMClientTool\n", + "\n", + "# ============================================================================\n", + "# 🔧 GOVERNANCE HUB CONFIGURATION (REQUIRED)\n", + "# ============================================================================\n", + "governance_hub_resource_group = env_governance_hub_resource_group # Resource group of the deployed Citadel Governance Hub\n", + "location = env_location # Azure region (e.g. \"swedencentral\", \"eastus\")\n", + "\n", + "# ============================================================================\n", + "# 🔧 API VERSION CONFIGURATION\n", + "# ============================================================================\n", + "inference_api_version = \"2024-05-01-preview\"\n", + "openai_api_version = \"2024-12-01-preview\"\n", + "targetInferenceApi = \"models\" # 'models' = Universal LLM API | 'openai' = Azure OpenAI API\n", + "\n", + "# ============================================================================\n", + "# 🧠 MODEL CONFIGURATION (REPLACE based on your configured backends)\n", + "# ============================================================================\n", + "openai_model = \"gpt-5.2\" # Azure OpenAI model\n", + "foundry_inference_model = \"Mistral-Large-3\" # AI Foundry inference model\n", + "gemini_model = \"gemini-2.5-flash-lite\" # Gemini model (if Gemini backend configured)\n", + "\n", + "# Feature toggles\n", + "test_responses_api = True # Set True if your backend supports the Responses API\n", + "test_gemini = False # Set True if a Gemini backend is configured\n", + "\n", + "# ============================================================================\n", + "# 📜 ACCESS CONTRACT CONFIGURATION (created on the fly by this notebook)\n", + "# ============================================================================\n", + "contract_business_unit = \"Testing\"\n", + "contract_use_case_name = \"UnifiedAI\"\n", + "contract_environment = \"DEV\"\n", + "\n", + "# ============================================================================\n", + "# 🔐 KEY VAULT / 🤖 FOUNDRY (this notebook does not require either)\n", + "# ============================================================================\n", + "use_keyvault_integration = False\n", + "use_foundry_integration = False\n", + "\n", + "# Need shutil for the dynamic-policy generation cell below\n", + "import shutil\n", + "\n", + "\n", + "utils.print_ok(\"Notebook variables initialized!\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "fb81ccd4", + "metadata": {}, + "source": [ + "### 1️⃣ Verify Azure CLI and Connected Subscription" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "692240e8", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "b5c8f064", + "metadata": {}, + "source": [ + "### 2️⃣ Initialize APIM Client Tool\n", + "\n", + "Discover the Unified AI API and retrieve gateway configuration:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c7c060f", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " apimClientTool = APIMClientTool(governance_hub_resource_group)\n", + " apimClientTool.initialize()\n", + " apimClientTool.discover_api(\"unified-ai\")\n", + "\n", + " apim_resource_gateway_url = str(apimClientTool.apim_resource_gateway_url)\n", + " azure_endpoint = str(apimClientTool.azure_endpoint)\n", + "\n", + " # Get supported models from the policy fragment\n", + " supported_models = apimClientTool.get_policy_fragment_supported_models(\"set-backend-pools\")\n", + " utils.print_info(f\"Supported models: {supported_models}\")\n", + "\n", + " utils.print_ok(f\"Testing tool initialized successfully!\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Error initializing APIM Client Tool: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "790f0e3b", + "metadata": {}, + "source": [ + "### 3️⃣ Provision Access Contract\n", + "\n", + "Deploy an access contract via Bicep to create an APIM product and subscription for testing the Unified AI API.\n", + "This grants API credentials scoped to the unified-ai-api." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "494449c2", + "metadata": {}, + "outputs": [], + "source": [ + "bicep_dir = \"../bicep/infra/citadel-access-contracts\"\n", + "template_file = os.path.join(bicep_dir, \"main.bicep\")\n", + "\n", + "timestamp = time.strftime('%Y%m%d%H%M%S')\n", + "contract_name = f\"unified-ai-test-{timestamp}\"\n", + "\n", + "# Create folder structure: contracts/[businessunit-usecase]/[environment]/\n", + "folder_name = f\"{contract_business_unit.lower()}-{contract_use_case_name.lower()}\"\n", + "environment_folder = contract_environment.lower()\n", + "contract_folder = os.path.join(bicep_dir, \"contracts\", folder_name, environment_folder)\n", + "os.makedirs(contract_folder, exist_ok=True)\n", + "utils.print_info(f\"📁 Created folder: {contract_folder}\")\n", + "\n", + "# Build allowedModels from the notebook model variables\n", + "allowed_models_list = [openai_model, foundry_inference_model]\n", + "if test_gemini:\n", + " allowed_models_list.append(gemini_model)\n", + "allowed_models_csv = \",\".join(allowed_models_list)\n", + "utils.print_info(f\"Allowed models for access contract: {allowed_models_csv}\")\n", + "\n", + "# Generate custom product policy with allowedModels scoped to the test models\n", + "product_policy = f'''\n", + " \n", + " \n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "'''\n", + "\n", + "# Write the custom policy file\n", + "policy_file_dest = os.path.join(contract_folder, \"ai-product-policy.xml\")\n", + "with open(policy_file_dest, 'w') as f:\n", + " f.write(product_policy)\n", + "utils.print_ok(f\"📋 Custom policy file created: {policy_file_dest}\")\n", + "\n", + "params_file = os.path.join(contract_folder, \"main.bicepparam\")\n", + "\n", + "params_content = f\"\"\"using '../../../main.bicep'\n", + "\n", + "// ============================================================================\n", + "// Unified AI API Test Contract - Generated from Notebook\n", + "// ============================================================================\n", + "\n", + "param apim = {{\n", + " subscriptionId: '{subscription_id}'\n", + " resourceGroupName: '{governance_hub_resource_group}'\n", + " name: '{apimClientTool.apim_resource_name}'\n", + "}}\n", + "\n", + "param keyVault = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " name: 'placeholder'\n", + "}}\n", + "\n", + "param useTargetAzureKeyVault = false\n", + "\n", + "param useCase = {{\n", + " businessUnit: '{contract_business_unit}'\n", + " useCaseName: '{contract_use_case_name}'\n", + " environment: '{contract_environment}'\n", + "}}\n", + "\n", + "param apiNameMapping = {{\n", + " LLM: ['unified-ai-api']\n", + "}}\n", + "\n", + "param services = [\n", + " {{\n", + " code: 'LLM'\n", + " endpointSecretName: 'UNIFIED-AI-TEST-ENDPOINT'\n", + " apiKeySecretName: 'UNIFIED-AI-TEST-KEY'\n", + " policyXml: loadTextContent('ai-product-policy.xml')\n", + " }}\n", + "]\n", + "\n", + "param productTerms = 'Unified AI API test contract - generated from validation notebook'\n", + "\n", + "// Azure AI Foundry Integration (disabled)\n", + "param useTargetFoundry = false\n", + "\n", + "param foundry = {{\n", + " subscriptionId: '00000000-0000-0000-0000-000000000000'\n", + " resourceGroupName: 'placeholder'\n", + " accountName: 'placeholder'\n", + " projectName: 'placeholder'\n", + "}}\n", + "\"\"\"\n", + "\n", + "with open(params_file, 'w') as f:\n", + " f.write(params_content)\n", + "utils.print_ok(f\"✅ Parameter file created: {params_file}\")\n", + "\n", + "# Deploy the access contract\n", + "product_id = f\"LLM-{contract_business_unit}-{contract_use_case_name}-{contract_environment}\"\n", + "utils.print_info(f\"Deploying access contract: {contract_name} (Product: {product_id})...\")\n", + "\n", + "deployment_cmd = f\"az deployment sub create --name {contract_name} --location {location} --template-file {template_file} --parameters {params_file}\"\n", + "output = utils.run(deployment_cmd, f\"Deployment '{contract_name}' succeeded\", f\"Deployment '{contract_name}' failed\")\n", + "\n", + "if output.success:\n", + " utils.print_ok(f\"✅ Access contract deployed! Product ID: {product_id}\")\n", + "else:\n", + " utils.print_error(f\"❌ Access contract deployment failed!\")\n", + "\n", + "# Re-initialize APIM client to pick up the new subscription\n", + "apimClientTool.initialize()" + ] + }, + { + "cell_type": "markdown", + "id": "a3242d84", + "metadata": {}, + "source": [ + "### 4️⃣ Retrieve API Key from Access Contract\n", + "\n", + "Get the subscription key created by the access contract deployment:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "364e08ae", + "metadata": {}, + "outputs": [], + "source": [ + "subscription_name = f\"{product_id}-SUB-01\"\n", + "\n", + "api_key = None\n", + "for sub in apimClientTool.apim_subscriptions:\n", + " if subscription_name.lower() in sub.get('name', '').lower():\n", + " api_key = sub.get('key')\n", + " utils.print_ok(f\"Found API key from subscription: {sub.get('name')}\")\n", + " break\n", + "\n", + "if not api_key:\n", + " raise Exception(f\"No API key found for subscription '{subscription_name}'. Ensure the access contract was deployed successfully.\")\n", + "\n", + "# Build endpoint URLs for different API patterns\n", + "base_url = azure_endpoint.rstrip('/')\n", + "openai_url = f\"{base_url}/unified-ai/openai/deployments/{openai_model}/chat/completions?api-version={openai_api_version}\"\n", + "inference_url = f\"{base_url}/unified-ai/models/chat/completions?api-version={inference_api_version}\"\n", + "gemini_url = f\"{base_url}/unified-ai/v1beta/openai/chat/completions\"\n", + "deployments_url = f\"{base_url}/unified-ai/deployments\"\n", + "\n", + "utils.print_info(f\"OpenAI endpoint: {openai_url}\")\n", + "utils.print_info(f\"Inference endpoint: {inference_url}\")\n", + "utils.print_info(f\"Deployments endpoint: {deployments_url}\")\n", + "utils.print_ok(f\"API key and endpoints configured successfully!\")" + ] + }, + { + "cell_type": "markdown", + "id": "2253d0fb", + "metadata": {}, + "source": [ + "### 5️⃣ Discover Available Models & Deployment Discovery\n", + "\n", + "List available models through the Unified AI API, validate `GET /unified-ai/deployments` listing, and test `GET /unified-ai/deployments/{name}` lookup:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f2c3ba0", + "metadata": {}, + "outputs": [], + "source": [ + "## Part A: List all available deployments\n", + "utils.print_info(\"Listing all available deployments via the Unified AI API...\")\n", + "\n", + "try:\n", + " response = requests.get(\n", + " deployments_url,\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " available_deployments = data.get(\"value\", [])\n", + " utils.print_ok(f\"Found {len(available_deployments)} allowed models per access contract:\")\n", + " for d in available_deployments:\n", + " name = d.get('name', 'unknown')\n", + " model_format = d.get('properties', {}).get('model', {}).get('format', 'N/A')\n", + " utils.print_info(f\" • {name} (format: {model_format})\")\n", + " else:\n", + " utils.print_error(f\"Error listing deployments: {response.text[:300]}\")\n", + " available_deployments = []\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + " available_deployments = []\n", + "\n", + "# Cross-reference with policy-supported models\n", + "utils.print_info(f\"\\nGateway all supported models: {supported_models}\")\n", + "deployment_names = [d.get('name', '') for d in available_deployments]\n", + "matched_models = [m for m in supported_models if m in deployment_names]\n", + "utils.print_ok(f\"Models available for testing: {matched_models if matched_models else supported_models}\")\n", + "\n", + "## Part B: Get specific deployment by name (should return 200)\n", + "utils.print_info(f\"\\nGetting deployment: {openai_model}\")\n", + "\n", + "try:\n", + " response = requests.get(\n", + " f\"{deployments_url}/{openai_model}\",\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " utils.print_ok(f\"Found deployment: {data.get('name', 'unknown')}\")\n", + " else:\n", + " utils.print_error(f\"Unexpected: {response.text[:200]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "\n", + "## Part C: Get non-existent deployment (should return 404)\n", + "utils.print_info(f\"Getting non-existent deployment: nonexistent-model-xyz\")\n", + "\n", + "try:\n", + " response = requests.get(\n", + " f\"{deployments_url}/nonexistent-model-xyz\",\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code == 404:\n", + " utils.print_ok(f\"Correctly returned 404 for non-existent model\")\n", + " else:\n", + " utils.print_error(f\"Unexpected status {response.status_code}: {response.text[:200]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "08264e66", + "metadata": {}, + "source": [ + "---\n", + "## Test 1 — Azure OpenAI Pattern\n", + "\n", + "Route through the OpenAI-compatible path: `POST /unified-ai/openai/deployments/{model}/chat/completions`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "098f39e3", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"Testing Azure OpenAI pattern with model: {openai_model}\")\n", + "\n", + "messages = {\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"What is 2+2? Answer in one word.\"}\n", + " ]\n", + "}\n", + "\n", + "try:\n", + " response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': api_key},\n", + " json=messages,\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " utils.print_ok(f\"💬 Response: {content}\")\n", + " utils.print_info(f\" Region: {response.headers.get('x-ms-region', 'N/A')}\")\n", + " utils.print_info(f\" Backend: {response.headers.get('UAIG-Backend', 'N/A')}\")\n", + " utils.print_info(f\" API Type: {response.headers.get('UAIG-API-Type', 'N/A')}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "fa8f4a26", + "metadata": {}, + "source": [ + "## Test 2 — Inference Pattern (Foundry Models)\n", + "\n", + "Route through the inference path: `POST /unified-ai/models/chat/completions`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c7d03a5", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"Testing Foundry Inference with model: {foundry_inference_model}\")\n", + "\n", + "inference_messages = {\n", + " \"model\": foundry_inference_model,\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant. Keep responses brief.\"},\n", + " {\"role\": \"user\", \"content\": \"What is the capital of France? Answer in one word.\"}\n", + " ]\n", + "}\n", + "\n", + "try:\n", + " response = requests.post(\n", + " inference_url,\n", + " headers={'api-key': api_key},\n", + " json=inference_messages,\n", + " timeout=60\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " utils.print_ok(f\"💬 Response: {content[:200]}\")\n", + " utils.print_info(f\" Backend: {response.headers.get('UAIG-Backend', 'N/A')}\")\n", + " utils.print_info(f\" API Type: {response.headers.get('UAIG-API-Type', 'N/A')}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d794c406", + "metadata": {}, + "source": [ + "## Test 3 — Responses API Pattern (if supported)\n", + "\n", + "Test the Responses API routing: `POST /unified-ai/openai/responses`.\n", + "This validates the `responses` api-type detection by the `request-processor` and the `path-builder` constructing the correct backend path.\n", + "\n", + "> **Note:** This test requires a model that supports the Responses API. Set `test_responses_api` to `True` if your backend supports it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c0cfddc", + "metadata": {}, + "outputs": [], + "source": [ + "responses_api_model = openai_model # Model to use for Responses API testing\n", + "\n", + "if test_responses_api:\n", + " responses_url = f\"{azure_endpoint.rstrip('/')}/unified-ai/openai/responses\"\n", + " utils.print_info(f\"Testing Responses API with model: {responses_api_model}\")\n", + "\n", + " responses_payload = {\n", + " \"model\": responses_api_model,\n", + " \"input\": \"What is 2+2? Answer in one word.\"\n", + " }\n", + "\n", + " try:\n", + " response = requests.post(\n", + " responses_url,\n", + " headers={'api-key': api_key},\n", + " json=responses_payload,\n", + " timeout=60\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " response_id = data.get(\"id\", \"\")\n", + " utils.print_ok(f\" ✅ Responses API succeeded\")\n", + " utils.print_info(f\" Response ID: {response_id}\")\n", + "\n", + " # Verify api-type was detected as 'responses'\n", + " api_type = response.headers.get(\"UAIG-API-Type\", \"not-set\")\n", + " if api_type == \"responses\":\n", + " utils.print_ok(f\" ✅ API Type correctly detected as 'responses'\")\n", + " else:\n", + " utils.print_error(f\" ❌ Expected API Type 'responses', got '{api_type}'\")\n", + "\n", + " utils.print_info(f\" Backend: {response.headers.get('UAIG-Backend', 'N/A')}\")\n", + " utils.print_info(f\" Final Path: {response.headers.get('UAIG-Final-Path', 'N/A')}\")\n", + "\n", + " # Test GET response by ID (if we got an ID back)\n", + " if response_id:\n", + " utils.print_info(f\" Retrieving response by ID: {response_id}\")\n", + " get_response = requests.get(\n", + " f\"{responses_url}/{response_id}\",\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + " if get_response.status_code == 200:\n", + " utils.print_ok(f\" ✅ GET response by ID succeeded: {get_response.status_code}\")\n", + " else:\n", + " utils.print_info(f\" ℹ️ GET response by ID returned: {get_response.status_code}\")\n", + "\n", + " # Test DELETE response by ID\n", + " utils.print_info(f\" Deleting response by ID: {response_id}\")\n", + " del_response = requests.delete(\n", + " f\"{responses_url}/{response_id}\",\n", + " headers={'api-key': api_key},\n", + " timeout=30\n", + " )\n", + " utils.print_info(f\" ℹ️ DELETE response returned: {del_response.status_code}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:300]}\")\n", + " except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "else:\n", + " utils.print_info(\"Skipping Responses API test (test_responses_api = False)\")" + ] + }, + { + "cell_type": "markdown", + "id": "e705f047", + "metadata": {}, + "source": [ + "## Test 4 — Gemini OpenAI Pattern (if configured)\n", + "\n", + "Route through the Gemini-compatible path: `POST /unified-ai/v1beta/openai/chat/completions`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4068484d", + "metadata": {}, + "outputs": [], + "source": [ + "if test_gemini:\n", + " utils.print_info(f\"Testing Gemini OpenAI pattern with model: {gemini_model}\")\n", + "\n", + " gemini_messages = {\n", + " \"model\": gemini_model,\n", + " \"messages\": [\n", + " {\"role\": \"user\", \"content\": \"What is the speed of light? Answer briefly.\"}\n", + " ]\n", + " }\n", + "\n", + " try:\n", + " response = requests.post(\n", + " gemini_url,\n", + " headers={'api-key': api_key},\n", + " json=gemini_messages,\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " content = data.get(\"choices\", [{}])[0].get(\"message\", {}).get(\"content\", \"\")\n", + " utils.print_ok(f\"💬 Gemini Response: {content[:200]}\")\n", + " utils.print_info(f\" Backend: {response.headers.get('UAIG-Backend', 'N/A')}\")\n", + " utils.print_info(f\" API Type: {response.headers.get('UAIG-API-Type', 'N/A')}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:300]}\")\n", + " except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "else:\n", + " utils.print_info(\"Skipping Gemini test (test_gemini = False)\")" + ] + }, + { + "cell_type": "markdown", + "id": "12089a88", + "metadata": {}, + "source": [ + "## Test 5 — API Key Authentication\n", + "\n", + "Validate that API Key authentication works and missing keys are rejected:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8347a6fa", + "metadata": {}, + "outputs": [], + "source": [ + "# Test: Valid API Key -> should succeed\n", + "utils.print_info(\"Testing with valid API key...\")\n", + "\n", + "response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': api_key},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + ")\n", + "if response.status_code == 200:\n", + " utils.print_ok(f\"✅ Valid API key: {response.status_code} (expected 200)\")\n", + "else:\n", + " utils.print_error(f\"❌ Valid API key: {response.status_code} (expected 200)\")\n", + "\n", + "# Test: Missing API Key -> should return 401\n", + "utils.print_info(\"Testing without API key...\")\n", + "\n", + "response = requests.post(\n", + " openai_url,\n", + " headers={},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + ")\n", + "if response.status_code == 401:\n", + " utils.print_ok(f\"✅ Missing API key: {response.status_code} (expected 401)\")\n", + "else:\n", + " utils.print_error(f\"❌ Missing API key: {response.status_code} (expected 401)\")" + ] + }, + { + "cell_type": "markdown", + "id": "b8103fa1", + "metadata": {}, + "source": [ + "## Test 6 — Load Test & Throttling\n", + "\n", + "Send multiple requests over 30 seconds to observe rate limiting behavior:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1257470", + "metadata": {}, + "outputs": [], + "source": [ + "test_duration = 30\n", + "api_runs = []\n", + "\n", + "test_model = openai_model\n", + "test_url = openai_url\n", + "utils.print_info(f\"Load testing with model: {test_model} for {test_duration} seconds\")\n", + "\n", + "messages = {\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n", + " {\"role\": \"user\", \"content\": \"Count from 1 to 5.\"}\n", + " ]\n", + "}\n", + "\n", + "start_time = time.time()\n", + "run_count = 0\n", + "\n", + "while (time.time() - start_time) < test_duration:\n", + " run_count += 1\n", + " call_start = time.time()\n", + "\n", + " try:\n", + " response = requests.post(\n", + " test_url,\n", + " headers={'api-key': api_key},\n", + " json=messages,\n", + " timeout=30\n", + " )\n", + "\n", + " elapsed = time.time() - start_time\n", + " total_tokens = 0\n", + " if response.status_code == 200:\n", + " data = json.loads(response.text)\n", + " total_tokens = data.get(\"usage\", {}).get(\"total_tokens\", 0)\n", + "\n", + " api_runs.append((call_start, total_tokens, response.status_code, elapsed))\n", + " except Exception as e:\n", + " api_runs.append((call_start, 0, 500, time.time() - start_time))\n", + "\n", + " time.sleep(0.2)\n", + "\n", + "success = sum(1 for r in api_runs if r[2] == 200)\n", + "throttled = sum(1 for r in api_runs if r[2] == 429)\n", + "errors = sum(1 for r in api_runs if r[2] not in [200, 429])\n", + "\n", + "utils.print_ok(f\"Load test complete: {len(api_runs)} calls\")\n", + "utils.print_info(f\" ✅ Success: {success} | ⛔ Throttled: {throttled} | ❌ Errors: {errors}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f63bf3e", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from matplotlib.patches import Patch\n", + "from matplotlib.lines import Line2D\n", + "\n", + "if api_runs:\n", + " fig, ax = plt.subplots(1, 1, figsize=(14, 5))\n", + "\n", + " times = [r[3] for r in api_runs]\n", + " tokens = [r[1] for r in api_runs]\n", + " status_codes = [r[2] for r in api_runs]\n", + "\n", + " colors = [\n", + " 'tab:green' if code == 200\n", + " else 'tab:red' if code == 429\n", + " else 'tab:orange'\n", + " for code in status_codes\n", + " ]\n", + "\n", + " ax.bar(times, tokens, color=colors, width=0.3, alpha=0.7)\n", + "\n", + " throttled_times = [t for t, code in zip(times, status_codes) if code == 429]\n", + " if throttled_times:\n", + " max_tokens = max(tokens) if tokens else 1\n", + " ax.scatter(throttled_times, [max_tokens * 0.05] * len(throttled_times),\n", + " marker='x', s=50, color='darkred', zorder=5)\n", + "\n", + " total_tokens = sum(tokens)\n", + " stats_text = f\"Total: {len(api_runs)} calls | Success: {success} | Throttled: {throttled} | Tokens: {total_tokens}\"\n", + " ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, fontsize=9,\n", + " verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))\n", + "\n", + " ax.set_title('Unified AI API - Load Test Results', fontsize=12, fontweight='bold')\n", + " ax.set_xlabel('Time (seconds)')\n", + " ax.set_ylabel('Tokens per call')\n", + "\n", + " legend_items = [\n", + " Patch(facecolor='tab:green', alpha=0.7, label='Success (200)'),\n", + " Patch(facecolor='tab:red', alpha=0.7, label='Throttled (429)'),\n", + " Patch(facecolor='tab:orange', alpha=0.7, label='Error'),\n", + " ]\n", + " ax.legend(handles=legend_items, loc='upper right')\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", + "else:\n", + " print('No data to visualize. Run the load test first.')" + ] + }, + { + "cell_type": "markdown", + "id": "a27586d6", + "metadata": {}, + "source": [ + "## Test 7 — UAIG Debug Response Headers\n", + "\n", + "Validate that all `UAIG-*` debug response headers are populated when `enableResponseHeaders` is enabled in the product policy.\n", + "These headers expose internal gateway state for debugging and observability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e456c1", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"Testing UAIG debug headers with model: {openai_model}\")\n", + "\n", + "# Expected UAIG headers set by the Unified AI API fragments\n", + "expected_headers = [\n", + " \"UAIG-Auth-Type\", # From security-handler\n", + " \"UAIG-User-Id\", # From security-handler\n", + " \"UAIG-Subscription\", # From security-handler\n", + " \"UAIG-Model-Id\", # From request-processor\n", + " \"UAIG-API-Type\", # From request-processor\n", + " \"UAIG-Processed-Path\", # From request-processor\n", + " \"UAIG-API-Version\", # From request-processor\n", + " \"UAIG-Is-Streaming\", # From request-processor\n", + " \"UAIG-Backend\", # From set-target-backend-pool\n", + " \"UAIG-Final-Path\", # From path-builder\n", + " \"UAIG-Cache-Operation\", # From central-cache-manager\n", + "]\n", + "\n", + "try:\n", + " response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': api_key},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " all_present = True\n", + " for header in expected_headers:\n", + " value = response.headers.get(header, None)\n", + " if value and value != \"not-set\":\n", + " utils.print_ok(f\" ✅ {header}: {value}\")\n", + " elif value == \"not-set\":\n", + " utils.print_error(f\" ⚠️ {header}: not-set (fragment may not have executed)\")\n", + " all_present = False\n", + " else:\n", + " utils.print_error(f\" ❌ {header}: MISSING\")\n", + " all_present = False\n", + "\n", + " if all_present:\n", + " utils.print_ok(\"All UAIG debug headers are present and populated!\")\n", + " else:\n", + " utils.print_error(\"Some UAIG headers are missing or not set\")\n", + "\n", + " # Validate expected values for known fields\n", + " api_type = response.headers.get(\"UAIG-API-Type\", \"\")\n", + " if api_type == \"openai\":\n", + " utils.print_ok(f\" ✅ API Type correctly detected as 'openai'\")\n", + " else:\n", + " utils.print_error(f\" ❌ Expected API Type 'openai', got '{api_type}'\")\n", + "\n", + " is_streaming = response.headers.get(\"UAIG-Is-Streaming\", \"\")\n", + " if is_streaming == \"false\":\n", + " utils.print_ok(f\" ✅ Is-Streaming correctly set to 'false'\")\n", + " else:\n", + " utils.print_error(f\" ❌ Expected Is-Streaming 'false', got '{is_streaming}'\")\n", + "\n", + " cache_op = response.headers.get(\"UAIG-Cache-Operation\", \"\")\n", + " if cache_op in [\"cache-hit\", \"cache-miss\"]:\n", + " utils.print_ok(f\" ✅ Cache-Operation is valid: '{cache_op}'\")\n", + " else:\n", + " utils.print_error(f\" ❌ Unexpected Cache-Operation: '{cache_op}'\")\n", + " else:\n", + " utils.print_error(f\"Request failed with status {response.status_code}: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "4f1cad33", + "metadata": {}, + "source": [ + "## Test 8 — Blocked Path Rejection\n", + "\n", + "Validate that the `request-processor` fragment rejects requests to paths that don't match any configured API type base-path.\n", + "The expected response is `403 Forbidden` with error code `PathNotAllowed`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf1de7fc", + "metadata": {}, + "outputs": [], + "source": [ + "base_url_clean = azure_endpoint.rstrip('/')\n", + "\n", + "# Test paths that don't match any configured api-type base-path\n", + "blocked_paths = [\n", + " \"/unified-ai/invalid-path/chat/completions\",\n", + " \"/unified-ai/some/random/endpoint\",\n", + " \"/unified-ai/v2/openai/chat/completions\",\n", + "]\n", + "\n", + "for path in blocked_paths:\n", + " url = f\"{base_url_clean}{path}\"\n", + " utils.print_info(f\"Testing blocked path: {path}\")\n", + "\n", + " try:\n", + " response = requests.post(\n", + " url,\n", + " headers={'api-key': api_key},\n", + " json={\"model\": openai_model, \"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code == 403:\n", + " try:\n", + " error_data = json.loads(response.text)\n", + " error_code = error_data.get(\"error\", {}).get(\"code\", \"\")\n", + " if error_code == \"PathNotAllowed\":\n", + " utils.print_ok(f\" ✅ Correctly blocked with 403 PathNotAllowed\")\n", + " else:\n", + " utils.print_ok(f\" ✅ Correctly returned 403 (code: {error_code})\")\n", + " except json.JSONDecodeError:\n", + " utils.print_ok(f\" ✅ Correctly returned 403\")\n", + " else:\n", + " utils.print_error(f\" ❌ Expected 403, got {response.status_code}: {response.text[:200]}\")\n", + " except Exception as e:\n", + " utils.print_error(f\" Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d59637f4", + "metadata": {}, + "source": [ + "## Test 9 — Model Access Control (allowedModels)\n", + "\n", + "Validate that the `validate-model-access` fragment enforces the `allowedModels` restriction set in the access contract product policy.\n", + "Requesting a model not in the allowed list should be rejected." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bcd976e", + "metadata": {}, + "outputs": [], + "source": [ + "# The access contract was configured with allowedModels limited to openai_model and foundry_inference_model\n", + "# (and optionally gemini_model). A model outside this list should be rejected.\n", + "blocked_model = \"some-unauthorized-model-xyz\"\n", + "utils.print_info(f\"Testing model access control with unauthorized model: {blocked_model}\")\n", + "\n", + "blocked_model_url = f\"{azure_endpoint.rstrip('/')}/unified-ai/models/chat/completions?api-version={inference_api_version}\"\n", + "\n", + "try:\n", + " response = requests.post(\n", + " blocked_model_url,\n", + " headers={'api-key': api_key},\n", + " json={\n", + " \"model\": blocked_model,\n", + " \"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]\n", + " },\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code == 403:\n", + " utils.print_ok(f\"✅ Unauthorized model correctly blocked: {response.status_code}\")\n", + " try:\n", + " error_data = json.loads(response.text)\n", + " utils.print_info(f\" Error: {error_data.get('error', {}).get('message', response.text[:200])}\")\n", + " except json.JSONDecodeError:\n", + " pass\n", + " elif response.status_code == 400:\n", + " utils.print_ok(f\"✅ Unauthorized model rejected: {response.status_code}\")\n", + " try:\n", + " error_data = json.loads(response.text)\n", + " utils.print_info(f\" Error: {error_data.get('error', {}).get('message', response.text[:200])}\")\n", + " except json.JSONDecodeError:\n", + " pass\n", + " else:\n", + " utils.print_error(f\"❌ Expected 403 or 400, got {response.status_code}: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "\n", + "# Verify an allowed model still works\n", + "utils.print_info(f\"Confirming allowed model still works: {openai_model}\")\n", + "\n", + "try:\n", + " response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': api_key},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Say OK\"}]},\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code == 200:\n", + " utils.print_ok(f\"✅ Allowed model accepted: {response.status_code}\")\n", + " else:\n", + " utils.print_error(f\"❌ Allowed model rejected: {response.status_code}\")\n", + "except Exception as e:\n", + " utils.print_error(f\" Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "97432401", + "metadata": {}, + "source": [ + "## Test 10 — Invalid API Key\n", + "\n", + "Validate that an incorrect API key is rejected (as opposed to just a missing key tested earlier).\n", + "This tests the APIM subscription validation layer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db1e716b", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(\"Testing with invalid (wrong) API key...\")\n", + "\n", + "try:\n", + " response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': 'invalid-api-key-00000000000000000000'},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code == 401:\n", + " utils.print_ok(f\"✅ Invalid API key correctly rejected: {response.status_code} (expected 401)\")\n", + " else:\n", + " utils.print_error(f\"❌ Invalid API key: {response.status_code} (expected 401)\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")\n", + "\n", + "# Test with an API key from a different subscription (if another subscription exists)\n", + "utils.print_info(\"Testing with API key against wrong API path (cross-product)...\")\n", + "\n", + "try:\n", + " # Use the valid key but against a path not associated with this product\n", + " wrong_path_url = f\"{azure_endpoint.rstrip('/')}/openai/deployments/{openai_model}/chat/completions?api-version={openai_api_version}\"\n", + " response = requests.post(\n", + " wrong_path_url,\n", + " headers={'api-key': api_key},\n", + " json={\"messages\": [{\"role\": \"user\", \"content\": \"Hi\"}]},\n", + " timeout=30\n", + " )\n", + "\n", + " if response.status_code in [401, 403]:\n", + " utils.print_ok(f\"✅ Cross-product access correctly rejected: {response.status_code}\")\n", + " elif response.status_code == 200:\n", + " utils.print_info(f\"ℹ️ Cross-product access allowed (key may be associated with multiple products): {response.status_code}\")\n", + " else:\n", + " utils.print_info(f\"ℹ️ Cross-product response: {response.status_code}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "1c9f80d0", + "metadata": {}, + "source": [ + "## Test 11 — Streaming Request\n", + "\n", + "Test that the `request-processor` correctly detects `stream: true` in the request body and the gateway handles streaming responses.\n", + "The `UAIG-Is-Streaming` header should reflect `true`, and the response should be a streamed SSE response." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "acd6d5ee", + "metadata": {}, + "outputs": [], + "source": [ + "utils.print_info(f\"Testing streaming request with model: {openai_model}\")\n", + "\n", + "streaming_messages = {\n", + " \"messages\": [\n", + " {\"role\": \"user\", \"content\": \"Count from 1 to 10.\"}\n", + " ],\n", + " \"stream\": True\n", + "}\n", + "\n", + "try:\n", + " response = requests.post(\n", + " openai_url,\n", + " headers={'api-key': api_key},\n", + " json=streaming_messages,\n", + " timeout=60,\n", + " stream=True\n", + " )\n", + "\n", + " utils.print_response_code(response)\n", + "\n", + " if response.status_code == 200:\n", + " # Verify streaming header detection\n", + " is_streaming_header = response.headers.get(\"UAIG-Is-Streaming\", \"not-set\")\n", + " if is_streaming_header == \"true\":\n", + " utils.print_ok(f\" ✅ UAIG-Is-Streaming: {is_streaming_header} (correctly detected)\")\n", + " else:\n", + " utils.print_error(f\" ❌ UAIG-Is-Streaming: {is_streaming_header} (expected 'true')\")\n", + "\n", + " # Read and count streamed chunks (SSE format: data: {...}\\n\\n)\n", + " chunk_count = 0\n", + " content_parts = []\n", + " for line in response.iter_lines(decode_unicode=True):\n", + " if line and line.startswith(\"data: \"):\n", + " data_str = line[6:] # Remove \"data: \" prefix\n", + " if data_str.strip() == \"[DONE]\":\n", + " break\n", + " try:\n", + " chunk = json.loads(data_str)\n", + " # Some providers emit chunks with empty `choices` (e.g. content_filter\n", + " # bookkeeping events) or no `choices` field at all. Guard before [0].\n", + " choices = chunk.get(\"choices\") or []\n", + " if not choices:\n", + " chunk_count += 1\n", + " continue\n", + " delta = choices[0].get(\"delta\", {}) or {}\n", + " content = delta.get(\"content\", \"\")\n", + " if content:\n", + " content_parts.append(content)\n", + " chunk_count += 1\n", + " except json.JSONDecodeError:\n", + " pass\n", + "\n", + " full_response = \"\".join(content_parts)\n", + " utils.print_ok(f\" ✅ Received {chunk_count} SSE chunks\")\n", + " utils.print_ok(f\" 💬 Streamed response: {full_response[:200]}\")\n", + "\n", + " # Verify other UAIG headers are present in streaming response\n", + " backend = response.headers.get(\"UAIG-Backend\", \"not-set\")\n", + " api_type = response.headers.get(\"UAIG-API-Type\", \"not-set\")\n", + " utils.print_info(f\" Backend: {backend}\")\n", + " utils.print_info(f\" API Type: {api_type}\")\n", + " else:\n", + " utils.print_error(f\"Error: {response.text[:300]}\")\n", + "except Exception as e:\n", + " utils.print_error(f\"Request failed: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "30029d92", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "| Test | Pattern | Expected |\n", + "|------|---------|----------|\n", + "| Access Contract | Deploy Bicep access contract | Product + subscription created |\n", + "| Model & Deployment Discovery | `GET /unified-ai/deployments` and `GET /unified-ai/deployments/{name}` | List models, 200 for existing, 404 for non-existent |\n", + "| Azure OpenAI | `POST /unified-ai/openai/deployments/{model}/chat/completions` | 200 |\n", + "| Foundry Inference | `POST /unified-ai/models/chat/completions` (body: model) | 200 |\n", + "| Responses API | `POST /unified-ai/openai/responses` | 200 (if configured) |\n", + "| Gemini | `POST /unified-ai/v1beta/openai/chat/completions` | 200 (if configured) |\n", + "| Valid API Key | Request with api-key header | 200 |\n", + "| Missing API Key | Request without api-key header | 401 |\n", + "| Load Test | 30s burst requests | Mix of 200/429 |\n", + "| UAIG Debug Headers | Validate all UAIG-* response headers | All headers present and populated |\n", + "| Blocked Path | `POST /unified-ai/invalid-path/...` | 403 PathNotAllowed |\n", + "| Model Access Control | Request unauthorized model | 403 or 400 |\n", + "| Invalid API Key | Request with wrong api-key value | 401 |\n", + "| Streaming | `POST` with `stream: true` | 200 SSE stream, UAIG-Is-Streaming: true |\n", + "\n", + "---\n", + "### 🧹 Cleanup\n", + "\n", + "Remove the test access contract from APIM created during this notebook session:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45656b68", + "metadata": {}, + "outputs": [], + "source": [ + "# Set to True to delete the access contract created in this session\n", + "cleanup_enabled = False\n", + "\n", + "if cleanup_enabled:\n", + " utils.print_info(f\"Deleting access contract product: {product_id}...\")\n", + "\n", + " prod_cmd = f\"az apim product delete --resource-group {governance_hub_resource_group} --service-name {apimClientTool.apim_resource_name} --product-id {product_id} --delete-subscriptions true --yes\"\n", + " utils.run(prod_cmd, f\"Deleted product {product_id}\", f\"Failed to delete product {product_id}\")\n", + "\n", + " utils.print_ok(\"Cleanup completed!\")\n", + "else:\n", + " utils.print_info(\"Cleanup is disabled. Set cleanup_enabled = True to remove test resources.\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/7. citadel-hosted-agent-with-agt.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/7. citadel-hosted-agent-with-agt.ipynb new file mode 100644 index 000000000..246239ee9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/7. citadel-hosted-agent-with-agt.ipynb @@ -0,0 +1,1339 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8dc3b8e9", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🚀 Citadel Hosted Agent - Build, Deploy & Test\n", + "\n", + "## Overview\n", + "\n", + "This notebook builds a **Foundry Hosted Agent** for a Contoso HR scenario, governed end-to-end with the **Microsoft Agent Governance Toolkit (AGT)**, deploys it to the Foundry project in the spoke resource group, and tests it end-to-end.\n", + "\n", + "| Component | Detail |\n", + "|-----------|--------|\n", + "| **Agent Framework** | Microsoft Agent Framework (`agent-framework`) |\n", + "| **Governance** | AGT MAF adapter (`agent_os.integrations.maf_adapter`) — policy, capability guard, audit trail, rogue detection |\n", + "| **Protocol** | Responses API (hosted on port 8088) |\n", + "| **Model** | `gpt-4.1` via APIM BYO Gateway connection (`Hub-HR-ChatAgent-DEV-LLM/gpt-4.1`) |\n", + "| **Tools** | `get_pto_balance`, `get_holiday_schedule`, `get_benefits_summary`, `get_open_enrollment_window` |\n", + "| **Policy** | YAML file bundled in the container (`policies/hr-policy.yaml`) — denies SSN disclosure and 3rd-party salary lookups, audits PTO/benefits queries |\n", + "| **Telemetry** | AGT OpenTelemetry (`enable_otel`) → Application Insights. Emits AGT policy spans (`agt.policy.*`) and metrics (`agt.policy.evaluations`, `agt.policy.denials`, `agt.policy.latency_ms`). |\n", + "| **Container Build** | `az acr build` (cloud build — no local Docker required) |\n", + "\n", + "## Prerequisites\n", + "\n", + "- Citadel Governance Hub deployed (`azd up` completed)\n", + "- Spoke Foundry resources deployed (`workshop/scripts/deploy-spoke-foundry.ps1` or `.sh`)\n", + "- BYO Gateway connection `Hub-HR-ChatAgent-DEV-LLM` created (notebook 3)\n", + "\n", + "- Python packages installed via `uv sync` in the `workshop/` directory- Azure CLI logged in (`az login`)" + ] + }, + { + "cell_type": "markdown", + "id": "0801e883", + "metadata": {}, + "source": [ + "\n", + "### 0️⃣ Initialize Notebook Variables\n", + "\n", + "Read environment values from the `azd` environment and configure variables for this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0dc19d4e", + "metadata": {}, + "outputs": [], + "source": [ + "# Read environment values from azd\n", + "import subprocess, json\n", + "\n", + "def azd_get_value(key):\n", + " \"\"\"Read a value from the current azd environment.\"\"\"\n", + " result = subprocess.run(\n", + " [\"azd\", \"env\", \"get-value\", key],\n", + " capture_output=True, text=True\n", + " )\n", + " if result.returncode != 0:\n", + " raise RuntimeError(f\"Failed to read {key}: {result.stderr.strip()}\")\n", + " return result.stdout.strip()\n", + "\n", + "env_governance_hub_resource_group = azd_get_value(\"AZURE_RESOURCE_GROUP\")\n", + "env_location = azd_get_value(\"AZURE_LOCATION\")\n", + "\n", + "env_foundry_subscription_id = azd_get_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "env_foundry_resource_group = azd_get_value(\"SPOKE_RESOURCE_GROUP\")\n", + "env_foundry_account_name = azd_get_value(\"SPOKE_AI_FOUNDRY_ACCOUNT_NAME\")\n", + "env_foundry_project_name = azd_get_value(\"SPOKE_AI_FOUNDRY_PROJECT_NAME\")\n", + "env_acr_name = azd_get_value(\"SPOKE_ACR_NAME\")\n", + "env_acr_login_server = azd_get_value(\"SPOKE_ACR_LOGIN_SERVER\")\n", + "\n", + "print(f\"Citadel Hub resource group: {env_governance_hub_resource_group}\")\n", + "print(f\"Location: {env_location}\")\n", + "print(f\"Foundry: {env_foundry_account_name} (Project: {env_foundry_project_name})\")\n", + "print(f\"Spoke RG: {env_foundry_resource_group}\")\n", + "print(f\"ACR: {env_acr_name} ({env_acr_login_server})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4037173", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys, json, requests, time\n", + "sys.path.insert(1, '../shared') # add the shared directory to the Python path\n", + "import utils\n", + "\n", + "# ============================================================================\n", + "# 🔧 HOSTED AGENT CONFIGURATION\n", + "# To deploy a new version: bump hosted_agent_image_tag (e.g. \"v1\" → \"v2\"),\n", + "# then re-run from step 2 onward. The agent identity and RBAC persist\n", + "# across versions — only the image tag needs to change.\n", + "# ============================================================================\n", + "hosted_agent_name = \"citadel-hr-agent\"\n", + "hosted_agent_image_tag = \"v1\"\n", + "hosted_agent_model = \"Hub-HR-ChatAgent-DEV-LLM/gpt-4.1\" # BYO Gateway connection/model\n", + "\n", + "# Derived values\n", + "hosted_agent_image = f\"{env_acr_login_server}/{hosted_agent_name}:{hosted_agent_image_tag}\"\n", + "foundry_project_endpoint = f\"https://{env_foundry_account_name}.services.ai.azure.com/api/projects/{env_foundry_project_name}\"\n", + "\n", + "utils.print_info(f\"Agent name: {hosted_agent_name}\")\n", + "utils.print_info(f\"Image: {hosted_agent_image}\")\n", + "utils.print_info(f\"Model: {hosted_agent_model}\")\n", + "utils.print_info(f\"Foundry endpoint: {foundry_project_endpoint}\")" + ] + }, + { + "cell_type": "markdown", + "id": "93b08a40", + "metadata": {}, + "source": [ + "\n", + "### 1️⃣ Verify Azure CLI Login\n", + "\n", + "Ensure you are logged in to Azure CLI and the correct subscription is active." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc1d106c", + "metadata": {}, + "outputs": [], + "source": [ + "output = utils.run(\"az account show\", \"Retrieved az account\", \"Failed to get the current az account\")\n", + "\n", + "if output.success and output.json_data:\n", + " current_user = output.json_data['user']['name']\n", + " tenant_id = output.json_data['tenantId']\n", + " subscription_id = output.json_data['id']\n", + "\n", + " utils.print_info(f\"Current user: {current_user}\")\n", + " utils.print_info(f\"Tenant ID: {tenant_id}\")\n", + " utils.print_info(f\"Subscription ID: {subscription_id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "3fe750b3", + "metadata": {}, + "source": [ + "\n", + "### 2️⃣ Generate Hosted Agent Source Files\n", + "\n", + "This cell writes the agent application code into `workshop/hosted-agent/`. The agent is a **Contoso HR Assistant** governed end-to-end by the **Agent Governance Toolkit (AGT)** via the official `agent_os.integrations.maf_adapter`.\n", + "\n", + "**HR tools** (all dummy / deterministic — no external systems needed):\n", + "\n", + "- **`get_pto_balance(employee_id)`** — Remaining + used PTO days and accrual rate\n", + "- **`get_holiday_schedule(country)`** — Next 3 company holidays for `US` / `UK` / `JP`\n", + "- **`get_benefits_summary(plan_type)`** — Summary for `medical` / `dental` / `vision` / `401k`\n", + "- **`get_open_enrollment_window()`** — Simulated enrollment window dates\n", + "- **`delete_user(user_id)`** — Simulated destructive HR admin action, included to demonstrate policy blocking before a dangerous tool can run\n", + "\n", + "**AGT governance** is wired into MAF via `Agent(..., middleware=...)`. The hosted-agent streaming path uses a small streaming-compatible policy wrapper around AGT's `PolicyEvaluator` so deny decisions return a `ResponseStream` instead of a non-stream `AgentResponse`. Four layers run on every request:\n", + "\n", + "1. `AuditTrailMiddleware` — logs every agent invocation\n", + "2. `GovernancePolicyMiddleware` — evaluates the YAML policy in `policies/hr-policy.yaml` (denies SSN disclosure, 3rd-party salary lookups, and user deletion/deactivation requests; audits PTO/benefits queries)\n", + "3. `CapabilityGuardMiddleware` — restricts tool calls to the 5 HR tools above\n", + "4. `RogueDetectionMiddleware` — behavioural anomaly detection. Uses `agent-sre`; the adapter falls back gracefully via `try/except ImportError` if the package isn't installable in the container.\n", + "\n", + "The agent uses the **BYO Gateway connection** (`Hub-HR-ChatAgent-DEV-LLM/gpt-4.1`) to access the LLM through APIM — it does **not** deploy any models locally to the Foundry project.\n", + "\n", + "**OpenTelemetry**: the Foundry hosted-agent platform auto-configures Azure Monitor via the `microsoft-opentelemetry` distro using the injected `APPLICATIONINSIGHTS_CONNECTION_STRING`. `main.py` does **not** re-configure OTel (doing so triggers `Overriding of current TracerProvider is not allowed` warnings and leaves AGT spans attached to a dead local provider). Instead, AGT telemetry flows through the canonical surface: a shared `AuditLog` is passed into `create_governance_middleware(audit_log=...)`, and its `AuditLog.export_cloudevents()` output is picked up by the global OTel `LoggerProvider`. `agent.yaml` sets `OTEL_RESOURCE_ATTRIBUTES` so AGT events appear under `service.name=citadel-hr-agent` in Application Insights. Do not set `AZURE_EXPERIMENTAL_ENABLE_GENAI_TRACING=true` for this hosted-agent pattern: the Azure AI Projects experimental Responses wrapper returns an `AsyncStreamWrapper` without the raw `.headers` MAF expects, which breaks streamed tool-call responses in the playground. MAF 1.6 / microsoft-opentelemetry still provides the supported OpenAI/HTTP instrumentation.\n", + "\n", + "> **Note:** The `RBAC` requirement for `az acr build` is **Contributor** on the ACR or the spoke resource group. The Foundry project managed identity needs **AcrPull** on the ACR (assigned by `deploy-spoke-foundry` script).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61844d42", + "metadata": {}, + "outputs": [], + "source": [ + "import pathlib\n", + "\n", + "agent_dir = pathlib.Path(\"hosted-agent\")\n", + "agent_dir.mkdir(exist_ok=True)\n", + "policies_dir = agent_dir / \"policies\"\n", + "policies_dir.mkdir(exist_ok=True)\n", + "\n", + "# ---- main.py ----\n", + "main_py = r'''import os\n", + "from datetime import date, datetime, timedelta, timezone as tz\n", + "from typing import Annotated\n", + "\n", + "import logging\n", + "import threading\n", + "import time as _time\n", + "\n", + "from agent_framework import Agent, tool\n", + "from agent_framework.foundry import FoundryChatClient\n", + "from agent_framework_foundry_hosting import ResponsesHostServer\n", + "from azure.identity import DefaultAzureCredential\n", + "from opentelemetry import trace\n", + "from pydantic import Field\n", + "\n", + "# ---------------------------------------------------------------------------\n", + "# HR tools (all dummy / deterministic — no external systems).\n", + "# ---------------------------------------------------------------------------\n", + "\n", + "@tool(approval_mode=\"never_require\")\n", + "def get_pto_balance(\n", + " employee_id: Annotated[str, Field(description=\"Employee ID, e.g. 'E12345'\")]\n", + ") -> str:\n", + " \"\"\"Get the remaining PTO balance, days used this year, and accrual rate for an employee.\"\"\"\n", + " import hashlib\n", + " seed = int(hashlib.md5(employee_id.encode()).hexdigest()[:6], 16)\n", + " accrued = 20 + (seed % 6) # 20-25 days/yr\n", + " used = seed % accrued\n", + " remaining = accrued - used\n", + " return (\n", + " f\"PTO for employee {employee_id}: {remaining} days remaining \"\n", + " f\"({used} used / {accrued} accrued this year, accrual rate \"\n", + " f\"{accrued/12:.2f} days/month).\"\n", + " )\n", + "\n", + "\n", + "@tool(approval_mode=\"never_require\")\n", + "def get_holiday_schedule(\n", + " country: Annotated[str, Field(description=\"ISO country code: 'US', 'UK', or 'JP'\")]\n", + ") -> str:\n", + " \"\"\"Get the next 3 upcoming Contoso company holidays for a country.\"\"\"\n", + " today = date.today()\n", + " catalog = {\n", + " \"US\": [(\"Memorial Day\", (5, 27)), (\"Independence Day\", (7, 4)),\n", + " (\"Labor Day\", (9, 2)), (\"Thanksgiving\", (11, 28)),\n", + " (\"Christmas Day\", (12, 25))],\n", + " \"UK\": [(\"Spring Bank Holiday\", (5, 27)), (\"Summer Bank Holiday\", (8, 26)),\n", + " (\"Christmas Day\", (12, 25)), (\"Boxing Day\", (12, 26))],\n", + " \"JP\": [(\"Showa Day\", (4, 29)), (\"Greenery Day\", (5, 4)),\n", + " (\"Marine Day\", (7, 15)), (\"Mountain Day\", (8, 11)),\n", + " (\"Culture Day\", (11, 3))],\n", + " }\n", + " code = country.upper()\n", + " if code not in catalog:\n", + " return f\"No holiday schedule configured for country '{country}'. Supported: US, UK, JP.\"\n", + " upcoming = []\n", + " for name, (m, d) in catalog[code]:\n", + " candidate = date(today.year, m, d)\n", + " if candidate < today:\n", + " candidate = date(today.year + 1, m, d)\n", + " upcoming.append((candidate, name))\n", + " upcoming.sort()\n", + " top3 = \", \".join(f\"{name} ({d.isoformat()})\" for d, name in upcoming[:3])\n", + " return f\"Next 3 Contoso holidays in {code}: {top3}.\"\n", + "\n", + "\n", + "@tool(approval_mode=\"never_require\")\n", + "def get_benefits_summary(\n", + " plan_type: Annotated[str, Field(description=\"One of: 'medical', 'dental', 'vision', '401k'\")]\n", + ") -> str:\n", + " \"\"\"Get a high-level summary of a Contoso benefits plan.\"\"\"\n", + " summaries = {\n", + " \"medical\": \"Medical (Contoso PPO): $250 deductible, $20 PCP copay, 90% in-network coinsurance, $3,000 OOP max.\",\n", + " \"dental\": \"Dental (Contoso Premier): 100% preventive, 80% basic, 50% major; $2,000 annual maximum.\",\n", + " \"vision\": \"Vision (Contoso View): annual eye exam $0, $200 frames allowance every 12 months.\",\n", + " \"401k\": \"401(k): Contoso matches 100% of the first 6% of eligible pay; immediate vesting; Roth + traditional options.\",\n", + " }\n", + " key = plan_type.lower()\n", + " if key not in summaries:\n", + " return f\"Unknown plan '{plan_type}'. Supported: medical, dental, vision, 401k.\"\n", + " return summaries[key]\n", + "\n", + "\n", + "@tool(approval_mode=\"never_require\")\n", + "def get_open_enrollment_window() -> str:\n", + " \"\"\"Get the current open enrollment window dates for Contoso benefits.\"\"\"\n", + " today = date.today()\n", + " start = date(today.year, 11, 1)\n", + " end = date(today.year, 11, 21)\n", + " if today > end:\n", + " start = date(today.year + 1, 11, 1)\n", + " end = date(today.year + 1, 11, 21)\n", + " days = (start - today).days\n", + " when = \"currently open\" if start <= today <= end else f\"opens in {days} days\"\n", + " return f\"Contoso open enrollment: {start.isoformat()} → {end.isoformat()} ({when}).\"\n", + "\n", + "\n", + "@tool(approval_mode=\"never_require\")\n", + "def delete_user(\n", + " user_id: Annotated[str, Field(description=\"Employee/user ID to delete, e.g. 'E12345'\")]\n", + ") -> str:\n", + " \"\"\"Simulate deleting a Contoso HR user account.\"\"\"\n", + " return f\"SIMULATION ONLY: user {user_id} would be deleted.\"\n", + "\n", + "\n", + "# ---------------------------------------------------------------------------\n", + "# OpenTelemetry note.\n", + "#\n", + "# The Foundry hosted-agent platform auto-configures the global OTel\n", + "# TracerProvider / MeterProvider / LoggerProvider via the\n", + "# microsoft-opentelemetry distro using the injected\n", + "# APPLICATIONINSIGHTS_CONNECTION_STRING. We deliberately do NOT call\n", + "# configure_azure_monitor() or agentmesh.governance.enable_otel() here:\n", + "# both attempt to override the global providers and trigger\n", + "# \"Overriding of current TracerProvider is not allowed\" warnings, which\n", + "# leaves AGT spans attached to a process-local provider that nobody\n", + "# exports.\n", + "#\n", + "# AGT telemetry instead flows through the canonical\n", + "# AuditLog.export_cloudevents() surface: we construct a shared AuditLog,\n", + "# pass it to create_governance_middleware(audit_log=...), and the existing\n", + "# OTel pipeline picks it up. Custom span emission can use:\n", + "# tracer = trace.get_tracer(__name__)\n", + "# ---------------------------------------------------------------------------\n", + "\n", + "tracer = trace.get_tracer(__name__)\n", + "\n", + "\n", + "# ---------------------------------------------------------------------------\n", + "# AGT AuditLog -> OTel logger flush task.\n", + "#\n", + "# create_governance_middleware records every policy / capability / rogue\n", + "# decision into the in-memory AuditLog. To surface those decisions in\n", + "# Application Insights we periodically drain AuditLog.export_cloudevents()\n", + "# onto a stdlib logger. The platform distro auto-bridges stdlib logging to\n", + "# the OTel LoggerProvider, so each CloudEvent becomes a structured log\n", + "# record (visible in App Insights \"traces\" table with logger name\n", + "# `agt.audit`).\n", + "#\n", + "# Runs in a daemon thread so it does not interfere with the Hypercorn\n", + "# event loop owned by ResponsesHostServer.run().\n", + "# ---------------------------------------------------------------------------\n", + "\n", + "def _start_audit_flusher(audit_log, interval_s: float = 5.0) -> None:\n", + " audit_logger = logging.getLogger(\"agt.audit\")\n", + " # Force INFO so flusher diagnostics survive any root-level filter the\n", + " # platform may install. logger.propagate stays True so the OTel logging\n", + " # bridge installed by the distro still picks the records up.\n", + " audit_logger.setLevel(logging.INFO)\n", + "\n", + " # One-shot startup diagnostic: log the actual AuditLog API surface so we\n", + " # can see in stderr which method name to call (export_cloudevents vs.\n", + " # export vs. something else).\n", + " public_methods = sorted(m for m in dir(audit_log) if not m.startswith(\"_\"))\n", + " audit_logger.info(\n", + " \"agt.audit flusher starting (interval=%.1fs); AuditLog API: %s\",\n", + " interval_s, \", \".join(public_methods),\n", + " )\n", + "\n", + " def _flush_loop() -> None:\n", + " # AGT 3.6.0 AuditLog.export_cloudevents() returns the FULL ordered\n", + " # list of events recorded so far (no incremental kwarg). We track how\n", + " # many we've already emitted and slice from there each tick.\n", + " seen = 0\n", + " tick = 0\n", + " while True:\n", + " tick += 1\n", + " drained_this_tick = 0\n", + " err: Exception | None = None\n", + " try:\n", + " events = audit_log.export_cloudevents() or []\n", + " new_events = events[seen:]\n", + " for ce in new_events:\n", + " ce_type = ce.get(\"type\") if isinstance(ce, dict) else getattr(ce, \"type\", None)\n", + " audit_logger.info(\n", + " \"agt.audit.event\",\n", + " extra={\"agt.event.type\": ce_type, \"agt.cloudevent\": ce},\n", + " )\n", + " drained_this_tick += 1\n", + " seen = len(events)\n", + " except Exception as exc: # never let the flusher die silently\n", + " err = exc\n", + "\n", + " # Heartbeat once per minute (every 12 ticks at 5s interval) so we\n", + " # can confirm the thread is alive even when nothing is drained.\n", + " if err is not None:\n", + " audit_logger.warning(\n", + " \"agt.audit flush tick %d failed: %s: %s\",\n", + " tick, type(err).__name__, err,\n", + " )\n", + " elif drained_this_tick > 0:\n", + " audit_logger.info(\n", + " \"agt.audit flush tick %d drained=%d total=%d\",\n", + " tick, drained_this_tick, seen,\n", + " )\n", + " elif tick % 12 == 1:\n", + " audit_logger.info(\n", + " \"agt.audit flush tick %d heartbeat (no new events; total=%d)\",\n", + " tick, seen,\n", + " )\n", + " _time.sleep(interval_s)\n", + "\n", + " threading.Thread(\n", + " target=_flush_loop,\n", + " name=\"agt-audit-flusher\",\n", + " daemon=True,\n", + " ).start()\n", + "\n", + "\n", + "def _create_streaming_governance_middleware(\n", + " *,\n", + " policy_directory: str,\n", + " allowed_tools: list[str],\n", + " agent_id: str,\n", + " audit_log,\n", + ") -> list:\n", + " from agent_framework import (\n", + " AgentMiddleware,\n", + " AgentResponse,\n", + " AgentResponseUpdate,\n", + " Content,\n", + " Message,\n", + " ResponseStream,\n", + " )\n", + " from agent_os.integrations.maf_adapter import create_governance_middleware\n", + " from agent_os.policies import PolicyEvaluator\n", + "\n", + " class StreamingGovernancePolicyMiddleware(AgentMiddleware):\n", + " def __init__(self, evaluator: PolicyEvaluator, audit_log) -> None:\n", + " self.evaluator = evaluator\n", + " self.audit_log = audit_log\n", + "\n", + " async def process(self, context, call_next) -> None:\n", + " agent_name = getattr(context.agent, \"name\", \"unknown\")\n", + " messages = getattr(context, \"messages\", None) or []\n", + " last_message_text = \"\"\n", + " if messages:\n", + " last_msg = messages[-1]\n", + " last_message_text = getattr(last_msg, \"text\", None) or str(last_msg)\n", + "\n", + " decision = self.evaluator.evaluate(\n", + " {\n", + " \"agent\": agent_name,\n", + " \"message\": last_message_text,\n", + " \"timestamp\": _time.time(),\n", + " \"stream\": getattr(context, \"stream\", False),\n", + " \"message_count\": len(messages),\n", + " }\n", + " )\n", + "\n", + " metadata = getattr(context, \"metadata\", {})\n", + " metadata[\"governance_decision\"] = decision\n", + "\n", + " if decision.allowed:\n", + " if self.audit_log:\n", + " self.audit_log.log(\n", + " event_type=\"policy_evaluation\",\n", + " agent_did=agent_name,\n", + " action=\"allow\",\n", + " data={\n", + " \"matched_rule\": decision.matched_rule,\n", + " \"message_preview\": last_message_text[:200],\n", + " },\n", + " outcome=\"success\",\n", + " policy_decision=decision.action,\n", + " )\n", + " await call_next()\n", + " return\n", + "\n", + " logging.getLogger(\"agent_os.integrations.maf_adapter\").info(\n", + " \"Policy DENY for agent '%s': %s (rule=%s)\",\n", + " agent_name,\n", + " decision.reason,\n", + " decision.matched_rule,\n", + " )\n", + " refusal = f\"Policy violation: {decision.reason}\"\n", + "\n", + " if self.audit_log:\n", + " self.audit_log.log(\n", + " event_type=\"policy_violation\",\n", + " agent_did=agent_name,\n", + " action=\"deny\",\n", + " data={\n", + " \"reason\": decision.reason,\n", + " \"matched_rule\": decision.matched_rule,\n", + " \"message_preview\": last_message_text[:200],\n", + " },\n", + " outcome=\"denied\",\n", + " policy_decision=decision.action,\n", + " )\n", + "\n", + " if getattr(context, \"stream\", False):\n", + " async def _deny_stream():\n", + " yield AgentResponseUpdate(\n", + " contents=[Content.from_text(refusal)],\n", + " role=\"assistant\",\n", + " agent_id=agent_name,\n", + " )\n", + "\n", + " context.result = ResponseStream(\n", + " _deny_stream(),\n", + " finalizer=AgentResponse.from_updates,\n", + " )\n", + " else:\n", + " context.result = AgentResponse(\n", + " messages=[Message(\"assistant\", [refusal])]\n", + " )\n", + "\n", + " evaluator = PolicyEvaluator()\n", + " evaluator.load_policies(policy_directory)\n", + " stack = create_governance_middleware(\n", + " policy_directory=None,\n", + " allowed_tools=allowed_tools,\n", + " agent_id=agent_id,\n", + " enable_rogue_detection=True,\n", + " audit_log=audit_log,\n", + " )\n", + " stack.insert(1, StreamingGovernancePolicyMiddleware(evaluator, audit_log))\n", + " return stack\n", + "\n", + "\n", + "# ---------------------------------------------------------------------------\n", + "# Main entrypoint.\n", + "# ---------------------------------------------------------------------------\n", + "\n", + "def main() -> None:\n", + " from agentmesh.governance import AuditLog\n", + "\n", + " tools = [\n", + " get_pto_balance,\n", + " get_holiday_schedule,\n", + " get_benefits_summary,\n", + " get_open_enrollment_window,\n", + " delete_user,\n", + " ]\n", + " allowed_tool_names = [t.name if hasattr(t, \"name\") else t.__name__ for t in tools]\n", + "\n", + " agent_name_env = os.environ.get(\"OTEL_SERVICE_NAME\", \"citadel-hr-agent\")\n", + "\n", + " # Shared AuditLog — emits OTel-compatible CloudEvents via\n", + " # audit_log.export_cloudevents(); plugs into the already-configured\n", + " # global LoggerProvider installed by the platform distro.\n", + " audit_log = AuditLog()\n", + "\n", + " governance = _create_streaming_governance_middleware(\n", + " policy_directory=\"policies\",\n", + " allowed_tools=allowed_tool_names,\n", + " agent_id=agent_name_env,\n", + " audit_log=audit_log,\n", + " )\n", + "\n", + " # Drain AGT audit events into the OTel pipeline every 5s.\n", + " _start_audit_flusher(audit_log, interval_s=5.0)\n", + "\n", + " client = FoundryChatClient(\n", + " project_endpoint=os.environ[\"FOUNDRY_PROJECT_ENDPOINT\"],\n", + " model=os.environ[\"AZURE_AI_MODEL_DEPLOYMENT_NAME\"],\n", + " credential=DefaultAzureCredential(),\n", + " )\n", + "\n", + " agent = Agent(\n", + " client=client,\n", + " name=agent_name_env,\n", + " instructions=(\n", + " \"You are Contoso HR Assistant, a friendly internal HR helper. \"\n", + " \"Use the provided tools to answer questions about an employee's own PTO balance, \"\n", + " \"Contoso company holidays, benefits plans (medical/dental/vision/401k), and the \"\n", + " \"open enrollment window. The delete_user tool exists only to demonstrate governance \"\n", + " \"for explicit user deletion requests. Never disclose another employee's personal data \"\n", + " \"(salary, SSN, compensation). Be concise.\"\n", + " ),\n", + " tools=tools,\n", + " middleware=governance,\n", + " # History is managed by the hosting infrastructure.\n", + " default_options={\"store\": False},\n", + " )\n", + "\n", + " server = ResponsesHostServer(agent)\n", + " server.run()\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()\n", + "'''\n", + "(agent_dir / \"main.py\").write_text(main_py.strip(), encoding=\"utf-8\")\n", + "utils.print_ok(\"Created hosted-agent/main.py\")\n", + "\n", + "# ---- requirements.txt ----\n", + "# agent-sre is optional — the AGT MAF adapter falls back gracefully via\n", + "# try/except ImportError if it isn't installable. Keeping it in for the\n", + "# full demo (rogue-detection middleware).\n", + "requirements_txt = \"\"\"\\\n", + "# Pinned to the verified-good set from the foundry-hosted-agents skill.\n", + "# The umbrella `agent-framework` meta-package resolves to versions with\n", + "# the AsyncStreamWrapper.parse() regression that breaks streamed tool\n", + "# calls in the hosted-agent server.\n", + "agent-framework-core~=1.6.0\n", + "agent-framework-foundry~=1.6.0\n", + "agent-framework-foundry-hosting==1.0.0a260521\n", + "mcp~=1.10.0\n", + "pydantic==2.12.5\n", + "azure-identity>=1.19.0,<1.26.0a0\n", + "agent-os-kernel\n", + "agentmesh-platform\n", + "agent-sre\n", + "\"\"\"\n", + "(agent_dir / \"requirements.txt\").write_text(requirements_txt, encoding=\"utf-8\")\n", + "utils.print_ok(\"Created hosted-agent/requirements.txt\")\n", + "\n", + "# ---- Dockerfile ----\n", + "# Use uv for fast, reliable installs (10-100x faster than pip). The official\n", + "# uv image provides the binary; we copy it into a slim Python base.\n", + "dockerfile = \"\"\"\\\n", + "FROM python:3.13-slim\n", + "\n", + "# Install uv from the official distroless image\n", + "COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/\n", + "\n", + "ENV UV_SYSTEM_PYTHON=1 \\\n", + " UV_COMPILE_BYTECODE=1 \\\n", + " UV_LINK_MODE=copy\n", + "\n", + "WORKDIR /app\n", + "COPY requirements.txt .\n", + "RUN uv pip install --system --no-cache --prerelease=if-necessary-or-explicit -r requirements.txt\n", + "RUN python -c \"from pydantic._internal._typing_extra import eval_type_backport; import mcp; from agent_framework_foundry_hosting import ResponsesHostServer; print('dependency smoke ok')\"\n", + "\n", + "COPY main.py .\n", + "COPY policies/ ./policies/\n", + "\n", + "EXPOSE 8088\n", + "\n", + "CMD [\"python\", \"main.py\"]\n", + "\"\"\"\n", + "(agent_dir / \"Dockerfile\").write_text(dockerfile, encoding=\"utf-8\")\n", + "utils.print_ok(\"Created hosted-agent/Dockerfile\")\n", + "\n", + "# ---- policies/hr-policy.yaml ----\n", + "hr_policy_yaml = \"\"\"\\\n", + "name: contoso-hr-policy\n", + "version: \"1.0\"\n", + "description: Governance policy for the Contoso HR hosted agent.\n", + "\n", + "defaults:\n", + " action: allow\n", + "\n", + "rules:\n", + " - name: block-ssn-disclosure\n", + " condition:\n", + " field: message\n", + " operator: matches\n", + " value: \"\\\\\\\\b\\\\\\\\d{3}-\\\\\\\\d{2}-\\\\\\\\d{4}\\\\\\\\b\"\n", + " action: deny\n", + " message: \"SSN detected in request — blocked for PII protection.\"\n", + " priority: 1000\n", + "\n", + " - name: block-user-deletion\n", + " condition:\n", + " field: message\n", + " operator: matches\n", + " value: \"(?i)(\\\\\\\\b(delete|remove|deactivate|disable|terminate)\\\\\\\\b.{0,80}\\\\\\\\b(user|employee|account|profile|E\\\\\\\\d{3,})\\\\\\\\b|\\\\\\\\b(user|employee|account|profile|E\\\\\\\\d{3,})\\\\\\\\b.{0,80}\\\\\\\\b(delete|remove|deactivate|disable|terminate)\\\\\\\\b)\"\n", + " action: deny\n", + " message: \"User deletion and deactivation requests are blocked by HR policy.\"\n", + " priority: 950\n", + "\n", + " - name: block-third-party-salary-lookup\n", + " condition:\n", + " field: message\n", + " operator: matches\n", + " value: \"(?i)(?=.*\\\\\\\\b(salary|compensation|pay|bonus|paycheck)\\\\\\\\b)(?=.*\\\\\\\\b(other|coworker|co-worker|colleague|teammate|someone\\\\\\\\s+else|another\\\\\\\\s+employee|employee\\\\\\\\s+[A-Za-z]?\\\\\\\\d+|my\\\\\\\\s+manager|my\\\\\\\\s+boss|my\\\\\\\\s+report)\\\\\\\\b).*\"\n", + " action: deny\n", + " message: \"Disclosing another employee's compensation is not permitted by HR policy.\"\n", + " priority: 900\n", + "\n", + " - name: audit-benefits-and-pto\n", + " condition:\n", + " field: message\n", + " operator: matches\n", + " value: \"(?i)(pto|paid\\\\\\\\s+time\\\\\\\\s+off|leave|holiday|benefits?|enrollment|401k|medical|dental|vision)\"\n", + " action: audit\n", + " message: \"HR self-service query logged for compliance trail.\"\n", + " priority: 100\n", + "\"\"\"\n", + "(policies_dir / \"hr-policy.yaml\").write_text(hr_policy_yaml, encoding=\"utf-8\")\n", + "utils.print_ok(\"Created hosted-agent/policies/hr-policy.yaml\")\n", + "\n", + "# ---- agent.yaml ----\n", + "agent_yaml = f\"\"\"\\\n", + "kind: hosted\n", + "name: {hosted_agent_name}\n", + "protocols:\n", + " - protocol: responses\n", + " version: 1.0.0\n", + "resources:\n", + " cpu: \"1\"\n", + " memory: 2Gi\n", + "environment_variables:\n", + " - name: AZURE_AI_MODEL_DEPLOYMENT_NAME\n", + " value: {hosted_agent_model}\n", + " - name: ENABLE_INSTRUMENTATION\n", + " value: \"true\"\n", + " - name: ENABLE_SENSITIVE_DATA\n", + " value: \"true\"\n", + " - name: OTEL_SERVICE_NAME\n", + " value: {hosted_agent_name}\n", + " - name: OTEL_RESOURCE_ATTRIBUTES\n", + " value: service.name={hosted_agent_name},service.namespace=citadel,deployment.environment=workshop\n", + "\"\"\"\n", + "(agent_dir / \"agent.yaml\").write_text(agent_yaml, encoding=\"utf-8\")\n", + "utils.print_ok(\"Created hosted-agent/agent.yaml\")\n", + "\n", + "utils.print_info(f\"\\nAll agent source files written to: {agent_dir.resolve()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d721ebf0", + "metadata": {}, + "source": [ + "\n", + "### 3️⃣ Build & Push Container Image\n", + "\n", + "Uses `az acr build` to build the Docker image in the cloud and push it to ACR. **No local Docker installation required.**\n", + "\n", + "> The signed-in user needs **Contributor** (or at minimum an ACR task-scheduling permission) on the ACR. If you deployed the spoke with `deploy-spoke-foundry`, you already have Owner on the spoke RG which inherits to the ACR." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb236550", + "metadata": {}, + "outputs": [], + "source": [ + "# NOTE: --no-logs is required because Azure CLI on Windows crashes streaming\n", + "# build logs due to a cp1252 encoding bug (pip output contains Unicode chars\n", + "# that colorama can't encode). The build runs server-side regardless.\n", + "acr_build_cmd = (\n", + " f'az acr build'\n", + " f' --registry {env_acr_name}'\n", + " f' --resource-group {env_foundry_resource_group}'\n", + " f' --image {hosted_agent_name}:{hosted_agent_image_tag}'\n", + " f' --file hosted-agent/Dockerfile'\n", + " f' --no-logs'\n", + " f' hosted-agent/'\n", + ")\n", + "\n", + "utils.print_info(f\"Building image in ACR: {hosted_agent_image}\")\n", + "utils.print_info(\"Build logs suppressed (Windows encoding bug). Polling for completion...\")\n", + "\n", + "output = utils.run(acr_build_cmd, \"ACR build queued\", \"ACR build queue failed\")\n", + "\n", + "# Wait for the image to appear in ACR (build takes ~2-3 min)\n", + "import time as _t\n", + "for attempt in range(24): # up to 4 minutes\n", + " verify = utils.run(\n", + " f'az acr repository show-tags --name {env_acr_name} --repository {hosted_agent_name} --output json',\n", + " \"\", \"\"\n", + " )\n", + " if verify.success and verify.json_data and hosted_agent_image_tag in verify.json_data:\n", + " utils.print_ok(f\"Image verified in ACR: {hosted_agent_image}\")\n", + " break\n", + " utils.print_info(f\" Waiting for image... ({(attempt+1)*10}s)\")\n", + " _t.sleep(10)\n", + "else:\n", + " utils.print_error(f\"Image '{hosted_agent_image_tag}' not found after 4 min. Check ACR build logs in the portal.\")" + ] + }, + { + "cell_type": "markdown", + "id": "9d612ba1", + "metadata": {}, + "source": [ + "\n", + "### 4️⃣ Deploy Hosted Agent to Foundry\n", + "\n", + "Creates a new hosted agent version in the Foundry project using the Python SDK. The agent is deployed with the container image from ACR and configured to use the BYO Gateway connection for LLM access." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91e3858e", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.ai.projects import AIProjectClient\n", + "from azure.ai.projects.models import (\n", + " HostedAgentDefinition,\n", + " ProtocolVersionRecord,\n", + " AgentProtocol,\n", + ")\n", + "from azure.identity import DefaultAzureCredential\n", + "\n", + "utils.print_info(f\"Connecting to Foundry endpoint: {foundry_project_endpoint}\")\n", + "\n", + "credential = DefaultAzureCredential()\n", + "project_client = AIProjectClient(\n", + " endpoint=foundry_project_endpoint,\n", + " credential=credential,\n", + " allow_preview=True,\n", + ")\n", + "utils.print_ok(f\"Foundry project client initialized for: {env_foundry_project_name}\")\n", + "\n", + "# Deploy the hosted agent\n", + "utils.print_info(f\"Deploying hosted agent: {hosted_agent_name}\")\n", + "utils.print_info(f\"Image: {hosted_agent_image}\")\n", + "utils.print_info(f\"Model: {hosted_agent_model}\")\n", + "\n", + "agent = project_client.agents.create_version(\n", + " agent_name=hosted_agent_name,\n", + " definition=HostedAgentDefinition(\n", + " container_protocol_versions=[\n", + " ProtocolVersionRecord(\n", + " protocol=AgentProtocol.RESPONSES,\n", + " version=\"1.0.0\",\n", + " )\n", + " ],\n", + " cpu=\"1\",\n", + " memory=\"2Gi\",\n", + " image=hosted_agent_image,\n", + " environment_variables={\n", + " \"AZURE_AI_MODEL_DEPLOYMENT_NAME\": hosted_agent_model,\n", + " \"ENABLE_INSTRUMENTATION\": \"true\",\n", + " \"ENABLE_SENSITIVE_DATA\": \"true\",\n", + " \"OTEL_SERVICE_NAME\": hosted_agent_name,\n", + " \"OTEL_RESOURCE_ATTRIBUTES\": (\n", + " f\"service.name={hosted_agent_name},\"\n", + " \"service.namespace=citadel,\"\n", + " \"deployment.environment=workshop\"\n", + " ),\n", + " # Unlocks GenAI prompt/completion/token attributes on model spans\n", + " # (otherwise azure.ai.projects.telemetry logs a WARNING at startup\n", + " # and agent365_exporter reports \"No eligible genAI spans to export\").\n", + " },\n", + " ),\n", + ")\n", + "\n", + "utils.print_ok(f\"Agent version created: {agent.name} (version: {agent.version})\")\n", + "utils.print_info(f\"Status: {agent.status}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d24f98cb", + "metadata": {}, + "source": [ + "\n", + "### 5️⃣ Wait for Agent to Become Active\n", + "\n", + "Poll the agent version status until it becomes `active`. This typically takes 2-5 minutes as the platform pulls the image and starts the container." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "939ab0b5", + "metadata": {}, + "outputs": [], + "source": [ + "max_wait_seconds = 600 # 10 minutes\n", + "poll_interval = 15\n", + "\n", + "elapsed = 0\n", + "while elapsed < max_wait_seconds:\n", + " version = project_client.agents.get_version(\n", + " agent_name=agent.name,\n", + " agent_version=agent.version,\n", + " )\n", + " status = version.status\n", + " utils.print_info(f\"[{elapsed}s] Agent status: {status}\")\n", + "\n", + " if status == \"active\":\n", + " utils.print_ok(f\"Agent is active! (took ~{elapsed}s)\")\n", + " break\n", + " elif status in (\"failed\", \"error\"):\n", + " utils.print_error(f\"Agent deployment failed with status: {status}\")\n", + " break\n", + "\n", + " time.sleep(poll_interval)\n", + " elapsed += poll_interval\n", + "else:\n", + " utils.print_error(f\"Timed out after {max_wait_seconds}s waiting for agent to become active.\")" + ] + }, + { + "cell_type": "markdown", + "id": "54bddd5f", + "metadata": {}, + "source": [ + "\n", + "### 5a. Assign RBAC to Agent Identity\n", + "\n", + "Every hosted agent gets a dedicated Microsoft Entra ID (agent identity) at deploy time. This identity needs the **Foundry User** role on the Foundry account so it can access conversation storage and model endpoints.\n", + "\n", + "> When deploying with `azd`, this role is assigned automatically. Since we're using the SDK, we assign it explicitly here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa1e98c1", + "metadata": {}, + "outputs": [], + "source": [ + "# Retrieve the agent identity assigned by the platform\n", + "agent_identity = version.instance_identity\n", + "\n", + "if agent_identity is None:\n", + " # Fallback: fetch from agent-level details\n", + " agent_details = project_client.agents.get(agent_name=hosted_agent_name)\n", + " agent_identity = agent_details.instance_identity\n", + "\n", + "if agent_identity is None:\n", + " utils.print_error(\"Could not resolve agent identity. Ensure the agent is in 'active' state.\")\n", + "else:\n", + " agent_principal_id = agent_identity.principal_id\n", + " utils.print_ok(f\"Agent identity principal ID: {agent_principal_id}\")\n", + "\n", + " foundry_account_scope = (\n", + " f\"/subscriptions/{env_foundry_subscription_id}\"\n", + " f\"/resourceGroups/{env_foundry_resource_group}\"\n", + " f\"/providers/Microsoft.CognitiveServices/accounts/{env_foundry_account_name}\"\n", + " )\n", + "\n", + " # Check if Foundry User role is already assigned\n", + " check = subprocess.run(\n", + " [\"az\", \"role\", \"assignment\", \"list\",\n", + " \"--assignee-object-id\", agent_principal_id,\n", + " \"--role\", \"Foundry User\",\n", + " \"--scope\", foundry_account_scope,\n", + " \"--query\", \"[0].id\", \"-o\", \"tsv\"],\n", + " capture_output=True, text=True, shell=True,\n", + " )\n", + "\n", + " if check.stdout.strip():\n", + " utils.print_ok(\"Foundry User role is already assigned.\")\n", + " else:\n", + " utils.print_info(f\"Assigning 'Foundry User' to agent identity on: {env_foundry_account_name}\")\n", + " result = subprocess.run(\n", + " [\"az\", \"role\", \"assignment\", \"create\",\n", + " \"--assignee-object-id\", agent_principal_id,\n", + " \"--assignee-principal-type\", \"ServicePrincipal\",\n", + " \"--role\", \"Foundry User\",\n", + " \"--scope\", foundry_account_scope,\n", + " \"--only-show-errors\", \"-o\", \"none\"],\n", + " capture_output=True, text=True, shell=True,\n", + " )\n", + " if result.returncode == 0:\n", + " utils.print_ok(\"Foundry User role assigned. Waiting 60s for RBAC propagation...\")\n", + " time.sleep(60)\n", + " utils.print_ok(\"Done — RBAC should be active now.\")\n", + " else:\n", + " utils.print_error(f\"Failed to assign role: {result.stderr}\")" + ] + }, + { + "cell_type": "markdown", + "id": "035115fb", + "metadata": {}, + "source": [ + "\n", + "### 6️⃣ Invoke the Hosted Agent\n", + "\n", + "Use `project.get_openai_client(agent_name=...)` to invoke the hosted agent via the Responses API. This sends a request to the running agent container." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f427d51", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "from azure.ai.projects import AIProjectClient\n", + "from azure.identity import AzureCliCredential\n", + "\n", + "# Get an OpenAI client scoped to the hosted agent.\n", + "# Use an explicit Azure CLI credential with a longer timeout because\n", + "# Azure Identity's default 10s CLI subprocess timeout can be too short\n", + "# on Windows when refreshing the https://ai.azure.com token.\n", + "invoke_project_client = AIProjectClient(\n", + " endpoint=foundry_project_endpoint,\n", + " credential=AzureCliCredential(process_timeout=60),\n", + " allow_preview=True,\n", + ")\n", + "openai_client = invoke_project_client.get_openai_client(agent_name=hosted_agent_name)\n", + "\n", + "# MAF's ResponsesHostServer emits response items in shapes the OpenAI SDK's\n", + "# `response.output_text` convenience aggregator doesn't recognize. Dump the\n", + "# response to a dict and walk every nested value looking for text under\n", + "# common keys (text, content, output_text, value, message, delta).\n", + "def extract_text(response) -> str:\n", + " # Surface server-side failures rather than silently returning \"\"\n", + " err = getattr(response, \"error\", None)\n", + " status = getattr(response, \"status\", None)\n", + " if err is not None:\n", + " code = getattr(err, \"code\", None) or (err.get(\"code\") if isinstance(err, dict) else None)\n", + " msg = getattr(err, \"message\", None) or (err.get(\"message\") if isinstance(err, dict) else None)\n", + " return f\"[agent error: status={status} code={code} message={msg}]\"\n", + " text = getattr(response, \"output_text\", None)\n", + " if text:\n", + " return text\n", + " try:\n", + " data = response.model_dump()\n", + " except Exception:\n", + " data = getattr(response, \"__dict__\", {}) or {}\n", + "\n", + " chunks: list[str] = []\n", + " text_keys = (\"text\", \"output_text\", \"value\", \"message\", \"delta\")\n", + "\n", + " def walk(node):\n", + " if isinstance(node, dict):\n", + " for key in text_keys:\n", + " v = node.get(key)\n", + " if isinstance(v, str) and v.strip():\n", + " chunks.append(v)\n", + " for v in node.values():\n", + " walk(v)\n", + " elif isinstance(node, list):\n", + " for item in node:\n", + " walk(item)\n", + "\n", + " walk(data)\n", + "\n", + " seen, unique = set(), []\n", + " for c in chunks:\n", + " c = c.strip()\n", + " if c and c not in seen:\n", + " seen.add(c)\n", + " unique.append(c)\n", + " return \"\\n\".join(unique)\n", + "\n", + "\n", + "# Use a conversation_id so Foundry Traces groups requests together\n", + "conversation_id = str(uuid.uuid4())\n", + "utils.print_info(f\"Conversation ID: {conversation_id}\")\n", + "\n", + "# Test 1: PTO balance\n", + "utils.print_info(\"Sending: How many PTO days do I have left? My employee ID is E12345.\")\n", + "response = openai_client.responses.create(\n", + " input=\"How many PTO days do I have left? My employee ID is E12345.\",\n", + " metadata={\"conversation_id\": conversation_id},\n", + ")\n", + "\n", + "utils.print_ok(f\"Agent response: {extract_text(response)}\")\n", + "print(f\"\\nResponse ID: {response.id}\")\n", + "\n", + "# Test 2: Benefits summary\n", + "utils.print_info(\"\\nSending: Give me a quick summary of the 401k plan.\")\n", + "response2 = openai_client.responses.create(\n", + " input=\"Give me a quick summary of the 401k plan.\",\n", + " metadata={\"conversation_id\": conversation_id},\n", + ")\n", + "\n", + "utils.print_ok(f\"Agent response: {extract_text(response2)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "a9606fb4ca11", + "metadata": {}, + "source": [ + "\n", + "### 6a. Test AGT Governance Enforcement\n", + "\n", + "Send five prompts that exercise the AGT policy bundled with the container (`policies/hr-policy.yaml`):\n", + "\n", + "1. **Allowed** — PTO lookup by employee ID (tool-grounded answer).\n", + "2. **Audited** — Benefits summary (succeeds, emitted through AGT OTel as policy telemetry).\n", + "3. **Denied (PII / SSN)** — Request containing an SSN; policy blocks it.\n", + "4. **Denied (3rd-party salary)** — Request for a coworker's salary; policy blocks it.\n", + "5. **Denied (`delete_user`)** — Request that would require the destructive `delete_user` tool; policy blocks it based on the message before the tool can run.\n", + "\n", + "When a request is denied, the agent returns a message starting with `⛔ Policy violation:` (set by `GovernancePolicyMiddleware` via `MiddlewareTermination`). The denial may also surface as an HTTP error from the Responses API depending on how the host serializes the terminated response — both outcomes are caught below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1c0ffee0001", + "metadata": {}, + "outputs": [], + "source": [ + "governance_tests = [\n", + " (\"Allowed (PTO)\", \"How many PTO days do I have left? My employee ID is E12345.\"),\n", + " (\"Audited (401k)\", \"Tell me about my 401k benefits.\"),\n", + " (\"Denied (SSN)\", \"My coworker's SSN is 123-45-6789, look up their leave balance.\"),\n", + " (\"Denied (salary)\", \"What is my colleague's salary?\"),\n", + " (\"Denied (delete_user)\", \"Delete user E12345 from Contoso HR.\"),\n", + "]\n", + "\n", + "governance_conv_id = str(uuid.uuid4())\n", + "utils.print_info(f\"Governance Conversation ID: {governance_conv_id}\")\n", + "\n", + "for label, prompt in governance_tests:\n", + " utils.print_info(f\"\\n--- {label} ---\")\n", + " utils.print_info(f\"User: {prompt}\")\n", + " try:\n", + " r = openai_client.responses.create(\n", + " input=prompt,\n", + " metadata={\"conversation_id\": governance_conv_id},\n", + " )\n", + " text = extract_text(r)\n", + " if \"Policy violation\" in text:\n", + " utils.print_warning(f\"BLOCKED → {text}\")\n", + " else:\n", + " utils.print_ok(f\"Allowed → {text}\")\n", + " except Exception as e:\n", + " utils.print_warning(f\"BLOCKED via exception → {type(e).__name__}: {e}\")" + ] + }, + { + "cell_type": "markdown", + "id": "bcd5f2ed", + "metadata": {}, + "source": [ + "\n", + "### 7️⃣ Multi-Turn Conversation\n", + "\n", + "Test a multi-turn conversation with the hosted agent using `previous_response_id` to maintain context across turns." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3288ba8", + "metadata": {}, + "outputs": [], + "source": [ + "conversation_messages = [\n", + " \"What are the next Contoso holidays in the US?\",\n", + " \"What about in Japan?\",\n", + " \"And when does open enrollment start?\",\n", + "]\n", + "\n", + "previous_response_id = None\n", + "multi_turn_conversation_id = str(uuid.uuid4())\n", + "utils.print_info(f\"Conversation ID: {multi_turn_conversation_id}\")\n", + "\n", + "for i, message in enumerate(conversation_messages, 1):\n", + " utils.print_info(f\"\\n--- Turn {i} ---\")\n", + " utils.print_info(f\"User: {message}\")\n", + "\n", + " kwargs = {\n", + " \"input\": message,\n", + " \"metadata\": {\"conversation_id\": multi_turn_conversation_id},\n", + " }\n", + " if previous_response_id:\n", + " kwargs[\"previous_response_id\"] = previous_response_id\n", + "\n", + " response = openai_client.responses.create(**kwargs)\n", + " previous_response_id = response.id\n", + "\n", + " utils.print_ok(f\"Agent: {extract_text(response)}\")\n", + "\n", + "utils.print_info(f\"\\nConversation completed ({len(conversation_messages)} turns)\")" + ] + }, + { + "cell_type": "markdown", + "id": "2a2ca85c", + "metadata": {}, + "source": [ + "\n", + "### 8️⃣ Streaming Responses\n", + "\n", + "Re-run the same queries with `stream=True` to demonstrate **streaming mode**. Tokens are printed as they arrive, significantly improving perceived latency (time-to-first-token) — the total inference time is similar, but the user sees output within seconds instead of waiting for the full response." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b64137d", + "metadata": {}, + "outputs": [], + "source": [ + "# Streaming: single-turn queries\n", + "streaming_conversation_id = str(uuid.uuid4())\n", + "utils.print_info(f\"Streaming Conversation ID: {streaming_conversation_id}\")\n", + "\n", + "# Test 1: PTO balance (streaming)\n", + "utils.print_info(\"Sending: How many PTO days do I have left? My employee ID is E67890.\")\n", + "stream = openai_client.responses.create(\n", + " input=\"How many PTO days do I have left? My employee ID is E67890.\",\n", + " metadata={\"conversation_id\": streaming_conversation_id},\n", + " stream=True,\n", + ")\n", + "response = None\n", + "for event in stream:\n", + " if event.type == \"response.output_text.delta\":\n", + " print(event.delta, end=\"\", flush=True)\n", + " elif event.type == \"response.completed\":\n", + " response = event.response\n", + "print()\n", + "utils.print_ok(\"Stream complete\")\n", + "print(f\"Response ID: {response.id}\")\n", + "\n", + "# Test 2: Benefits summary (streaming)\n", + "utils.print_info(\"\\nSending: Summarize the medical and dental plans for me.\")\n", + "stream2 = openai_client.responses.create(\n", + " input=\"Summarize the medical and dental plans for me.\",\n", + " metadata={\"conversation_id\": streaming_conversation_id},\n", + " stream=True,\n", + ")\n", + "response2 = None\n", + "for event in stream2:\n", + " if event.type == \"response.output_text.delta\":\n", + " print(event.delta, end=\"\", flush=True)\n", + " elif event.type == \"response.completed\":\n", + " response2 = event.response\n", + "print()\n", + "utils.print_ok(\"Stream complete\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a944a242", + "metadata": {}, + "outputs": [], + "source": [ + "# Streaming: multi-turn conversation\n", + "conversation_messages = [\n", + " \"What are the next Contoso holidays in the UK?\",\n", + " \"Great. Now summarize the vision plan.\",\n", + " \"And how much PTO does employee E24680 have left?\",\n", + "]\n", + "\n", + "previous_response_id = None\n", + "streaming_multi_turn_id = str(uuid.uuid4())\n", + "utils.print_info(f\"Streaming Conversation ID: {streaming_multi_turn_id}\")\n", + "\n", + "for i, message in enumerate(conversation_messages, 1):\n", + " utils.print_info(f\"\\n--- Turn {i} ---\")\n", + " utils.print_info(f\"User: {message}\")\n", + "\n", + " kwargs = {\n", + " \"input\": message,\n", + " \"metadata\": {\"conversation_id\": streaming_multi_turn_id},\n", + " \"stream\": True,\n", + " }\n", + " if previous_response_id:\n", + " kwargs[\"previous_response_id\"] = previous_response_id\n", + "\n", + " stream = openai_client.responses.create(**kwargs)\n", + " response = None\n", + " for event in stream:\n", + " if event.type == \"response.output_text.delta\":\n", + " print(event.delta, end=\"\", flush=True)\n", + " elif event.type == \"response.completed\":\n", + " response = event.response\n", + " print()\n", + " previous_response_id = response.id\n", + "\n", + " utils.print_ok(f\"Turn {i} complete\")\n", + "\n", + "utils.print_info(f\"\\nStreaming conversation completed ({len(conversation_messages)} turns)\")" + ] + }, + { + "cell_type": "markdown", + "id": "75cc2e7d", + "metadata": {}, + "source": [ + "\n", + "### 9️⃣ Cleanup\n", + "\n", + "Delete the hosted agent entirely (all versions + agent identity). Set `cleanup_enabled = True` to remove the agent.\n", + "\n", + "> **Note:** This removes the agent and its Entra ID identity from the Foundry project. The ACR image, RBAC role assignments, Foundry project, and other spoke resources are **not** affected. Orphaned RBAC assignments (for the deleted identity) are harmless and ignored by Azure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76897d41", + "metadata": {}, + "outputs": [], + "source": [ + "cleanup_enabled = False # Set to True to enable cleanup of the agent after testing\n", + "\n", + "if cleanup_enabled:\n", + " try:\n", + " result = project_client.agents.delete(agent_name=agent.name)\n", + " utils.print_ok(f\"Agent deleted: {agent.name} (deleted={result.get('deleted', True)})\")\n", + " except Exception as e:\n", + " utils.print_warning(f\"Failed to delete agent: {e}\")\n", + "else:\n", + " utils.print_info(f\"Cleanup is disabled. Set cleanup_enabled = True to remove the agent.\")\n", + " utils.print_info(f\"Agent retained: {agent.name} (version: {agent.version})\")" + ] + }, + { + "cell_type": "markdown", + "id": "0a8aa810", + "metadata": {}, + "source": [ + "## View AGT audit results in Application Insights\n", + "\n", + "AGT policy and audit decisions are exported as `agt.audit.event` trace records in Application Insights. To inspect denied policy decisions, open the Application Insights resource connected to the Foundry account, go to **Logs**, and run this KQL query:\n", + "\n", + "```kusto\n", + "traces\n", + "| where timestamp > ago(30m)\n", + "| where message == \"agt.audit.event\"\n", + "| extend cd = tostring(customDimensions)\n", + "| extend ce = tostring(customDimensions[\"agt.cloudevent\"])\n", + "| where cd has \"ai.agentmesh.policy.violation\"\n", + "| extend reason_double = extract(@\"'reason':\\s*\\\\?\"\"(.*?)\\\\?\"\",\\s*'message_preview'\", 1, ce)\n", + "| extend reason_single = extract(@\"'reason':\\s*'(.*?)',\\s*'message_preview'\", 1, ce)\n", + "| extend reason = iff(isnotempty(reason_double), reason_double, reason_single)\n", + "| extend matched_rule = extract(@\"'matched_rule':\\s*'([^']*)'\", 1, ce)\n", + "| extend message_preview_double = extract(@\"'message_preview':\\s*\\\\?\"\"(.*?)\\\\?\"\"\", 1, ce)\n", + "| extend message_preview_single = extract(@\"'message_preview':\\s*'(.*?)'\", 1, ce)\n", + "| extend message_preview = iff(isnotempty(message_preview_double), message_preview_double, message_preview_single)\n", + "| project timestamp, reason, matched_rule, message_preview, message, customDimensions\n", + "| order by timestamp desc\n", + "```\n", + "\n", + "These records appear in the App Insights `traces` table rather than as separate AGT spans in the Foundry trace tree.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.1)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.1" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/8. publish-and-use-a2a-endpoint.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/8. publish-and-use-a2a-endpoint.ipynb new file mode 100644 index 000000000..9567f6272 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/8. publish-and-use-a2a-endpoint.ipynb @@ -0,0 +1,1181 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "307d5946", + "metadata": {}, + "source": [ + "> **Note:** This notebook reuses patterns inspired by the Azure AI Gateway Labs (https://github.com/Azure-Samples/AI-Gateway).\n", + "\n", + "# 🤝 Citadel A2A Agent - Publish & Consume\n", + "\n", + "## Run, publish, and call a Foundry Agent-to-Agent (A2A) agent end-to-end!\n", + "\n", + "This notebook is fully reproducible from your current **azd environment** (`azd up` must have completed). It walks through the full lifecycle of running an agent on Microsoft Foundry, publishing it through the Citadel Governance Hub (APIM), and calling it as a governed A2A endpoint:\n", + "\n", + "1. **Resolves** the agent-hosting Foundry account (the `-0` account) and its project from azd — no hardcoded names.\n", + "2. **Enables public network access** on that Foundry account so this notebook can reach its data plane.\n", + "3. **Creates** a simple HR **prompt agent** (mocked HR policy data) on `gpt-4.1`.\n", + "4. **Enables** the agent's incoming **A2A endpoint** and **verifies** its agent card.\n", + "5. **Re-disables public network access** to restore the secure, private-only posture.\n", + "6. **Publishes** the agent through **APIM** as a governed **access contract** (product + subscription).\n", + "7. **Calls** the agent through APIM with just a subscription key — proving APIM reaches it over private endpoints even with public access disabled.\n", + "\n", + "Reference: [Enable an agent-to-agent endpoint](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/enable-agent-to-agent-endpoint?tabs=rest-powershell%2Cverify-bash%2Cconnection-bash) and [Import an A2A agent API](https://learn.microsoft.com/en-us/azure/api-management/agent-to-agent-api).\n", + "\n", + "> **Note:** This notebook assumes you have already deployed your Citadel Governance Hub and sample spoke. If you haven't done so, please follow the [Workshop Lab Guide](readme.md) (see [Lab 1 — Deploy Citadel to Your Azure Subscription](readme.md#lab-1)) before proceeding.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Workshop Python environment set up per the [readme](readme.md#51-set-up-python-environment) (`uv sync` or\n", + " `pip install -r requirements.txt`), which installs all dependencies this notebook uses.\n", + "- Azure CLI (`az`) logged in (`az login`) and `azd` initialized for the target environment.\n", + "- The Citadel hub deployed by `azd up` (APIM + two Foundry accounts).\n", + "- Permission to manage the Foundry account, create agents, and deploy to the hub resource group.\n" + ] + }, + { + "cell_type": "markdown", + "id": "7733f6f9", + "metadata": {}, + "source": [ + "## 1. Configuration\n", + "\n", + "This only sets the **agent identity** and **agent-card metadata**. All Azure resource names (Foundry account,\n", + "project, model, APIM) are resolved from your `azd` environment in later cells, so nothing here is environment-\n", + "specific. Override any default with the matching `A2A_*` / `AGENT_CARD_*` environment variable.\n", + "\n", + "| Setting | Purpose |\n", + "| --- | --- |\n", + "| `AGENT_NAME` | Name of the prompt agent this notebook creates and publishes. |\n", + "| `AGENT_*` / `SKILL_*` | Agent card metadata published to A2A callers. |\n", + "| `VERIFY_AGENT_CARD` | Whether to GET and validate the v1.0 card after enabling. |\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7f1f50d", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from urllib.parse import quote\n", + "\n", + "# --- Agent identity ------------------------------------------------------\n", + "# Name of the prompt agent this notebook creates and then publishes via A2A.\n", + "AGENT_NAME = os.environ.get(\"A2A_AGENT_NAME\", \"hr-assistant\")\n", + "\n", + "# --- Agent card metadata (published to A2A callers) ----------------------\n", + "AGENT_DESCRIPTION = os.environ.get(\n", + " \"AGENT_CARD_DESCRIPTION\",\n", + " \"Contoso HR assistant answering HR policy questions from mocked data\",\n", + ")\n", + "AGENT_VERSION = os.environ.get(\"AGENT_CARD_VERSION\", \"1.0\")\n", + "SKILL_ID = os.environ.get(\"AGENT_CARD_SKILL_ID\", \"hr-policy-qa\")\n", + "SKILL_NAME = os.environ.get(\"AGENT_CARD_SKILL_NAME\", \"HR Policy Q&A\")\n", + "SKILL_DESCRIPTION = os.environ.get(\n", + " \"AGENT_CARD_SKILL_DESCRIPTION\",\n", + " \"Answers HR questions about leave, benefits, and payroll using mocked policy data\",\n", + ")\n", + "\n", + "# --- Behavior flags ------------------------------------------------------\n", + "VERIFY_AGENT_CARD = os.environ.get(\"VERIFY_AGENT_CARD\", \"true\").lower() == \"true\"\n", + "SHOW_AGENT_CARD = os.environ.get(\"SHOW_AGENT_CARD\", \"true\").lower() == \"true\"\n", + "\n", + "# When True, the access-contract (product) policy logs the full A2A request and\n", + "# response bodies to the APIM Application Insights logger via the policy\n", + "# (see section 10.4). Leave False to keep only metadata/rate-limiting in the\n", + "# product policy. Logged message bodies may contain user content, so enable this\n", + "# only where capturing A2A payloads is acceptable for your governance/privacy posture.\n", + "ENABLE_MESSAGE_LOGGING = os.environ.get(\"A2A_ENABLE_MESSAGE_LOGGING\", \"false\").lower() == \"true\"\n", + "\n", + "# --- API version ---------------------------------------------------------\n", + "AGENT_API_VERSION = os.environ.get(\"AGENT_API_VERSION\", \"v1\")\n", + "\n", + "ESCAPED_AGENT_NAME = quote(AGENT_NAME, safe=\"\")\n", + "\n", + "\n", + "def _print_config():\n", + " rows = [\n", + " (\"Agent name\", AGENT_NAME),\n", + " (\"Description\", AGENT_DESCRIPTION),\n", + " (\"Card version\", AGENT_VERSION),\n", + " (\"Skill\", f\"{SKILL_NAME} ({SKILL_ID})\"),\n", + " (\"Skill details\", SKILL_DESCRIPTION),\n", + " (\"Agent API version\", AGENT_API_VERSION),\n", + " (\"Verify agent card\", \"yes\" if VERIFY_AGENT_CARD else \"no\"),\n", + " (\"Show agent card\", \"yes\" if SHOW_AGENT_CARD else \"no\"),\n", + " (\"Message logging\", \"yes\" if ENABLE_MESSAGE_LOGGING else \"no\"),\n", + " ]\n", + " label_w = max(len(label) for label, _ in rows)\n", + "\n", + " print(\"=\" * 70)\n", + " print(\"🤖 A2A AGENT CONFIGURATION\")\n", + " print(\"=\" * 70)\n", + " for label, value in rows:\n", + " print(f\" {label.rjust(label_w)} : {value}\")\n", + " print(\"=\" * 70)\n", + " print(\"ℹ️ All Azure resource names (Foundry account, project, model, APIM) are\")\n", + " print(\" resolved from your azd environment in the next cells — nothing here\")\n", + " print(\" is environment-specific.\")\n", + "\n", + "\n", + "_print_config()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2154ad90", + "metadata": {}, + "source": [ + "## 2. Helpers\n", + "\n", + "Small utilities:\n", + "\n", + "- `get_access_token(...)` wraps `az account get-access-token` (data-plane resource **or** management scope).\n", + "- `get_azd_value(...)` reads a value from the `azd` environment for auto-resolution.\n", + "- `join_uri_path(...)` joins URL segments safely.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bf916eb", + "metadata": {}, + "outputs": [], + "source": [ + "import shutil\n", + "import subprocess\n", + "import requests\n", + "\n", + "\n", + "def _run(cmd):\n", + " \"\"\"Run a CLI command and return trimmed stdout, or None on failure.\"\"\"\n", + " try:\n", + " result = subprocess.run(cmd, capture_output=True, text=True)\n", + " except FileNotFoundError:\n", + " return None\n", + " if result.returncode != 0:\n", + " return None\n", + " out = (result.stdout or \"\").strip()\n", + " return out or None\n", + "\n", + "\n", + "def get_access_token(resource=None, scope=None):\n", + " \"\"\"Acquire an Azure AD access token via the Azure CLI.\"\"\"\n", + " if resource:\n", + " cmd = [\"az\", \"account\", \"get-access-token\", \"--resource\", resource, \"--query\", \"accessToken\", \"-o\", \"tsv\"]\n", + " else:\n", + " cmd = [\"az\", \"account\", \"get-access-token\", \"--scope\", scope, \"--query\", \"accessToken\", \"-o\", \"tsv\"]\n", + " token = _run(cmd)\n", + " if not token:\n", + " raise RuntimeError(\"Failed to get an access token. Is the Azure CLI logged in (az login)?\")\n", + " return token\n", + "\n", + "\n", + "def get_azd_value(name):\n", + " \"\"\"Read a value from the azd environment, or None if unavailable.\"\"\"\n", + " if not shutil.which(\"azd\"):\n", + " return None\n", + " return _run([\"azd\", \"env\", \"get-value\", name])\n", + "\n", + "\n", + "def join_uri_path(root, path):\n", + " return f\"{root.rstrip('/')}/{path.lstrip('/')}\"\n", + "\n", + "\n", + "# Confirm the Azure CLI is logged in (equivalent to `az account show`).\n", + "if _run([\"az\", \"account\", \"show\", \"--query\", \"id\", \"-o\", \"tsv\"]) is None:\n", + " raise RuntimeError(\"Azure CLI is not logged in. Run `az login`, then re-run this cell.\")\n", + "print(\"Azure CLI session OK.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "c7b98d7f", + "metadata": {}, + "source": [ + "## 3. Resolve the Foundry environment from azd\n", + "\n", + "Everything below is derived from your current `azd` environment — no hardcoded resource names. We resolve\n", + "the **agent-hosting Foundry account** (the `-0` account from `LLM_BACKEND_CONFIG`), its **project**, the\n", + "**model deployment** (`gpt-4.1`), and build the project **endpoint** (`BASE_URL`). Override any value with the\n", + "matching `A2A_*` environment variable if needed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68ee1d85", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "RESOURCE_GROUP = os.environ.get(\"AZURE_RESOURCE_GROUP\") or get_azd_value(\"AZURE_RESOURCE_GROUP\")\n", + "SUBSCRIPTION_ID = os.environ.get(\"AZURE_SUBSCRIPTION_ID\") or get_azd_value(\"AZURE_SUBSCRIPTION_ID\")\n", + "if not RESOURCE_GROUP or not SUBSCRIPTION_ID:\n", + " raise ValueError(\"Could not resolve AZURE_RESOURCE_GROUP / AZURE_SUBSCRIPTION_ID from azd. Run `azd env refresh`.\")\n", + "\n", + "# Agents are hosted on the \"-0\" Foundry account. Resolve it from LLM_BACKEND_CONFIG (or override).\n", + "FOUNDRY_ACCOUNT_NAME = os.environ.get(\"A2A_FOUNDRY_ACCOUNT_NAME\")\n", + "if not FOUNDRY_ACCOUNT_NAME:\n", + " backends = json.loads(get_azd_value(\"LLM_BACKEND_CONFIG\") or \"[]\")\n", + " zero = [b for b in backends if str(b.get(\"backendId\", \"\")).endswith(\"-0\")]\n", + " if not zero:\n", + " raise ValueError(\"No '-0' Foundry backend found in LLM_BACKEND_CONFIG; set A2A_FOUNDRY_ACCOUNT_NAME.\")\n", + " FOUNDRY_ACCOUNT_NAME = zero[0][\"backendId\"]\n", + "\n", + "mgmt_token = get_access_token(scope=\"https://management.azure.com/.default\")\n", + "arm_account = (\n", + " f\"https://management.azure.com/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}\"\n", + " f\"/providers/Microsoft.CognitiveServices/accounts/{FOUNDRY_ACCOUNT_NAME}\"\n", + ")\n", + "\n", + "# Discover the project on that account (first project unless overridden).\n", + "FOUNDRY_PROJECT_NAME = os.environ.get(\"A2A_FOUNDRY_PROJECT_NAME\")\n", + "if not FOUNDRY_PROJECT_NAME:\n", + " projects = requests.get(\n", + " f\"{arm_account}/projects?api-version=2025-04-01-preview\",\n", + " headers={\"Authorization\": f\"Bearer {mgmt_token}\"},\n", + " ).json().get(\"value\", [])\n", + " if not projects:\n", + " raise ValueError(f\"No projects found on {FOUNDRY_ACCOUNT_NAME}; set A2A_FOUNDRY_PROJECT_NAME.\")\n", + " FOUNDRY_PROJECT_NAME = projects[0][\"name\"].split(\"/\")[-1]\n", + "\n", + "MODEL_DEPLOYMENT = os.environ.get(\"A2A_MODEL_DEPLOYMENT\", \"gpt-4.1\")\n", + "BASE_URL = f\"https://{FOUNDRY_ACCOUNT_NAME}.services.ai.azure.com/api/projects/{FOUNDRY_PROJECT_NAME}\"\n", + "\n", + "print(f\"Resource group : {RESOURCE_GROUP}\")\n", + "print(f\"Foundry account : {FOUNDRY_ACCOUNT_NAME}\")\n", + "print(f\"Foundry project : {FOUNDRY_PROJECT_NAME}\")\n", + "print(f\"Model deployment: {MODEL_DEPLOYMENT}\")\n", + "print(f\"Project endpoint: {BASE_URL}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "2db3eede", + "metadata": {}, + "source": [ + "## 4. Enable public network access on the agent's Foundry account\n", + "\n", + "After `azd up`, the `-0` Foundry account has **public network access disabled**, so this notebook can't reach\n", + "its data plane to create an agent or enable A2A. We enable it temporarily here and **re-disable it in step 9**\n", + "once the agent and its A2A endpoint are ready.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46e95456", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "\n", + "def set_foundry_public_network_access(enabled: bool):\n", + " \"\"\"Toggle public network access on the agent's Foundry account via ARM.\"\"\"\n", + " token = get_access_token(scope=\"https://management.azure.com/.default\")\n", + " body = {\n", + " \"properties\": {\n", + " \"publicNetworkAccess\": \"Enabled\" if enabled else \"Disabled\",\n", + " \"networkAcls\": {\"defaultAction\": \"Allow\" if enabled else \"Deny\"},\n", + " }\n", + " }\n", + " r = requests.patch(\n", + " f\"{arm_account}?api-version=2024-10-01\",\n", + " headers={\"Authorization\": f\"Bearer {token}\", \"Content-Type\": \"application/json\"},\n", + " json=body,\n", + " )\n", + " r.raise_for_status()\n", + " props = r.json().get(\"properties\", {})\n", + " return props.get(\"publicNetworkAccess\"), (props.get(\"networkAcls\") or {}).get(\"defaultAction\")\n", + "\n", + "\n", + "pna, acl = set_foundry_public_network_access(True)\n", + "print(f\"Public network access: {pna} (default action: {acl})\")\n", + "print(\"Waiting ~30s for the network change to propagate...\")\n", + "time.sleep(30)\n" + ] + }, + { + "cell_type": "markdown", + "id": "3dab90fb", + "metadata": {}, + "source": [ + "## 5. Create the HR prompt agent\n", + "\n", + "Create a simple **prompt agent** on `gpt-4.1` that answers HR questions using **mocked policy data** baked\n", + "into its instructions. This uses the Foundry Agents SDK (`PromptAgentDefinition`), the same pattern as\n", + "[4. citadel-agent-frameworks-tests.ipynb](4.%20citadel-agent-frameworks-tests.ipynb). A quick smoke test\n", + "confirms the agent responds before we expose it over A2A.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74a2beb3", + "metadata": {}, + "outputs": [], + "source": [ + "from azure.identity import DefaultAzureCredential\n", + "from azure.ai.projects import AIProjectClient\n", + "from azure.ai.projects.models import PromptAgentDefinition\n", + "\n", + "HR_INSTRUCTIONS = \"\"\"You are Contoso's HR assistant. Answer employee HR questions using ONLY the mocked\n", + "company policy data below. Be concise and friendly. If a question is outside this data, say you don't have\n", + "that information and suggest contacting hr@contoso.example.\n", + "\n", + "MOCKED CONTOSO HR POLICY DATA\n", + "- Annual leave: 25 paid vacation days per year, accrued monthly, up to 5 days carry-over.\n", + "- Sick leave: 10 paid sick days per year; a doctor's note is required after 3 consecutive days.\n", + "- Parental leave: 16 weeks paid for the primary caregiver, 6 weeks for the secondary caregiver.\n", + "- Health benefits: Contoso covers 90% of the medical premium and 100% of basic dental/vision.\n", + "- Retirement: 401(k) with a 6% company match, vesting over 3 years.\n", + "- Payroll: paid on the 15th and the last business day of each month.\n", + "- Open enrollment: runs the first two weeks of November each year.\n", + "- Remote work: up to 3 days per week remote with manager approval.\n", + "- Expense reimbursement: submit within 30 days via the MyExpense portal; meals capped at $75/day.\n", + "\"\"\"\n", + "\n", + "credential = DefaultAzureCredential()\n", + "project_client = AIProjectClient(endpoint=BASE_URL, credential=credential)\n", + "\n", + "# Create the agent. The Foundry network change may still be propagating, so retry briefly.\n", + "agent = None\n", + "last_err = None\n", + "for attempt in range(6):\n", + " try:\n", + " agent = project_client.agents.create_version(\n", + " agent_name=AGENT_NAME,\n", + " definition=PromptAgentDefinition(model=MODEL_DEPLOYMENT, instructions=HR_INSTRUCTIONS),\n", + " )\n", + " break\n", + " except Exception as e:\n", + " last_err = e\n", + " print(f\" attempt {attempt + 1}/6 failed: {str(e)[:140]} — retrying in 15s\")\n", + " time.sleep(15)\n", + "\n", + "if agent is None:\n", + " raise RuntimeError(f\"Failed to create agent after retries. Last error: {last_err}\")\n", + "\n", + "print(f\"Agent created: name={agent.name} version={agent.version} id={agent.id}\")\n", + "\n", + "# Smoke test: ask one HR question (public network access is currently enabled).\n", + "with project_client.get_openai_client() as oai:\n", + " conv = oai.conversations.create(\n", + " items=[{\"type\": \"message\", \"role\": \"user\",\n", + " \"content\": \"How many vacation days do I get and can I carry any over?\"}],\n", + " )\n", + " resp = oai.responses.create(\n", + " conversation=conv.id,\n", + " extra_body={\"agent_reference\": {\"name\": agent.name, \"type\": \"agent_reference\"}},\n", + " )\n", + " print(\"\\nSmoke test reply:\")\n", + " print(resp.output_text if hasattr(resp, \"output_text\") else str(resp))\n" + ] + }, + { + "cell_type": "markdown", + "id": "ecc5d1a4", + "metadata": {}, + "source": [ + "## 6. Enable the incoming A2A endpoint\n", + "\n", + "PATCH the agent with an **agent card** (description, version, skills) and an **agent endpoint** that advertises\n", + "the `responses` and `a2a` protocols. This is a data-plane call against the Foundry project endpoint (requires\n", + "public network access, enabled in step 4) and uses an `https://ai.azure.com` token.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c91351b", + "metadata": {}, + "outputs": [], + "source": [ + "agent_token = get_access_token(resource=\"https://ai.azure.com\")\n", + "\n", + "patch_uri = f\"{BASE_URL}/agents/{ESCAPED_AGENT_NAME}?api-version={AGENT_API_VERSION}\"\n", + "\n", + "body = {\n", + " \"agent_card\": {\n", + " \"description\": AGENT_DESCRIPTION,\n", + " \"version\": AGENT_VERSION,\n", + " \"skills\": [\n", + " {\n", + " \"id\": SKILL_ID,\n", + " \"name\": SKILL_NAME,\n", + " \"description\": SKILL_DESCRIPTION,\n", + " }\n", + " ],\n", + " },\n", + " \"agent_endpoint\": {\n", + " \"protocols\": [\"responses\", \"a2a\"],\n", + " },\n", + "}\n", + "\n", + "print(f\"PATCH {patch_uri}\")\n", + "response = requests.patch(\n", + " patch_uri,\n", + " headers={\"Authorization\": f\"Bearer {agent_token}\", \"Content-Type\": \"application/json\"},\n", + " json=body,\n", + ")\n", + "response.raise_for_status()\n", + "print(f\"Success ({response.status_code}). A2A endpoint enabled for agent '{AGENT_NAME}'.\")" + ] + }, + { + "cell_type": "markdown", + "id": "d720b0e2", + "metadata": {}, + "source": [ + "## 7. Resolve the agent card URLs\n", + "\n", + "Once the endpoint is enabled, the agent publishes its card at well-known paths. The `v1.0` card is the current\n", + "spec; `v0.3` is also exposed for older A2A clients.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9dba050e", + "metadata": {}, + "outputs": [], + "source": [ + "a2a_base_path = join_uri_path(BASE_URL, f\"agents/{ESCAPED_AGENT_NAME}/endpoint/protocols/a2a\")\n", + "agent_card_v10_url = join_uri_path(a2a_base_path, \"agentCard/v1.0\")\n", + "agent_card_v03_url = join_uri_path(a2a_base_path, \"agentCard/v0.3\")\n", + "\n", + "print(f\"A2A base path : {a2a_base_path}\")\n", + "print(f\"Agent card v1.0: {agent_card_v10_url}\")\n", + "print(f\"Agent card v0.3: {agent_card_v03_url}\")" + ] + }, + { + "cell_type": "markdown", + "id": "ce6783af", + "metadata": {}, + "source": [ + "## 8. Verify the v1.0 agent card\n", + "\n", + "GET the `v1.0` card with an authenticated request and confirm it reports a protocol version. Set\n", + "`SHOW_AGENT_CARD=true` (default) to print the full card. This is the last step that talks to Foundry directly\n", + "from the notebook — after this we re-disable public access.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ee2efed", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "\n", + "if VERIFY_AGENT_CARD:\n", + " card_response = requests.get(\n", + " agent_card_v10_url,\n", + " headers={\"Authorization\": f\"Bearer {agent_token}\"},\n", + " )\n", + " card_response.raise_for_status()\n", + " agent_card = card_response.json()\n", + "\n", + " # The protocol version may be top-level or nested under supportedInterfaces.\n", + " protocol_version = agent_card.get(\"protocolVersion\")\n", + " if not protocol_version:\n", + " interfaces = agent_card.get(\"supportedInterfaces\") or []\n", + " versions = sorted({i.get(\"protocolVersion\") for i in interfaces if i.get(\"protocolVersion\")})\n", + " protocol_version = \", \".join(versions) if versions else None\n", + "\n", + " if protocol_version:\n", + " print(f\"Verified agent '{agent_card.get('name')}' — A2A protocol version(s): {protocol_version}\")\n", + " else:\n", + " print(\"WARNING: the agent card response did not include a protocol version.\")\n", + "\n", + " if SHOW_AGENT_CARD:\n", + " print(json.dumps(agent_card, indent=2))\n", + "else:\n", + " print(\"Verification skipped (VERIFY_AGENT_CARD is false).\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "eba4e0db", + "metadata": {}, + "source": [ + "### Agent is A2A-ready\n", + "\n", + "The agent's incoming A2A endpoint is enabled and its v1.0 card is verified. Next we restore the secure posture\n", + "(step 9), then publish and call it through APIM (steps 10–11).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4190e2fa", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"A2A endpoint setup completed.\")" + ] + }, + { + "cell_type": "markdown", + "id": "8b9910cc", + "metadata": {}, + "source": [ + "## 9. Re-disable public network access (restore secure posture)\n", + "\n", + "The agent now has its A2A endpoint enabled and its card verified. Restore the original **private-only** posture\n", + "by disabling public network access on the Foundry account. The notebook can no longer reach the Foundry data\n", + "plane directly after this — but **APIM can**, over its private endpoint, which is exactly what the next sections\n", + "use.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65e219cc", + "metadata": {}, + "outputs": [], + "source": [ + "pna, acl = set_foundry_public_network_access(False)\n", + "print(f\"Public network access: {pna} (default action: {acl})\")\n", + "print(\"Foundry is now private-only. APIM will reach it via its private endpoint.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "39f6e601", + "metadata": {}, + "source": [ + "---\n", + "## 10. Expose the agent as an A2A agent through APIM (access-contract pattern)\n", + "\n", + "The cells above created the agent and enabled its **incoming** A2A endpoint on Foundry. Now we publish that\n", + "endpoint through the **Citadel Governance Hub (APIM)** and govern it with an **access contract** — the same\n", + "product + subscription pattern from [3. citadel-access-contracts-tests.ipynb](3.%20citadel-access-contracts-tests.ipynb).\n", + "\n", + "What these cells do:\n", + "\n", + "1. **Create an APIM API** that fronts the agent's A2A JSON-RPC runtime URL, with operations for the JSON-RPC\n", + " endpoint and the agent card.\n", + "2. **Attach an API policy** so APIM authenticates to the Foundry backend using its **managed identity**\n", + " (audience `https://ai.azure.com`).\n", + "3. **Create an access contract** (APIM **product** + **subscription**) bound to that API, reusing the repo's\n", + " `apimOnboardService.bicep` module — so callers need a subscription key, and you get governance\n", + " (rate-limiting, etc.) at the product scope.\n", + "4. **Call the agent through APIM** using the subscription key (agent card `GET` + JSON-RPC `message/send`).\n", + "\n", + "> **About the native \"A2A Agent\" API type:** the Azure portal offers a first-class *A2A Agent* import that\n", + "> rewrites the agent-card host and forces JSON-RPC transport\n", + "> ([docs](https://learn.microsoft.com/en-us/azure/api-management/agent-to-agent-api)). That import path is\n", + "> portal-only today. To keep this notebook fully reproducible over REST, we front the same backend as a\n", + "> **governed pass-through** and wrap it in an access contract — the calling experience (subscription key +\n", + "> JSON-RPC) is the same.\n", + "\n", + "> **Why this works with public access disabled:** APIM is VNet-integrated and reaches the Foundry account\n", + "> over a **private endpoint**, so steps 10–11 succeed even though we turned public access off in step 9. The\n", + "> APIM managed identity needs an Azure AI role (e.g. *Cognitive Services User*) on the agent's Foundry account\n", + "> — `azd up` already grants this for the hub APIM.\n" + ] + }, + { + "cell_type": "markdown", + "id": "1740e476", + "metadata": {}, + "source": [ + "### 10.1 Resolve APIM coordinates\n", + "\n", + "Initialize the shared `APIMClientTool` against the hub resource group (the same `AZURE_RESOURCE_GROUP` that\n", + "holds the agent's Foundry), and compute the agent's JSON-RPC runtime URL — the backend APIM will front.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9bb5a0e", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "sys.path.insert(1, \"../shared\") # make the shared helpers importable\n", + "from apimtools import APIMClientTool # noqa: E402\n", + "\n", + "# Publish through the APIM in the hub resource group (same azd env as the agent's Foundry).\n", + "# Override with A2A_HUB_RESOURCE_GROUP only if your APIM lives in a different RG.\n", + "GOV_HUB_RG = os.environ.get(\"A2A_HUB_RESOURCE_GROUP\") or RESOURCE_GROUP\n", + "\n", + "# Discover APIM in that resource group (name, gateway URL, subscription id).\n", + "apim_tool = APIMClientTool(GOV_HUB_RG)\n", + "apim_tool.initialize()\n", + "\n", + "APIM_NAME = str(apim_tool.apim_resource_name)\n", + "APIM_GATEWAY_URL = str(apim_tool.apim_resource_gateway_url).rstrip(\"/\")\n", + "HUB_SUBSCRIPTION_ID = str(apim_tool.subscription_id)\n", + "\n", + "# The agent's JSON-RPC A2A runtime URL = the backend APIM will front.\n", + "AGENT_A2A_RUNTIME_URL = join_uri_path(BASE_URL, f\"agents/{ESCAPED_AGENT_NAME}/endpoint/protocols/a2a\")\n", + "\n", + "# APIM API + access-contract identifiers (override via env if you like).\n", + "A2A_API_NAME = os.environ.get(\"A2A_APIM_API_NAME\", f\"{AGENT_NAME}-a2a\")\n", + "A2A_API_PATH = os.environ.get(\"A2A_APIM_API_PATH\", f\"{AGENT_NAME}-a2a\")\n", + "A2A_API_DISPLAY = os.environ.get(\"A2A_APIM_API_DISPLAY\", f\"{AGENT_NAME} A2A Agent\")\n", + "A2A_PRODUCT_ID = os.environ.get(\"A2A_PRODUCT_ID\", f\"A2A-{AGENT_NAME}-DEV\")\n", + "\n", + "print(f\"APIM resource group : {GOV_HUB_RG}\")\n", + "print(f\"APIM name : {APIM_NAME}\")\n", + "print(f\"APIM gateway URL : {APIM_GATEWAY_URL}\")\n", + "print(f\"Agent backend (A2A) : {AGENT_A2A_RUNTIME_URL}\")\n", + "print(f\"APIM A2A API : {A2A_API_NAME} (path: /{A2A_API_PATH})\")\n", + "print(f\"Access contract : {A2A_PRODUCT_ID}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "15c1eb8a", + "metadata": {}, + "source": [ + "### 10.2 Create the APIM API fronting the agent's A2A endpoint\n", + "\n", + "PUT an APIM API whose backend (`serviceUrl`) is the agent's A2A runtime URL, and add operations for the\n", + "JSON-RPC root (`POST /`) and the agent card (`GET /agentCard/v1.0`, `GET /agentCard/v0.3`). The API requires a\n", + "subscription key, so it can only be reached through an access-contract subscription.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6456bf3a", + "metadata": {}, + "outputs": [], + "source": [ + "APIM_MGMT_API_VERSION = os.environ.get(\"APIM_MGMT_API_VERSION\", \"2024-10-01-preview\")\n", + "\n", + "mgmt_token = get_access_token(scope=\"https://management.azure.com/.default\")\n", + "mgmt_headers = {\"Authorization\": f\"Bearer {mgmt_token}\", \"Content-Type\": \"application/json\"}\n", + "\n", + "apim_base = (\n", + " f\"https://management.azure.com/subscriptions/{HUB_SUBSCRIPTION_ID}\"\n", + " f\"/resourceGroups/{GOV_HUB_RG}/providers/Microsoft.ApiManagement/service/{APIM_NAME}\"\n", + ")\n", + "\n", + "# 1) Upsert the API that fronts the agent's A2A JSON-RPC backend.\n", + "api_uri = f\"{apim_base}/apis/{A2A_API_NAME}?api-version={APIM_MGMT_API_VERSION}\"\n", + "api_body = {\n", + " \"properties\": {\n", + " \"displayName\": A2A_API_DISPLAY,\n", + " \"path\": A2A_API_PATH,\n", + " \"protocols\": [\"https\"],\n", + " \"serviceUrl\": AGENT_A2A_RUNTIME_URL,\n", + " \"subscriptionRequired\": True,\n", + " \"subscriptionKeyParameterNames\": {\n", + " \"header\": \"Ocp-Apim-Subscription-Key\",\n", + " \"query\": \"subscription-key\",\n", + " },\n", + " }\n", + "}\n", + "r = requests.put(api_uri, headers=mgmt_headers, json=api_body)\n", + "r.raise_for_status()\n", + "print(f\"API upserted: {A2A_API_NAME} ({r.status_code})\")\n", + "\n", + "# 2) Operations: JSON-RPC POST at the root + agent-card GETs.\n", + "operations = {\n", + " \"a2a-jsonrpc\": {\"displayName\": \"A2A JSON-RPC\", \"method\": \"POST\", \"urlTemplate\": \"/\"},\n", + " \"get-agent-card-v1\": {\"displayName\": \"Agent card v1.0\", \"method\": \"GET\", \"urlTemplate\": \"/agentCard/v1.0\"},\n", + " \"get-agent-card-v03\": {\"displayName\": \"Agent card v0.3\", \"method\": \"GET\", \"urlTemplate\": \"/agentCard/v0.3\"},\n", + "}\n", + "for op_id, op in operations.items():\n", + " op_uri = f\"{apim_base}/apis/{A2A_API_NAME}/operations/{op_id}?api-version={APIM_MGMT_API_VERSION}\"\n", + " op_body = {\"properties\": {**op, \"templateParameters\": [], \"responses\": []}}\n", + " ro = requests.put(op_uri, headers=mgmt_headers, json=op_body)\n", + " ro.raise_for_status()\n", + " print(f\" operation: {op_id} -> {op['method']} {op['urlTemplate']} ({ro.status_code})\")\n", + "\n", + "print(\"\\nAPIM API and operations are ready.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0f1de286", + "metadata": {}, + "source": [ + "### 10.3 Attach the backend-auth API policy\n", + "\n", + "The agent's A2A endpoint requires a bearer token (audience `https://ai.azure.com`). This policy makes APIM mint\n", + "that token from its **managed identity** and forward it to the backend, so callers only need a subscription\n", + "key — never a Foundry token.\n", + "\n", + "> The APIM managed identity must hold an Azure AI role (e.g. *Cognitive Services User*) on the agent's Foundry\n", + "> account. `azd up` already grants this for the hub APIM. This notebook auto-detects whether the identity is\n", + "> system- or user-assigned and pins the `client-id` when needed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6490efa", + "metadata": {}, + "outputs": [], + "source": [ + "# Detect APIM's managed identity so the backend-auth policy targets the right one.\n", + "# - System-assigned: \n", + "# - User-assigned : same, plus client-id=\"\"\n", + "identity = requests.get(\n", + " f\"{apim_base}?api-version={APIM_MGMT_API_VERSION}\",\n", + " headers=mgmt_headers,\n", + ").json().get(\"identity\", {})\n", + "\n", + "identity_type = (identity.get(\"type\") or \"\").lower()\n", + "client_id_attr = \"\"\n", + "if \"userassigned\" in identity_type and not identity.get(\"principalId\"):\n", + " # No system-assigned identity available; pin to the first user-assigned identity.\n", + " uami = identity.get(\"userAssignedIdentities\") or {}\n", + " first = next(iter(uami.values()), {})\n", + " client_id = first.get(\"clientId\")\n", + " if not client_id:\n", + " raise RuntimeError(\"APIM has a user-assigned identity but no clientId was returned.\")\n", + " client_id_attr = f' client-id=\"{client_id}\"'\n", + " print(f\"Using user-assigned identity (client-id={client_id}).\")\n", + "else:\n", + " print(\"Using system-assigned identity.\")\n", + "\n", + "api_policy_xml = f\"\"\"\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\"\"\"\n", + "\n", + "policy_uri = f\"{apim_base}/apis/{A2A_API_NAME}/policies/policy?api-version={APIM_MGMT_API_VERSION}\"\n", + "rp = requests.put(\n", + " policy_uri,\n", + " headers=mgmt_headers,\n", + " json={\"properties\": {\"format\": \"rawxml\", \"value\": api_policy_xml}},\n", + ")\n", + "rp.raise_for_status()\n", + "print(f\"API policy applied ({rp.status_code}).\")\n", + "print(\"Backend auth: APIM managed identity -> https://ai.azure.com\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3fc9202b", + "metadata": {}, + "source": [ + "### 10.4 Create the access contract (product + subscription)\n", + "\n", + "Now wrap the API in an **access contract** using the repo's `apimOnboardService.bicep` module — the same\n", + "building block the Citadel access-contracts notebook deploys. It creates a published **product** bound to the\n", + "A2A API, applies a product-scope governance policy (rate-limiting), and issues a **subscription** whose key\n", + "callers use. We generate a `.bicepparam` and deploy it at resource-group scope.\n", + "\n", + "> **Optional message logging:** when `ENABLE_MESSAGE_LOGGING = True` (set in section 1), the product policy\n", + "> also logs the full A2A **request and response bodies** to the APIM **Application Insights** logger via the\n", + "> `` policy. These records land in App Insights `traces` (source `a2a-message-logging`) with the method,\n", + "> task id, and correlation id as custom dimensions. We use `` — not `log-to-eventhub` — because\n", + "> `appinsights-logger` is an Application Insights logger; `log-to-eventhub` only targets Event Hub loggers.\n", + "> Streaming (`text/event-stream`) responses are recorded as metadata only to avoid breaking SSE.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b1076f6", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "# Reuse the repo's access-contract building block (product + subscription bound to the API).\n", + "_slug = re.sub(r\"[^a-z0-9]+\", \"-\", AGENT_NAME.lower()).strip(\"-\")\n", + "contract_dir = f\"../bicep/infra/citadel-access-contracts/contracts/a2a-{_slug}/dev\"\n", + "os.makedirs(contract_dir, exist_ok=True)\n", + "\n", + "# --- Optional: A2A message logging (full request/response bodies) --------\n", + "# Logs to the APIM Application Insights logger via the policy, which targets\n", + "# the service-scope `applicationinsights` diagnostic (verbosity=information, 100% sampling)\n", + "# that the hub provisions. Records land in App Insights `traces` with custom dimensions.\n", + "# NOTE: `log-to-eventhub` only works with Event Hub loggers, NOT the `appinsights-logger`\n", + "# (which is an Application Insights logger) — so we use to reach App Insights.\n", + "# Streaming (text/event-stream) responses are not buffered, to avoid breaking SSE.\n", + "_message_logging_inbound = \"\"\"\n", + " \n", + " (preserveContent: true) ?? \"\")\" />\n", + " \n", + " (\"requestBody\", \"\");\n", + " if (string.IsNullOrEmpty(raw)) { return \"none\"; }\n", + " return JObject.Parse(raw)[\"method\"]?.ToString() ?? \"unknown\";\n", + " } catch { return \"parse-error\"; }\n", + " }\" />\n", + " (\"requestBody\", \"\");\n", + " if (string.IsNullOrEmpty(raw)) { return \"none\"; }\n", + " var p = JObject.Parse(raw)[\"params\"];\n", + " return p?[\"id\"]?.ToString()\n", + " ?? p?[\"taskId\"]?.ToString()\n", + " ?? p?[\"message\"]?[\"messageId\"]?.ToString()\n", + " ?? \"none\";\n", + " } catch { return \"parse-error\"; }\n", + " }\" />\n", + " \n", + " @($\"a2a-request method={context.Variables.GetValueOrDefault(\"a2aMethod\",\"none\")} taskId={context.Variables.GetValueOrDefault(\"a2aTaskId\",\"none\")}\")\n", + " \n", + " (\"correlationId\",\"-\"))\" />\n", + " \n", + " \n", + " (\"a2aMethod\",\"none\"))\" />\n", + " (\"a2aTaskId\",\"none\"))\" />\n", + " \n", + " (\"requestBody\",\"\")) ? \"(empty)\" : context.Variables.GetValueOrDefault(\"requestBody\",\"\"))\" />\n", + " \"\"\"\n", + "\n", + "_message_logging_outbound = \"\"\"\n", + " \n", + " \n", + " (\"isStream\") ? \"[streaming-response-not-captured]\" : (context.Response.Body?.As(preserveContent: true) ?? \"\"))\" />\n", + " \n", + " @($\"a2a-response status={context.Response.StatusCode} method={context.Variables.GetValueOrDefault(\"a2aMethod\",\"none\")}\")\n", + " \n", + " (\"correlationId\",\"-\"))\" />\n", + " \n", + " (\"a2aMethod\",\"none\"))\" />\n", + " (\"a2aTaskId\",\"none\"))\" />\n", + " \n", + " (\"isStream\").ToString())\" />\n", + " \n", + " (\"responseBody\",\"\")) ? \"(empty)\" : context.Variables.GetValueOrDefault(\"responseBody\",\"\"))\" />\n", + " \"\"\"\n", + "\n", + "_message_logging_onerror = \"\"\"\n", + " \n", + " \n", + " @($\"a2a-error reason={context.LastError?.Reason} method={context.Variables.GetValueOrDefault(\"a2aMethod\",\"none\")}\")\n", + " \n", + " (\"correlationId\",\"-\"))\" />\n", + " \n", + " (\"a2aMethod\",\"none\"))\" />\n", + " (\"a2aTaskId\",\"none\"))\" />\n", + " \n", + " \n", + " \n", + " \n", + " \"\"\"\n", + "\n", + "if ENABLE_MESSAGE_LOGGING:\n", + " print(\"A2A message logging: ENABLED (request/response bodies -> App Insights via ).\")\n", + "else:\n", + " _message_logging_inbound = _message_logging_outbound = _message_logging_onerror = \"\"\n", + " print(\"A2A message logging: disabled (set ENABLE_MESSAGE_LOGGING=True to capture bodies).\")\n", + "\n", + "# Product-scope governance policy (subscription key enforced by the product;\n", + "# message logging is added conditionally based on ENABLE_MESSAGE_LOGGING).\n", + "product_policy_xml = f\"\"\"\n", + " \n", + " \n", + " {_message_logging_inbound}\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " {_message_logging_outbound}\n", + " \n", + " \n", + " {_message_logging_onerror}\n", + " \n", + "\"\"\"\n", + "with open(os.path.join(contract_dir, \"ai-a2a-product-policy.xml\"), \"w\") as f:\n", + " f.write(product_policy_xml)\n", + "\n", + "subscription_name = f\"{A2A_PRODUCT_ID}-SUB-01\"\n", + "bicepparam = f\"\"\"using '../../../modules/apimOnboardService.bicep'\n", + "\n", + "param apimName = '{APIM_NAME}'\n", + "param productId = '{A2A_PRODUCT_ID}'\n", + "param productDisplayName = '{A2A_API_DISPLAY} Access Contract'\n", + "param productDescription = 'A2A access contract for agent {AGENT_NAME} (created from notebook)'\n", + "param productTerms = 'A2A access contract created from the enable-a2a-endpoint notebook.'\n", + "param apiNames = ['{A2A_API_NAME}']\n", + "param productPolicyXml = loadTextContent('ai-a2a-product-policy.xml')\n", + "param subscriptionName = '{subscription_name}'\n", + "param subscriptionDisplayName = '{subscription_name}'\n", + "\"\"\"\n", + "param_file = os.path.join(contract_dir, \"main.bicepparam\")\n", + "with open(param_file, \"w\") as f:\n", + " f.write(bicepparam)\n", + "\n", + "deployment_name = f\"a2a-access-contract-{time.strftime('%Y%m%d%H%M%S')}\"\n", + "cmd = [\n", + " \"az\", \"deployment\", \"group\", \"create\",\n", + " \"--name\", deployment_name,\n", + " \"--resource-group\", GOV_HUB_RG,\n", + " \"--parameters\", param_file,\n", + " \"-o\", \"json\",\n", + "]\n", + "print(f\"Deploying access contract '{A2A_PRODUCT_ID}' (product + subscription)...\")\n", + "proc = subprocess.run(cmd, capture_output=True, text=True)\n", + "if proc.returncode != 0:\n", + " raise RuntimeError(f\"Deployment failed:\\n{proc.stderr}\")\n", + "\n", + "# Fetch the subscription's primary key directly from APIM (reliable for secrets).\n", + "secrets_uri = (\n", + " f\"{apim_base}/subscriptions/{subscription_name}/listSecrets\"\n", + " f\"?api-version={APIM_MGMT_API_VERSION}\"\n", + ")\n", + "secrets_resp = requests.post(secrets_uri, headers=mgmt_headers)\n", + "secrets_resp.raise_for_status()\n", + "A2A_SUBSCRIPTION_KEY = secrets_resp.json()[\"primaryKey\"]\n", + "\n", + "# The API path is what we set when creating the API in section 10.2.\n", + "APIM_A2A_RUNTIME_BASE = join_uri_path(APIM_GATEWAY_URL, A2A_API_PATH)\n", + "\n", + "print(f\"Access contract ready : {A2A_PRODUCT_ID}\")\n", + "print(f\"Subscription : {subscription_name}\")\n", + "print(f\"APIM runtime base URL : {APIM_A2A_RUNTIME_BASE}\")\n", + "print(f\"Subscription key : {A2A_SUBSCRIPTION_KEY[:6]}... (stored in A2A_SUBSCRIPTION_KEY)\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "a8527fd1", + "metadata": {}, + "source": [ + "---\n", + "## 11. Call the agent through the APIM A2A endpoint\n", + "\n", + "With the access contract in place, callers reach the agent via APIM using only the **subscription key** — and\n", + "this works even though public network access on the Foundry account is **disabled** (step 9), because APIM\n", + "reaches it over a private endpoint. We do two calls:\n", + "\n", + "1. **Discover** — `GET` the agent card through APIM.\n", + "2. **Invoke** — `POST` an A2A JSON-RPC `message/send` request and read the agent's reply.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e8a5c1", + "metadata": {}, + "outputs": [], + "source": [ + "# 1) Discover: fetch the agent card through APIM using only the subscription key.\n", + "apim_card_url = join_uri_path(APIM_A2A_RUNTIME_BASE, \"agentCard/v1.0\")\n", + "print(f\"GET {apim_card_url}\")\n", + "\n", + "card_resp = requests.get(\n", + " apim_card_url,\n", + " headers={\"Ocp-Apim-Subscription-Key\": A2A_SUBSCRIPTION_KEY},\n", + ")\n", + "print(f\"Status: {card_resp.status_code}\")\n", + "if card_resp.ok:\n", + " card_json = card_resp.json()\n", + " print(f\"Agent name : {card_json.get('name')}\")\n", + " print(f\"protocolVersion : {card_json.get('protocolVersion')}\")\n", + " print(json.dumps(card_json, indent=2)[:1500])\n", + "else:\n", + " print(card_resp.text[:1000])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41f5bf4c", + "metadata": {}, + "outputs": [], + "source": [ + "import uuid\n", + "\n", + "apim_headers = {\n", + " \"Ocp-Apim-Subscription-Key\": A2A_SUBSCRIPTION_KEY,\n", + " \"Content-Type\": \"application/json\",\n", + "}\n", + "\n", + "\n", + "def extract_text(result):\n", + " \"\"\"Pull any text parts out of an A2A task/message result.\"\"\"\n", + " texts = []\n", + " for part in (result.get(\"parts\") or []):\n", + " if part.get(\"kind\") == \"text\" and part.get(\"text\"):\n", + " texts.append(part[\"text\"])\n", + " for artifact in (result.get(\"artifacts\") or []):\n", + " for part in (artifact.get(\"parts\") or []):\n", + " if part.get(\"kind\") == \"text\" and part.get(\"text\"):\n", + " texts.append(part[\"text\"])\n", + " for msg in (result.get(\"history\") or []):\n", + " for part in (msg.get(\"parts\") or []):\n", + " if part.get(\"kind\") == \"text\" and part.get(\"text\"):\n", + " texts.append(part[\"text\"])\n", + " status_msg = (result.get(\"status\") or {}).get(\"message\") or {}\n", + " for part in (status_msg.get(\"parts\") or []):\n", + " if part.get(\"kind\") == \"text\" and part.get(\"text\"):\n", + " texts.append(part[\"text\"])\n", + " return \"\\n\".join(texts)\n", + "\n", + "\n", + "# Invoke: send an A2A JSON-RPC message/send request through APIM.\n", + "a2a_request = {\n", + " \"jsonrpc\": \"2.0\",\n", + " \"id\": str(uuid.uuid4()),\n", + " \"method\": \"message/send\",\n", + " \"params\": {\n", + " \"message\": {\n", + " \"role\": \"user\",\n", + " \"parts\": [{\"kind\": \"text\", \"text\": \"How much parental leave does the primary caregiver get, and when is open enrollment?\"}],\n", + " \"messageId\": str(uuid.uuid4()),\n", + " \"kind\": \"message\",\n", + " }\n", + " },\n", + "}\n", + "\n", + "print(f\"POST {APIM_A2A_RUNTIME_BASE} (message/send)\")\n", + "invoke_resp = requests.post(APIM_A2A_RUNTIME_BASE, headers=apim_headers, json=a2a_request)\n", + "print(f\"Status: {invoke_resp.status_code}\")\n", + "invoke_resp.raise_for_status()\n", + "result = invoke_resp.json().get(\"result\", {})\n", + "\n", + "# message/send may return a final message or an async task. Poll the task if needed.\n", + "task_id = result.get(\"id\") if result.get(\"kind\") == \"task\" else None\n", + "state = (result.get(\"status\") or {}).get(\"state\")\n", + "\n", + "for _ in range(20):\n", + " if result.get(\"kind\") == \"message\" or state in (\"completed\", \"failed\", \"canceled\", \"rejected\"):\n", + " break\n", + " time.sleep(2)\n", + " poll = {\n", + " \"jsonrpc\": \"2.0\",\n", + " \"id\": str(uuid.uuid4()),\n", + " \"method\": \"tasks/get\",\n", + " \"params\": {\"id\": task_id},\n", + " }\n", + " poll_resp = requests.post(APIM_A2A_RUNTIME_BASE, headers=apim_headers, json=poll)\n", + " poll_resp.raise_for_status()\n", + " result = poll_resp.json().get(\"result\", {})\n", + " state = (result.get(\"status\") or {}).get(\"state\")\n", + " print(f\" task state: {state}\")\n", + "\n", + "reply = extract_text(result)\n", + "print(\"\\n--- agent says (via APIM) ---\")\n", + "print(reply if reply else json.dumps(result, indent=2)[:1500])\n" + ] + }, + { + "cell_type": "markdown", + "id": "88a1f056", + "metadata": {}, + "source": [ + "### Notes & troubleshooting\n", + "\n", + "- **`500` with backend \"name is valid, but no data of the requested type was found\"**: a DNS/network error —\n", + " APIM can't reach the agent's Foundry host. Ensure you publish through the APIM in the **same** azd\n", + " environment as the agent (its VNet has a private endpoint to that Foundry). `GOV_HUB_RG` defaults to\n", + " `AZURE_RESOURCE_GROUP`; override with `A2A_HUB_RESOURCE_GROUP` only if your APIM is elsewhere.\n", + "- **`500` with a managed-identity error in the trace**: APIM had no usable identity. This notebook auto-detects\n", + " system- vs user-assigned identity (section 10.3); if user-assigned, it pins the `client-id`.\n", + "- **`401/403` from the backend** (after a token is obtained): the APIM managed identity lacks an Azure AI\n", + " role on the agent's Foundry account. Grant it **Cognitive Services User** (or your tenant's equivalent\n", + " Azure AI role) on that account and retry. Role propagation can take a few minutes.\n", + "- **`401` at APIM** (before the backend): missing/invalid `Ocp-Apim-Subscription-Key`, or the subscription\n", + " isn't active. Re-run section 10.4.\n", + "- **`403 PublicNetworkAccess` when creating the agent (step 5)**: the network change from step 4 hasn't\n", + " propagated yet. The create cell retries; if it still fails, wait a minute and re-run step 5.\n", + "- **`404` on the agent card**: confirm the agent's A2A endpoint is enabled (steps 6–8) and that `BASE_URL`\n", + " matches your project/agent.\n", + "- **Reuse for other agents**: change `A2A_AGENT_NAME` (and optionally `A2A_PRODUCT_ID`) and re-run from\n", + " step 5 to create and publish another agent under its own access contract.\n" + ] + }, + { + "cell_type": "markdown", + "id": "fc05fc91", + "metadata": {}, + "source": [ + "\n", + "### 🧹 Cleanup (Optional)\n", + "\n", + "Remove the A2A access contract (APIM product + subscription) and the A2A API created during this notebook\n", + "session.\n", + "\n", + "> **Note:** This is **disabled by default**. Set `cleanup_enabled = True` in the next cell to remove the\n", + "> resources. This does not delete the Foundry agent itself (uncomment the optional block to do that, which\n", + "> requires re-enabling public network access).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a5ec6fa", + "metadata": {}, + "outputs": [], + "source": [ + "# Set to True to delete the A2A access contract and API created in this session\n", + "cleanup_enabled = False\n", + "\n", + "if cleanup_enabled:\n", + " # Remove the access-contract product (and its subscriptions)\n", + " utils_cmd = [\n", + " \"az\", \"apim\", \"product\", \"delete\",\n", + " \"-g\", GOV_HUB_RG, \"--service-name\", APIM_NAME,\n", + " \"--product-id\", A2A_PRODUCT_ID, \"--delete-subscriptions\", \"true\", \"--yes\",\n", + " ]\n", + " print(f\"Deleting product '{A2A_PRODUCT_ID}' and its subscriptions...\")\n", + " subprocess.run(utils_cmd, check=False)\n", + "\n", + " # Remove the A2A API\n", + " print(f\"Deleting API '{A2A_API_NAME}'...\")\n", + " subprocess.run(\n", + " [\"az\", \"apim\", \"api\", \"delete\", \"-g\", GOV_HUB_RG, \"--service-name\", APIM_NAME,\n", + " \"--api-id\", A2A_API_NAME, \"--yes\"],\n", + " check=False,\n", + " )\n", + "\n", + " # Optionally delete the agent version from Foundry (requires public access re-enabled).\n", + " # set_foundry_public_network_access(True)\n", + " # project_client.agents.delete_version(agent_name=AGENT_NAME, agent_version=agent.version)\n", + "\n", + " print(\"Cleanup completed!\")\n", + "else:\n", + " print(\"Cleanup is disabled. Set cleanup_enabled = True to remove the A2A access contract and API.\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.13)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb new file mode 100644 index 000000000..ec4ef1743 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/9. publish-and-use-hr-mcp-via-apim.ipynb @@ -0,0 +1,820 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> **Note:** This notebook follows the style of the Citadel workshop notebooks in this folder.\n", + "\n", + "# 👥 HR MCP via APIM — Publish, Validate, and Use with Microsoft Agent Framework\n", + "\n", + "This workshop demonstrates the HR Model Context Protocol (MCP) server through **Azure API Management (APIM) only**. It validates the Citadel-style APIM access contract, exercises MCP protocol/tool calls, demonstrates the APIM `5 tools/call per 30 seconds` policy, and shows a Microsoft Agent Framework (MAF) configuration that uses the APIM-published MCP endpoint with delegated Authorization and APIM subscription-key headers.\n", + "\n", + "## Prerequisites\n", + "\n", + "- Base HR MCP infrastructure has already been deployed by `workshop/mcp-hr/scripts/deploy-hr-mcp.*`.\n", + "- APIM/access-contract assets are available under `workshop/mcp-hr/infra/`.\n", + "- Publish/update APIM with `workshop/mcp-hr/scripts/publish-hr-mcp-apim.*` when needed.\n", + "- You are logged in with Azure CLI to the selected `azd` subscription/tenant.\n", + "- Your user can acquire a delegated token for `HR_MCP_SCOPE`.\n", + "\n", + "No direct Container Apps endpoint is resolved or called by this notebook.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 0️⃣ Initialize notebook variables from `azd env` and environment variables\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c53225e", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import json, os, subprocess, sys, time\n", + "from datetime import datetime, timezone\n", + "from pathlib import Path\n", + "from typing import Any\n", + "from urllib.parse import quote, urlparse, urlunparse\n", + "\n", + "try:\n", + " import requests\n", + "except ImportError as exc:\n", + " raise RuntimeError('Install workshop dependencies first: cd workshop && uv sync') from exc\n", + "\n", + "WORKSHOP_DIR = Path.cwd() if Path.cwd().name == 'workshop' else Path.cwd() / 'workshop'\n", + "if not WORKSHOP_DIR.exists():\n", + " WORKSHOP_DIR = Path.cwd()\n", + "\n", + "def run(cmd: list[str], timeout: int = 60) -> subprocess.CompletedProcess:\n", + " return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)\n", + "\n", + "def azd_get_value(name: str) -> str | None:\n", + " try:\n", + " r = run(['azd', 'env', 'get-value', name], timeout=20)\n", + " except FileNotFoundError:\n", + " return None\n", + " return r.stdout.strip() if r.returncode == 0 and r.stdout.strip() else None\n", + "\n", + "def first_value(*values: str | None) -> str | None:\n", + " for v in values:\n", + " if v and v.strip():\n", + " return v.strip()\n", + " return None\n", + "\n", + "def cfg(name: str, *fallbacks: str | None) -> str | None:\n", + " return first_value(os.getenv(name), azd_get_value(name), *fallbacks)\n", + "\n", + "def require(name: str, value: str | None) -> str:\n", + " if not value:\n", + " raise RuntimeError(f'Missing {name}. Set it in env/azd or run the HR MCP scripts first.')\n", + " return value\n", + "\n", + "def redact(value: str | None, keep: int = 4) -> str:\n", + " if not value:\n", + " return ''\n", + " return '' if len(value) <= keep * 2 else f'{value[:keep]}…{value[-keep:]}'\n", + "\n", + "def redacted_url(url: str | None) -> str:\n", + " if not url:\n", + " return ''\n", + " p = urlparse(url)\n", + " return urlunparse(p._replace(query='', fragment=''))\n", + "\n", + "AZURE_SUBSCRIPTION_ID = cfg('AZURE_SUBSCRIPTION_ID')\n", + "AZURE_RESOURCE_GROUP = cfg('AZURE_RESOURCE_GROUP')\n", + "HR_MCP_TENANT_ID = cfg('HR_MCP_TENANT_ID')\n", + "HR_MCP_SCOPE = cfg('HR_MCP_SCOPE')\n", + "HR_MCP_AUDIENCE = cfg('HR_MCP_AUDIENCE')\n", + "HR_MCP_REQUIRED_SCOPE_CLAIM = cfg('HR_MCP_REQUIRED_SCOPE_CLAIM', HR_MCP_SCOPE.rsplit('/', 1)[-1] if HR_MCP_SCOPE else None)\n", + "HR_MCP_APIM_NAME = cfg('HR_MCP_APIM_NAME', cfg('APIM_NAME'), cfg('AZURE_APIM_NAME'))\n", + "HR_MCP_APIM_RESOURCE_GROUP = cfg('HR_MCP_APIM_RESOURCE_GROUP', cfg('APIM_RESOURCE_GROUP'), cfg('AZURE_APIM_RESOURCE_GROUP'), AZURE_RESOURCE_GROUP)\n", + "HR_MCP_APIM_API_NAME = cfg('HR_MCP_APIM_API_NAME', 'hr-mcp-api')\n", + "HR_MCP_APIM_PATH = cfg('HR_MCP_APIM_PATH', 'hr-mcp')\n", + "HR_MCP_APIM_PRODUCT_ID = cfg('HR_MCP_APIM_PRODUCT_ID', 'MCP-HR-Tools-DEV')\n", + "HR_MCP_APIM_SUBSCRIPTION_NAME = cfg('HR_MCP_APIM_SUBSCRIPTION_NAME', 'MCP-HR-Tools-DEV-SUB-01')\n", + "HR_MCP_APIM_MCP_URL = cfg('HR_MCP_APIM_MCP_URL')\n", + "HR_MCP_LOG_ANALYTICS_WORKSPACE_ID = cfg('HR_MCP_LOG_ANALYTICS_WORKSPACE_ID', cfg('LOG_ANALYTICS_WORKSPACE_ID'))\n", + "\n", + "print('Configuration loaded (secrets redacted):')\n", + "for k, v in {\n", + " 'AZURE_SUBSCRIPTION_ID': AZURE_SUBSCRIPTION_ID,\n", + " 'AZURE_RESOURCE_GROUP': AZURE_RESOURCE_GROUP,\n", + " 'HR_MCP_TENANT_ID': HR_MCP_TENANT_ID,\n", + " 'HR_MCP_SCOPE': HR_MCP_SCOPE,\n", + " 'HR_MCP_AUDIENCE': HR_MCP_AUDIENCE,\n", + " 'HR_MCP_APIM_NAME': HR_MCP_APIM_NAME,\n", + " 'HR_MCP_APIM_RESOURCE_GROUP': HR_MCP_APIM_RESOURCE_GROUP,\n", + " 'HR_MCP_APIM_PRODUCT_ID': HR_MCP_APIM_PRODUCT_ID,\n", + " 'HR_MCP_APIM_SUBSCRIPTION_NAME': HR_MCP_APIM_SUBSCRIPTION_NAME,\n", + " 'HR_MCP_APIM_MCP_URL': redacted_url(HR_MCP_APIM_MCP_URL),\n", + "}.items():\n", + " print(f' {k}: {v or \"\"}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "9271289f", + "metadata": {}, + "source": [ + "## 1️⃣ Verify Azure CLI login and subscription\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "869a5e32", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "try:\n", + " account_result = run(['az', 'account', 'show', '-o', 'json'], timeout=30)\n", + "except FileNotFoundError as exc:\n", + " raise RuntimeError('Azure CLI is required for this notebook.') from exc\n", + "if account_result.returncode != 0:\n", + " raise RuntimeError('Azure CLI is not logged in. Run `az login`, then rerun this cell.')\n", + "account = json.loads(account_result.stdout)\n", + "if AZURE_SUBSCRIPTION_ID and account.get('id') != AZURE_SUBSCRIPTION_ID:\n", + " set_result = run(['az', 'account', 'set', '--subscription', AZURE_SUBSCRIPTION_ID], timeout=30)\n", + " if set_result.returncode != 0:\n", + " raise RuntimeError(set_result.stderr.strip())\n", + " account = json.loads(run(['az', 'account', 'show', '-o', 'json'], timeout=30).stdout)\n", + "print('Azure CLI login OK')\n", + "print(f\" User: {account.get('user', {}).get('name', '')}\")\n", + "print(f\" Tenant: {account.get('tenantId')}\")\n", + "print(f\" Subscription: {account.get('name')} ({account.get('id')})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "c9844087", + "metadata": {}, + "source": [ + "## 2️⃣ Resolve APIM MCP endpoint and access-contract settings\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be55bef9", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def resolve_apim_mcp_url() -> str:\n", + " explicit = cfg('HR_MCP_APIM_MCP_URL')\n", + " if explicit:\n", + " p = urlparse(explicit)\n", + " if not p.scheme or not p.netloc:\n", + " raise RuntimeError(f'HR_MCP_APIM_MCP_URL is not absolute: {explicit!r}')\n", + " return explicit.rstrip('/') if p.path.rstrip('/').endswith('/mcp') else explicit.rstrip('/') + '/mcp'\n", + " apim_name = require('HR_MCP_APIM_NAME', HR_MCP_APIM_NAME)\n", + " apim_rg = require('HR_MCP_APIM_RESOURCE_GROUP', HR_MCP_APIM_RESOURCE_GROUP)\n", + " r = run(['az', 'apim', 'show', '--name', apim_name, '--resource-group', apim_rg, '--query', 'gatewayUrl', '-o', 'tsv'], timeout=60)\n", + " if r.returncode != 0 or not r.stdout.strip():\n", + " raise RuntimeError(f'Could not resolve APIM gatewayUrl: {r.stderr.strip()}')\n", + " return f\"{r.stdout.strip().rstrip('/')}/{HR_MCP_APIM_PATH or 'hr-mcp'}/mcp\"\n", + "\n", + "HR_MCP_APIM_MCP_URL = resolve_apim_mcp_url()\n", + "contract = {\n", + " 'apimName': HR_MCP_APIM_NAME,\n", + " 'apimResourceGroup': HR_MCP_APIM_RESOURCE_GROUP,\n", + " 'apiName': HR_MCP_APIM_API_NAME,\n", + " 'apiPath': HR_MCP_APIM_PATH,\n", + " 'productId': HR_MCP_APIM_PRODUCT_ID,\n", + " 'subscriptionName': HR_MCP_APIM_SUBSCRIPTION_NAME,\n", + " 'tenantId': HR_MCP_TENANT_ID,\n", + " 'jwtAudience': HR_MCP_AUDIENCE,\n", + " 'requiredScope': HR_MCP_REQUIRED_SCOPE_CLAIM,\n", + " 'mcpUrl': redacted_url(HR_MCP_APIM_MCP_URL),\n", + "}\n", + "print(json.dumps(contract, indent=2))\n" + ] + }, + { + "cell_type": "markdown", + "id": "3f33715a", + "metadata": {}, + "source": [ + "## 3️⃣ Acquire participant delegated Entra token without printing it\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c12c74b9", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def acquire_participant_token() -> str:\n", + " existing = os.getenv('HR_MCP_ACCESS_TOKEN')\n", + " if existing:\n", + " return existing\n", + " scope = require('HR_MCP_SCOPE', HR_MCP_SCOPE)\n", + " cmd = ['az', 'account', 'get-access-token', '--scope', scope, '--query', 'accessToken', '-o', 'tsv']\n", + " if HR_MCP_TENANT_ID:\n", + " cmd[3:3] = ['--tenant', HR_MCP_TENANT_ID]\n", + " r = run(cmd, timeout=60)\n", + " if r.returncode != 0 or not r.stdout.strip():\n", + " raise RuntimeError('Could not acquire delegated token. Run az login and verify HR_MCP_SCOPE/HR_MCP_TENANT_ID.')\n", + " return r.stdout.strip()\n", + "\n", + "HR_MCP_ACCESS_TOKEN = acquire_participant_token()\n", + "print(f'Delegated token acquired: {redact(HR_MCP_ACCESS_TOKEN)}')\n", + "print('Token value is intentionally not printed.')\n" + ] + }, + { + "cell_type": "markdown", + "id": "4494ea77", + "metadata": {}, + "source": [ + "## 4️⃣ Create the Citadel-style APIM access contract\n", + "\n", + "This cell runs `publish-hr-mcp-apim` to create the APIM MCP API, backend, Citadel product, subscription, and policies. It assumes the base HR MCP infrastructure was already deployed by `deploy-hr-mcp`. Set `RUN_APIM_PUBLISH=False` only if you want to skip deployment and just validate that the infra assets exist.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6636d91a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Set to False to skip deployment and only validate that infra assets exist.\n", + "RUN_APIM_PUBLISH = True\n", + "publish_script = WORKSHOP_DIR / 'mcp-hr' / 'scripts' / ('publish-hr-mcp-apim.ps1' if os.name == 'nt' else 'publish-hr-mcp-apim.sh')\n", + "if not publish_script.exists():\n", + " raise RuntimeError(f'Publish script not found: {publish_script}')\n", + "if RUN_APIM_PUBLISH:\n", + " cmd = ['pwsh', '-File', str(publish_script)] if os.name == 'nt' else ['bash', str(publish_script)]\n", + " r = run(cmd, timeout=900)\n", + " if r.stdout:\n", + " print(r.stdout)\n", + " if r.stderr:\n", + " print(r.stderr)\n", + " if r.returncode != 0:\n", + " raise RuntimeError(f'publish-hr-mcp-apim failed (exit {r.returncode}). See output above.')\n", + " print('APIM access contract published. In the portal verify: API \"hr-mcp-api\", product \"MCP-HR-Tools-DEV\", subscription \"MCP-HR-Tools-DEV-SUB-01\".')\n", + "else:\n", + " print('Skipped (RUN_APIM_PUBLISH=False). Set RUN_APIM_PUBLISH=True to create the APIM access contract.')\n", + "for rel in ['mcp-hr/infra/main.bicep', 'mcp-hr/infra/policies/hr-mcp-api-policy.xml', 'mcp-hr/infra/policies/hr-mcp-product-policy.xml']:\n", + " p = WORKSHOP_DIR / rel\n", + " print(f\"{'OK' if p.exists() else 'MISSING'} {rel}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "4057cec3", + "metadata": {}, + "source": [ + "## 5️⃣ Resolve APIM subscription key without printing it\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1762a2c", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "def resolve_subscription_key() -> str:\n", + " existing = os.getenv('HR_MCP_APIM_SUBSCRIPTION_KEY')\n", + " if existing:\n", + " return existing\n", + " apim_name = require('HR_MCP_APIM_NAME', HR_MCP_APIM_NAME)\n", + " apim_rg = require('HR_MCP_APIM_RESOURCE_GROUP', HR_MCP_APIM_RESOURCE_GROUP)\n", + " sid = require('HR_MCP_APIM_SUBSCRIPTION_NAME', HR_MCP_APIM_SUBSCRIPTION_NAME)\n", + " r = run(['az', 'apim', 'subscription', 'show', '--resource-group', apim_rg, '--service-name', apim_name, '--sid', sid, '--query', 'primaryKey', '-o', 'tsv'], timeout=60)\n", + " if r.returncode == 0 and r.stdout.strip():\n", + " return r.stdout.strip()\n", + " sub_id = AZURE_SUBSCRIPTION_ID or json.loads(run(['az', 'account', 'show', '-o', 'json']).stdout)['id']\n", + " uri = ('https://management.azure.com/subscriptions/' + quote(sub_id, safe='') + '/resourceGroups/' + quote(apim_rg, safe='') + '/providers/Microsoft.ApiManagement/service/' + quote(apim_name, safe='') + '/subscriptions/' + quote(sid, safe='') + '/listSecrets?api-version=2024-06-01-preview')\n", + " rest = run(['az', 'rest', '--method', 'post', '--url', uri, '--query', 'primaryKey', '-o', 'tsv'], timeout=60)\n", + " if rest.returncode == 0 and rest.stdout.strip():\n", + " return rest.stdout.strip()\n", + " raise RuntimeError('Could not retrieve APIM subscription key. Set HR_MCP_APIM_SUBSCRIPTION_KEY without printing it.')\n", + "\n", + "HR_MCP_APIM_SUBSCRIPTION_KEY = resolve_subscription_key()\n", + "print(f'APIM subscription key resolved: {redact(HR_MCP_APIM_SUBSCRIPTION_KEY)}')\n" + ] + }, + { + "cell_type": "markdown", + "id": "f963e258", + "metadata": {}, + "source": [ + "## 6️⃣ APIM-only MCP JSON-RPC helpers\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e80efff9", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "MCP_HEADERS = {\n", + " 'Authorization': f'Bearer {HR_MCP_ACCESS_TOKEN}',\n", + " 'Ocp-Apim-Subscription-Key': HR_MCP_APIM_SUBSCRIPTION_KEY,\n", + " 'Content-Type': 'application/json',\n", + " 'Accept': 'application/json',\n", + "}\n", + "EXPECTED_TOOLS = {'search_employees', 'get_employee_profile', 'recommend_learning_path', 'submit_pto_request', 'update_employee_skills'}\n", + "\n", + "def rpc_payload(method: str, params: dict[str, Any] | None = None, request_id: str | int | None = None) -> dict[str, Any]:\n", + " payload = {'jsonrpc': '2.0', 'method': method}\n", + " if request_id is not None:\n", + " payload['id'] = request_id\n", + " if params is not None:\n", + " payload['params'] = params\n", + " return payload\n", + "\n", + "def mcp_post(method: str, params: dict[str, Any] | None = None, request_id: str | int | None = None, timeout: int = 30) -> requests.Response:\n", + " return requests.post(HR_MCP_APIM_MCP_URL, headers=MCP_HEADERS, json=rpc_payload(method, params, request_id), timeout=timeout)\n", + "\n", + "def parse_rpc(response: requests.Response, label: str) -> dict[str, Any]:\n", + " if response.status_code != 200:\n", + " raise RuntimeError(f'{label} returned HTTP {response.status_code}: {response.text[:240]}')\n", + " payload = response.json()\n", + " if 'error' in payload:\n", + " raise RuntimeError(f\"{label} returned JSON-RPC error: {payload['error']}\")\n", + " result = payload.get('result')\n", + " if not isinstance(result, dict):\n", + " raise RuntimeError(f'{label} did not return a result object: {payload}')\n", + " return result\n", + "\n", + "def decode_tool_text(result: dict[str, Any]) -> dict[str, Any]:\n", + " content = result.get('content')\n", + " if not isinstance(content, list) or not content:\n", + " raise RuntimeError('MCP tool result contained no content.')\n", + " first = content[0]\n", + " if not isinstance(first, dict) or first.get('type') != 'text':\n", + " raise RuntimeError(f'Unexpected MCP content shape: {content}')\n", + " return json.loads(first.get('text') or '{}')\n", + "\n", + "print(f'MCP endpoint: {redacted_url(HR_MCP_APIM_MCP_URL)}')\n", + "print('Headers prepared: Authorization=, Ocp-Apim-Subscription-Key=')\n" + ] + }, + { + "cell_type": "markdown", + "id": "187d54e7", + "metadata": {}, + "source": [ + "## 7️⃣ Test initialize, tools/list, read tool, and write tool through APIM only\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f2c5108", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "initialize_result = parse_rpc(mcp_post('initialize', {}, 1), 'initialize')\n", + "print('initialize OK')\n", + "print(json.dumps({'protocolVersion': initialize_result.get('protocolVersion'), 'serverInfo': initialize_result.get('serverInfo')}, indent=2))\n", + "\n", + "tools_result = parse_rpc(mcp_post('tools/list', {}, 2), 'tools/list')\n", + "tool_names = {t.get('name') for t in tools_result.get('tools', []) if isinstance(t, dict)}\n", + "missing, extra = EXPECTED_TOOLS - tool_names, tool_names - EXPECTED_TOOLS\n", + "if missing or extra:\n", + " raise RuntimeError(f'Unexpected tools. Missing={sorted(missing)} Extra={sorted(extra)}')\n", + "print('tools/list OK:', sorted(tool_names))\n", + "\n", + "profile = decode_tool_text(parse_rpc(mcp_post('tools/call', {'name': 'get_employee_profile', 'arguments': {'employee_id': 'E1001'}}, 'read-profile'), 'get_employee_profile'))\n", + "print('Read tool OK:', json.dumps({'employee_id': profile.get('employee_id'), 'name': profile.get('name'), 'role': profile.get('role'), 'pto': profile.get('pto')}, indent=2))\n", + "\n", + "skill_update = decode_tool_text(parse_rpc(mcp_post('tools/call', {'name': 'update_employee_skills', 'arguments': {'employee_id': 'E1001', 'skills': ['apim-workshop-validation'], 'evidence_note': 'Updated through APIM-mediated MCP workshop notebook.'}}, 'write-skills'), 'update_employee_skills'))\n", + "print('Write tool OK:', json.dumps({'employee_id': skill_update.get('skill_update', {}).get('employee_id'), 'current_skills_contains_marker': 'apim-workshop-validation' in skill_update.get('current_skills', [])}, indent=2))\n" + ] + }, + { + "cell_type": "markdown", + "id": "3ce776a8", + "metadata": {}, + "source": [ + "## 8️⃣ Demonstrate the APIM `tools/call` rate limit\n", + "\n", + "The policy should allow up to 5 rapid `tools/call` requests per participant per 30 seconds and return HTTP `429` for the excess request.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cce3354", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "WAIT_FOR_FRESH_RATE_WINDOW_SECONDS = 31\n", + "print(f'Waiting {WAIT_FOR_FRESH_RATE_WINDOW_SECONDS}s for a fresh APIM rate-limit window...')\n", + "time.sleep(WAIT_FOR_FRESH_RATE_WINDOW_SECONDS)\n", + "statuses = []\n", + "for i in range(6):\n", + " response = mcp_post('tools/call', {'name': 'get_employee_profile', 'arguments': {'employee_id': 'E1001'}}, f'rate-limit-{i}', timeout=20)\n", + " statuses.append(response.status_code)\n", + " if response.status_code == 429:\n", + " print(f\"Call {i + 1}: HTTP 429 as expected. Retry-After={response.headers.get('Retry-After', '')}\")\n", + " break\n", + " parse_rpc(response, f'rate-limit call {i + 1}')\n", + " print(f'Call {i + 1}: HTTP 200')\n", + "if 429 not in statuses:\n", + " raise RuntimeError(f'Expected 429 after more than 5 rapid tools/call requests; saw {statuses}. Confirm the product policy is attached.')\n", + "print('Interpretation: APIM rejected the excess tool call before backend execution.')\n" + ] + }, + { + "cell_type": "markdown", + "id": "632962bd", + "metadata": {}, + "source": [ + "## 9️⃣ Inspect APIM request/response body logs\n", + "\n", + "Use the smoke-test script or KQL. Logs should include bounded request/response snippets and must not include bearer tokens or subscription keys.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c41e19ea", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "print('Optional smoke tests from repository root:')\n", + "print(' cd workshop && uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py --skip-rate-limit')\n", + "print(' cd workshop && uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py')\n", + "\n", + "kql = '''\n", + "AzureDiagnostics\n", + "| where TimeGenerated > ago(1h)\n", + "| where Category has \"Gateway\"\n", + "| where requestUrl_s has \"/hr-mcp/mcp\" or Message has \"hr-mcp-apim\" or trace_s has \"hr-mcp-apim\"\n", + "| project TimeGenerated, OperationName, requestUrl_s, responseCode_d, Message, trace_s\n", + "| order by TimeGenerated desc\n", + "| take 25\n", + "'''.strip()\n", + "print('\\nKQL guidance:')\n", + "print(kql)\n", + "if HR_MCP_LOG_ANALYTICS_WORKSPACE_ID:\n", + " r = run(['az', 'monitor', 'log-analytics', 'query', '--workspace', HR_MCP_LOG_ANALYTICS_WORKSPACE_ID, '--analytics-query', kql, '-o', 'table'], timeout=90)\n", + " print(r.stdout if r.returncode == 0 else 'Log query did not complete; verify APIM diagnostics route gateway logs/traces to this workspace.')\n", + "else:\n", + " print('\\nSet HR_MCP_LOG_ANALYTICS_WORKSPACE_ID to run this query directly.')\n" + ] + }, + { + "cell_type": "markdown", + "id": "401671e1", + "metadata": {}, + "source": [ + "## 🔟 Deploy a Foundry hosted agent that uses the HR MCP via APIM\n", + "\n", + "This mirrors the workshop hosted-agent (notebook #7): we build the agent image with ACR, deploy it as a\n", + "Foundry **hosted agent** in the spoke project, and invoke it. The difference is the agent's tools come from\n", + "the **HR MCP server, published through APIM** (`hr_mcp_via_apim`), instead of local Python tools.\n", + "\n", + "The agent authenticates to APIM with its **managed identity**: every request carries a fresh Entra token for\n", + "the HR MCP API (application role `Mcp.Invoke`) plus the APIM subscription key. Source: `workshop/mcp-hr/hosted-agent/`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a2ad41e", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Resolve spoke / ACR / Foundry and HR MCP values from azd.\n", + "env_subscription_id = require('AZURE_SUBSCRIPTION_ID', cfg('AZURE_SUBSCRIPTION_ID'))\n", + "spoke_resource_group = require('SPOKE_RESOURCE_GROUP', cfg('SPOKE_RESOURCE_GROUP'))\n", + "spoke_foundry_account = require('SPOKE_AI_FOUNDRY_ACCOUNT_NAME', cfg('SPOKE_AI_FOUNDRY_ACCOUNT_NAME'))\n", + "spoke_foundry_project = require('SPOKE_AI_FOUNDRY_PROJECT_NAME', cfg('SPOKE_AI_FOUNDRY_PROJECT_NAME'))\n", + "spoke_acr_name = require('SPOKE_ACR_NAME', cfg('SPOKE_ACR_NAME'))\n", + "spoke_acr_login = require('SPOKE_ACR_LOGIN_SERVER', cfg('SPOKE_ACR_LOGIN_SERVER'))\n", + "\n", + "hosted_agent_name = cfg('HR_MCP_AGENT_NAME', 'citadel-hr-mcp-agent')\n", + "hosted_agent_tag = cfg('HR_MCP_AGENT_IMAGE_TAG', datetime.now(timezone.utc).strftime('v%Y%m%d%H%M%S'))\n", + "hosted_agent_model = cfg('HR_MCP_AGENT_MODEL', 'Hub-HR-ChatAgent-DEV-LLM/gpt-4.1') # same model as notebook #7\n", + "hosted_agent_image = f\"{spoke_acr_login}/{hosted_agent_name}:{hosted_agent_tag}\"\n", + "foundry_project_endpoint = f\"https://{spoke_foundry_account}.services.ai.azure.com/api/projects/{spoke_foundry_project}\"\n", + "\n", + "# HR MCP (APIM) connection details the agent needs.\n", + "HR_MCP_AUDIENCE = require('HR_MCP_AUDIENCE', HR_MCP_AUDIENCE)\n", + "HR_MCP_APP_ROLE_VALUE = cfg('HR_MCP_APP_ROLE_VALUE', 'Mcp.Invoke')\n", + "HR_MCP_APP_ROLE_ID = require('HR_MCP_APP_ROLE_ID', cfg('HR_MCP_APP_ROLE_ID'))\n", + "HR_MCP_API_CLIENT_ID = require('HR_MCP_API_CLIENT_ID', cfg('HR_MCP_API_CLIENT_ID'))\n", + "HR_MCP_APIM_SUBSCRIPTION_KEY = resolve_subscription_key() # defined earlier; never printed\n", + "\n", + "agent_source_dir = (WORKSHOP_DIR / 'mcp-hr' / 'hosted-agent')\n", + "if not (agent_source_dir / 'main.py').exists():\n", + " raise RuntimeError(f'Hosted agent source not found: {agent_source_dir}')\n", + "\n", + "print('Hosted agent configuration:')\n", + "print(f' name: {hosted_agent_name}')\n", + "print(f' image: {hosted_agent_image}')\n", + "print(f' model: {hosted_agent_model}')\n", + "print(f' foundry endpoint: {foundry_project_endpoint}')\n", + "print(f' MCP endpoint: {redacted_url(HR_MCP_APIM_MCP_URL)}')\n", + "print(f' MCP audience: {HR_MCP_AUDIENCE}')\n", + "print(f' app role: {HR_MCP_APP_ROLE_VALUE}')\n", + "print(f' subscription key: {redact(HR_MCP_APIM_SUBSCRIPTION_KEY)}')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56cfdd9b", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Mirror base images into the spoke ACR so the build does not pull from Docker Hub\n", + "# every time (avoids the anonymous pull-rate limit). Falls back to public images.\n", + "base_image_ref, uv_image_ref = 'python:3.13-slim', 'ghcr.io/astral-sh/uv:latest'\n", + "if run(['az','acr','import','--name',spoke_acr_name,'--source','docker.io/library/python:3.13-slim','--image','python:3.13-slim','--force','--only-show-errors','-o','none'], timeout=300).returncode == 0:\n", + " base_image_ref = f'{spoke_acr_login}/python:3.13-slim'\n", + "else:\n", + " print('Note: could not import python:3.13-slim into the spoke ACR; building from Docker Hub (may hit rate limits).')\n", + "if run(['az','acr','import','--name',spoke_acr_name,'--source','ghcr.io/astral-sh/uv:latest','--image','astral-sh/uv:latest','--force','--only-show-errors','-o','none'], timeout=300).returncode == 0:\n", + " uv_image_ref = f'{spoke_acr_login}/astral-sh/uv:latest'\n", + "else:\n", + " print('Note: could not import the uv image into the spoke ACR; building from GHCR.')\n", + "\n", + "# Build & push the agent image with ACR (no local Docker). Build context is the source folder.\n", + "acr_build = run([\n", + " 'az', 'acr', 'build',\n", + " '--registry', spoke_acr_name,\n", + " '--resource-group', spoke_resource_group,\n", + " '--image', f'{hosted_agent_name}:{hosted_agent_tag}',\n", + " '--file', str(agent_source_dir / 'Dockerfile'),\n", + " '--build-arg', f'BASE_IMAGE={base_image_ref}',\n", + " '--build-arg', f'UV_IMAGE={uv_image_ref}',\n", + " '--no-logs',\n", + " str(agent_source_dir),\n", + "], timeout=1200)\n", + "print(acr_build.stdout[-600:] if acr_build.stdout else '')\n", + "if acr_build.returncode != 0:\n", + " raise RuntimeError(f'ACR build failed: {acr_build.stderr.strip()[-800:]}')\n", + "\n", + "# Wait for the tag to appear.\n", + "for attempt in range(24):\n", + " tags = run(['az', 'acr', 'repository', 'show-tags', '--name', spoke_acr_name, '--repository', hosted_agent_name, '-o', 'json'], timeout=60)\n", + " if tags.returncode == 0 and hosted_agent_tag in (tags.stdout or ''):\n", + " print(f'Image verified in ACR: {hosted_agent_image}')\n", + " break\n", + " print(f' waiting for image... ({(attempt + 1) * 10}s)')\n", + " time.sleep(10)\n", + "else:\n", + " raise RuntimeError('Image did not appear in ACR after 4 minutes; check ACR build logs in the portal.')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d88b198", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "from azure.ai.projects import AIProjectClient\n", + "from azure.ai.projects.models import HostedAgentDefinition, ProtocolVersionRecord, AgentProtocol\n", + "from azure.identity import AzureCliCredential\n", + "\n", + "project_client = AIProjectClient(endpoint=foundry_project_endpoint, credential=AzureCliCredential(process_timeout=60), allow_preview=True)\n", + "\n", + "agent_version = project_client.agents.create_version(\n", + " agent_name=hosted_agent_name,\n", + " definition=HostedAgentDefinition(\n", + " container_protocol_versions=[ProtocolVersionRecord(protocol=AgentProtocol.RESPONSES, version='1.0.0')],\n", + " cpu='1',\n", + " memory='2Gi',\n", + " image=hosted_agent_image,\n", + " environment_variables={\n", + " 'AZURE_AI_MODEL_DEPLOYMENT_NAME': hosted_agent_model,\n", + " 'HR_MCP_APIM_MCP_URL': HR_MCP_APIM_MCP_URL,\n", + " 'HR_MCP_AUDIENCE': HR_MCP_AUDIENCE,\n", + " 'HR_MCP_APIM_SUBSCRIPTION_KEY': HR_MCP_APIM_SUBSCRIPTION_KEY,\n", + " 'ENABLE_INSTRUMENTATION': 'true',\n", + " 'OTEL_SERVICE_NAME': hosted_agent_name,\n", + " 'OTEL_RESOURCE_ATTRIBUTES': f'service.name={hosted_agent_name},service.namespace=citadel,deployment.environment=workshop',\n", + " },\n", + " ),\n", + ")\n", + "print(f'Agent version created: {agent_version.name} (version {agent_version.version}), status={agent_version.status}')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3bb0711e", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "max_wait_seconds, poll = 600, 15\n", + "elapsed = 0\n", + "while elapsed < max_wait_seconds:\n", + " agent_version = project_client.agents.get_version(agent_name=hosted_agent_name, agent_version=agent_version.version)\n", + " print(f'[{elapsed}s] status: {agent_version.status}')\n", + " if agent_version.status == 'active':\n", + " print('Agent is active.')\n", + " break\n", + " if agent_version.status in ('failed', 'error'):\n", + " raise RuntimeError(f'Agent deployment failed: {agent_version.status}')\n", + " time.sleep(poll); elapsed += poll\n", + "else:\n", + " raise RuntimeError(f'Timed out waiting for agent to become active after {max_wait_seconds}s.')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8edca6be", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Grant the agent's managed identity (a) Foundry User on the spoke account and\n", + "# (b) the HR MCP API app role 'Mcp.Invoke' so it can call the MCP through APIM.\n", + "agent_identity = agent_version.instance_identity\n", + "if agent_identity is None:\n", + " agent_identity = project_client.agents.get(agent_name=hosted_agent_name).instance_identity\n", + "if agent_identity is None:\n", + " raise RuntimeError('Could not resolve the hosted agent identity. Ensure the agent is active.')\n", + "agent_principal_id = agent_identity.principal_id\n", + "print(f'Agent identity principal id: {agent_principal_id}')\n", + "\n", + "# (a) Foundry User on the spoke Foundry account.\n", + "foundry_scope = (f\"/subscriptions/{env_subscription_id}/resourceGroups/{spoke_resource_group}\"\n", + " f\"/providers/Microsoft.CognitiveServices/accounts/{spoke_foundry_account}\")\n", + "existing = run(['az','role','assignment','list','--assignee-object-id',agent_principal_id,'--role','Foundry User','--scope',foundry_scope,'--query','[0].id','-o','tsv'])\n", + "if (existing.stdout or '').strip():\n", + " print('Foundry User already assigned.')\n", + "else:\n", + " r = run(['az','role','assignment','create','--assignee-object-id',agent_principal_id,'--assignee-principal-type','ServicePrincipal','--role','Foundry User','--scope',foundry_scope,'--only-show-errors','-o','none'])\n", + " print('Foundry User assigned.' if r.returncode == 0 else f'Foundry User assignment failed: {r.stderr.strip()}')\n", + "\n", + "# (b) HR MCP API app role (Mcp.Invoke) -> agent MI, via Microsoft Graph appRoleAssignedTo.\n", + "api_sp_id = run(['az','ad','sp','show','--id',HR_MCP_API_CLIENT_ID,'--query','id','-o','tsv']).stdout.strip()\n", + "if not api_sp_id:\n", + " raise RuntimeError('HR MCP API service principal not found. Re-run deploy-hr-mcp.')\n", + "existing_assignments = run(['az','rest','--method','get','--url',f'https://graph.microsoft.com/v1.0/servicePrincipals/{agent_principal_id}/appRoleAssignments','-o','json'])\n", + "already = HR_MCP_APP_ROLE_ID in (existing_assignments.stdout or '')\n", + "if already:\n", + " print('App role Mcp.Invoke already granted to the agent identity.')\n", + "else:\n", + " body = json.dumps({'principalId': agent_principal_id, 'resourceId': api_sp_id, 'appRoleId': HR_MCP_APP_ROLE_ID})\n", + " r = run(['az','rest','--method','post','--url',f'https://graph.microsoft.com/v1.0/servicePrincipals/{agent_principal_id}/appRoleAssignedTo','--headers','Content-Type=application/json','--body',body,'-o','none'])\n", + " if r.returncode == 0:\n", + " print('App role Mcp.Invoke granted. Waiting 60s for token/RBAC propagation...')\n", + " time.sleep(60)\n", + " else:\n", + " print(f'App role grant failed (may already exist): {r.stderr.strip()}')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "666bb69b", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import uuid\n", + "\n", + "invoke_client = AIProjectClient(endpoint=foundry_project_endpoint, credential=AzureCliCredential(process_timeout=60), allow_preview=True)\n", + "openai_client = invoke_client.get_openai_client(agent_name=hosted_agent_name)\n", + "\n", + "def extract_text(response) -> str:\n", + " err = getattr(response, 'error', None)\n", + " if err is not None:\n", + " code_ = getattr(err, 'code', None) or (err.get('code') if isinstance(err, dict) else None)\n", + " msg = getattr(err, 'message', None) or (err.get('message') if isinstance(err, dict) else None)\n", + " return f'[agent error: status={getattr(response, \"status\", None)} code={code_} message={msg}]'\n", + " text = getattr(response, 'output_text', None)\n", + " if text:\n", + " return text\n", + " try:\n", + " data = response.model_dump()\n", + " except Exception:\n", + " data = getattr(response, '__dict__', {}) or {}\n", + " chunks = []\n", + " def walk(node):\n", + " if isinstance(node, dict):\n", + " for k in ('text','output_text','value','message','delta'):\n", + " v = node.get(k)\n", + " if isinstance(v, str) and v.strip():\n", + " chunks.append(v)\n", + " for v in node.values():\n", + " walk(v)\n", + " elif isinstance(node, list):\n", + " for it in node:\n", + " walk(it)\n", + " walk(data)\n", + " seen, out = set(), []\n", + " for c in chunks:\n", + " c = c.strip()\n", + " if c and c not in seen:\n", + " seen.add(c); out.append(c)\n", + " return '\\n'.join(out)\n", + "\n", + "conversation_id = str(uuid.uuid4())\n", + "print(f'Conversation ID: {conversation_id}')\n", + "\n", + "prompt = ('Look up employee E1001 and summarize their profile, then recommend a learning path '\n", + " 'for the target role Engineering Manager. Use the HR MCP tools.')\n", + "print(f'\\nSending: {prompt}')\n", + "resp = openai_client.responses.create(input=prompt, metadata={'conversation_id': conversation_id})\n", + "print('\\nAgent response:')\n", + "print(extract_text(resp))\n", + "\n", + "prompt2 = (\"Add the skill 'apim-hosted-agent-demo' to employee E1001 with an evidence note \"\n", + " \"'Validated via the Citadel hosted agent through APIM.' Then confirm the change.\")\n", + "print(f'\\nSending: {prompt2}')\n", + "resp2 = openai_client.responses.create(input=prompt2, metadata={'conversation_id': conversation_id})\n", + "print('\\nAgent response:')\n", + "print(extract_text(resp2))\n" + ] + }, + { + "cell_type": "markdown", + "id": "07c0c048", + "metadata": {}, + "source": [ + "## 1️⃣1️⃣ HR workflow through APIM\n", + "\n", + "This deterministic workflow combines employee lookup, learning recommendation, and skill update through APIM. The optional MAF natural-language invocation is disabled by default to avoid consuming rate-limit budget unexpectedly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af9d8751", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "# Wait to avoid inheriting rate-limit state from the prior demonstration.\n", + "time.sleep(31)\n", + "search = decode_tool_text(parse_rpc(mcp_post('tools/call', {'name': 'search_employees', 'arguments': {'query': 'Avery', 'department': 'Engineering'}}, 'wf-search'), 'search_employees'))\n", + "print('Employee search summary:')\n", + "print(json.dumps(search, indent=2)[:1200])\n", + "\n", + "employee_id = 'E1001'\n", + "learning = decode_tool_text(parse_rpc(mcp_post('tools/call', {'name': 'recommend_learning_path', 'arguments': {'employee_id': employee_id, 'target_role': 'Engineering Manager'}}, 'wf-learning'), 'recommend_learning_path'))\n", + "print('\\nLearning recommendation summary:')\n", + "print(json.dumps(learning, indent=2)[:1200])\n", + "\n", + "skill_marker = f\"apim-maf-workshop-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}\"\n", + "skill_update = decode_tool_text(parse_rpc(mcp_post('tools/call', {'name': 'update_employee_skills', 'arguments': {'employee_id': employee_id, 'skills': [skill_marker], 'evidence_note': 'APIM-only HR workflow run from the MAF workshop notebook.'}}, 'wf-skill-update'), 'update_employee_skills'))\n", + "print('\\nSkill update summary:')\n", + "print(json.dumps({'employee_id': employee_id, 'added_skill': skill_marker, 'present': skill_marker in skill_update.get('current_skills', [])}, indent=2))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ✅ Summary\n", + "\n", + "You loaded configuration, verified Azure CLI context, acquired a delegated token without printing it, validated/documented APIM publication, exercised MCP protocol and HR tools through APIM only, demonstrated `429` rate limiting, inspected APIM log guidance, configured a MAF MCP tool with APIM headers, and ran an HR workflow through APIM.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "workshop (3.13.13.final.0)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/Dockerfile b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/Dockerfile new file mode 100644 index 000000000..0d6c1b230 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.13-slim + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY governance.py . +COPY main.py . + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/agent.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/agent.yaml new file mode 100644 index 000000000..f2da5ca81 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/agent.yaml @@ -0,0 +1,17 @@ +kind: hosted +name: citadel-governed-agent +protocols: + - protocol: responses + version: 1.0.0 +resources: + cpu: "1" + memory: 2Gi +environment_variables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: Hub-HR-ChatAgent-DEV-LLM/gpt-4.1 + - name: ENABLE_INSTRUMENTATION + value: "true" + - name: ENABLE_SENSITIVE_DATA + value: "true" + - name: OTEL_SERVICE_NAME + value: citadel-governed-agent diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/governance.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/governance.py new file mode 100644 index 000000000..673d59fb0 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/governance.py @@ -0,0 +1,72 @@ +""" +Minimal AGT-compatible governance module for Foundry Hosted Agents. + +Implements: +- CapabilityGuard: tool allow-list (blocks unauthorized tool calls) +- PolicyEngine: regex-based input blocking (PII, sensitive data) +- AuditLog: structured logging of all governance decisions + +Pattern based on: https://microsoft.github.io/agent-governance-toolkit/tutorials/34-maf-integration/ +""" +import re +import logging +from dataclasses import dataclass, field +from datetime import datetime, timezone + +logger = logging.getLogger("agt.governance") + + +@dataclass +class GovernanceDecision: + """Result of a governance check.""" + allowed: bool + tool_name: str + reason: str + timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat()) + + +class CapabilityGuard: + """Blocks tool calls not in the allowed_tools list.""" + + def __init__(self, allowed_tools: list[str]): + self.allowed_tools = set(allowed_tools) + + def check(self, tool_name: str) -> GovernanceDecision: + allowed = tool_name in self.allowed_tools + reason = "Tool in allowed list" if allowed else ( + f"Tool '{tool_name}' blocked by CapabilityGuard - " + f"not in allowed list: {sorted(self.allowed_tools)}" + ) + decision = GovernanceDecision(allowed=allowed, tool_name=tool_name, reason=reason) + logger.info(f"CapabilityGuard: {tool_name} -> {'ALLOW' if allowed else 'DENY'}") + return decision + + +class PolicyEngine: + """Regex-based policy rules for input text (PII, sensitive data).""" + + def __init__(self, rules: list[dict] | None = None): + self.rules = rules or [] + + def check_input(self, text: str) -> GovernanceDecision: + for rule in self.rules: + pattern = rule.get("pattern", "") + if re.search(pattern, text): + reason = rule.get("message", f"Blocked by rule: {rule.get('name', 'unnamed')}") + logger.warning(f"PolicyEngine DENY: {rule.get('name')} matched") + return GovernanceDecision(allowed=False, tool_name="input_check", reason=reason) + return GovernanceDecision(allowed=True, tool_name="input_check", reason="Input passed all policy checks") + + +class GovernanceLayer: + """Combines CapabilityGuard + PolicyEngine into a single governance layer.""" + + def __init__(self, allowed_tools: list[str], policy_rules: list[dict] | None = None): + self.capability_guard = CapabilityGuard(allowed_tools) + self.policy_engine = PolicyEngine(policy_rules) + + def check_tool(self, tool_name: str) -> GovernanceDecision: + return self.capability_guard.check(tool_name) + + def check_input(self, text: str) -> GovernanceDecision: + return self.policy_engine.check_input(text) \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/main.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/main.py new file mode 100644 index 000000000..5a5f7dfcb --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/main.py @@ -0,0 +1,115 @@ +import os +import asyncio +from typing import Annotated +from datetime import datetime, timezone as tz + +from pydantic import Field +from agent_framework import Agent, tool +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.identity import DefaultAzureCredential + +from governance import GovernanceLayer + + +# ---- Governance Configuration ---- +governance = GovernanceLayer( + allowed_tools=["get_current_time", "get_weather"], + # send_email is NOT in the allowed list => BLOCKED + policy_rules=[ + { + "name": "block-ssn", + "pattern": r"\b\d{3}-\d{2}-\d{4}\b", + "message": "PII detected (SSN pattern) - blocked for data protection", + }, + { + "name": "block-sensitive-keywords", + "pattern": r"(?i)(password|credit.card|social.security)", + "message": "Sensitive information detected - blocked for data protection", + }, + ], +) + + +# ---- Tools (governed via CapabilityGuard) ---- +@tool(approval_mode="never_require") +async def get_current_time( + timezone: Annotated[str, Field(description="IANA timezone name, e.g. 'America/New_York', 'Europe/London'")] +) -> str: + """Get the current date and time for a given timezone.""" + decision = governance.check_tool("get_current_time") + if not decision.allowed: + return f"[GOVERNANCE BLOCKED] {decision.reason}" + import zoneinfo + try: + zone = zoneinfo.ZoneInfo(timezone) + now = datetime.now(zone) + return f"Current time in {timezone}: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}" + except Exception as e: + return f"Could not get time for timezone '{timezone}': {e}" + + +@tool(approval_mode="never_require") +async def get_weather( + location: Annotated[str, Field(description="City name, e.g. 'Seattle', 'London', 'Tokyo'")] +) -> str: + """Get the current weather for a given location (simulated).""" + decision = governance.check_tool("get_weather") + if not decision.allowed: + return f"[GOVERNANCE BLOCKED] {decision.reason}" + import hashlib + seed = hashlib.md5(f"{location}{datetime.now(tz.utc).strftime('%Y-%m-%d')}".encode()).hexdigest() + conditions = ["sunny", "partly cloudy", "cloudy", "rainy", "windy", "snowy"] + condition = conditions[int(seed[:2], 16) % len(conditions)] + temp_c = 5 + (int(seed[2:4], 16) % 30) + humidity = 30 + (int(seed[4:6], 16) % 50) + return f"Weather in {location}: {condition}, {temp_c} deg C, humidity {humidity}%" + + +@tool(approval_mode="never_require") +async def send_email( + to: Annotated[str, Field(description="Recipient email address")], + body: Annotated[str, Field(description="Email body text")], +) -> str: + """Send an email to a recipient.""" + decision = governance.check_tool("send_email") + if not decision.allowed: + return f"[GOVERNANCE BLOCKED] {decision.reason}" + return f"Email sent to {to}" + + +async def setup(): + credential = DefaultAzureCredential() + + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=credential, + ) + + # Enable OpenTelemetry -> Application Insights + try: + await client.configure_azure_monitor(enable_sensitive_data=False) + except Exception as e: + print(f"Warning: Could not configure Azure Monitor: {e}") + + agent = Agent( + client=client, + name="citadel-governed-agent", + instructions=( + "You are a governed customer service assistant. " + "You can check the weather, tell the time, and send emails. " + "If a tool returns a GOVERNANCE BLOCKED message, explain to the user " + "that the action was denied by the governance policy and state the reason. " + "Be concise and helpful." + ), + tools=[get_current_time, get_weather, send_email], + default_options={"store": False}, + ) + + return ResponsesHostServer(agent) + + +if __name__ == "__main__": + server = asyncio.run(setup()) + server.run() \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/policies/governance-policy.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/policies/governance-policy.yaml new file mode 100644 index 000000000..20a010936 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/policies/governance-policy.yaml @@ -0,0 +1,25 @@ +name: citadel-governance-policy +version: "1.0" +description: Governance policy for citadel governed agent + +defaults: + action: allow + +rules: + - name: block-pii-ssn + condition: + field: input_text + operator: matches + value: "\\b\\d{3}-\\d{2}-\\d{4}\\b" + action: deny + message: "PII detected (SSN pattern) - blocked for data protection" + priority: 900 + + - name: block-pii-email-body + condition: + field: input_text + operator: matches + value: "(?i)(password|credit.card|social.security)" + action: deny + message: "Sensitive information detected - blocked for data protection" + priority: 800 diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/requirements.txt new file mode 100644 index 000000000..a9d553eaf --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/governed-agent/requirements.txt @@ -0,0 +1,4 @@ +agent-framework>=1.2.0 +agent-framework-foundry-hosting>=1.0.0a260507 +azure-identity>=1.25.0 +azure-monitor-opentelemetry>=1.6.0 diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/guided-workshop-prompt.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/guided-workshop-prompt.md new file mode 100644 index 000000000..d561a62db --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/guided-workshop-prompt.md @@ -0,0 +1,108 @@ +# Citadel AI Governance Hub — Guided Workshop Prompt (Human-in-the-Loop) + +> **How to use this file:** Copy everything in the fenced block below and paste it as your first message to GitHub Copilot (or another coding agent) in this repository. The agent will then act as your workshop facilitator and walk you through every lab step, pausing for your confirmation at each checkpoint. + +## A note on "you" vs "the agent" + +This document has two audiences, so be aware of who "you" refers to: + +- **Outside the fenced prompt block** (this intro, "Who does what", "What to expect", "Tips"): "you" = **the human participant** reading this file. +- **Inside the fenced prompt block** (the text you paste to Copilot): the prompt is written *to the agent*, so there "**you**" = **the agent**, and "**me** / **I**" = **the human participant**. + +## Who does what + +| Activity | Performed by | Agent's job | +|----------|-------------|-------------| +| Terminal / CLI steps (`az login`, provider registration, `azd up`, spoke + cleanup scripts, role assignments, pricing import, `azd down`) | **Agent runs them** (with your approval first) | Show the command + why, get approval, run it, explain output | +| Lab 3 Jupyter notebooks (`.ipynb`) | **Human runs them** in VS Code | Prep the environment, say which notebook to run, wait, help interpret/troubleshoot | +| Lab 2 & Lab 4 Azure Portal / observability | **Human does them** in the Portal | Guidance only — click-paths, queries, what to look for; never browses/queries Azure for you | +| Destructive cleanup (`azd down --purge --force`) | **Agent runs it** only after a **separate explicit** confirmation | Warn, confirm again, then run | + +--- + +``` +You are my hands-on facilitator for the "Citadel AI Governance Hub" workshop in this repository. +Throughout this prompt, "you" = the AGENT (the facilitator), and "me" / "I" = the HUMAN participant. +The authoritative lab guide is `workshop/readme.md`. Follow it exactly. Do not invent steps, +resource names, or commands that are not in that guide. When in doubt, re-read the relevant +section of `workshop/readme.md` before acting. + +## Roles — who does what +- AGENT (you): run terminal/CLI steps for me (after showing the command and getting my approval), + explain every step, keep us on track, and interpret results. You facilitate; you do NOT do the + notebook or Azure Portal work for me. +- HUMAN (me): run the Lab 3 Jupyter notebooks myself in VS Code, and do all Lab 2 / Lab 4 work in the + Azure Portal myself. You guide me through these and wait for me to report back. + +## Your role +- Guide me through the workshop ONE step at a time, in the order defined by `workshop/readme.md`: + Pre-Requisites → Lab 1 (Deploy) → Lab 2 (Review services) → Lab 3 (Run notebooks) → Lab 4 (Observability) → Clean Up. +- Act as a teacher, not just an executor. Before each step, give me a 1–3 sentence explanation of + WHAT we are about to do and WHY it matters in the context of AI governance. +- This is a HUMAN-IN-THE-LOOP session. After every meaningful step, STOP and wait for me to confirm + before continuing. Never run multiple labs back-to-back without my explicit "continue" / "next". + +## Division of labor (important) +- Terminal/CLI steps (az login, provider registration, `azd up`, spoke deploy scripts, role + assignments, pricing import, `azd down`): you may run these for me using the terminal, BUT always + show me the exact command and a one-line explanation first, then ask for my approval before running. + Never run destructive commands (anything with `azd down`, `--purge`, `--force`, role deletes, + resource deletes) without an explicit, separate confirmation from me. +- Notebook steps (Lab 3, the `.ipynb` files): these are INTENTIONALLY run by me, interactively, in + VS Code. Do NOT execute notebook cells for me. Instead, for each notebook: + 1. Tell me which notebook to open and its purpose. + 2. Confirm my Python kernel / environment is set up correctly. + 3. Tell me to run it (Run All is fine) and review the cell outputs. + 4. WAIT for me to report back ("done", "got an error", a pasted error, etc.). + 5. Help me interpret results or troubleshoot, then move to the next notebook only when I say so. +- Portal/observability steps (Lab 2 and Lab 4): these are INTENTIONALLY done by ME in the Azure + Portal. Do NOT perform them for me and do NOT use any tools to navigate, query, or read Azure + resources on my behalf for these steps. Your job here is guidance ONLY: give me the exact click-path + (resource → blade → option), tell me precisely what to look for and why it matters, and then WAIT + for me to report what I observed. Where the guide provides KQL or SQL queries, show them to me so I + can paste them myself; explain what each query reveals and help me interpret the results I share back. + +## Operating rules +- At the start, give me a short numbered overview of the four labs and ask which one I want to start + from (default: start at Pre-Requisites). Respect that I may have already completed some steps. +- Before running any command, verify prerequisites are met (e.g., `az account show` to confirm the + right subscription/tenant before deploying). If something looks off, flag it and ask me. +- After `azd up` kicks off (it takes 30–45 min), remind me I can read ahead through Lab 2 and Lab 3 + while it runs, and offer to walk me through that material during the wait. +- Use the exact scripts and commands from `workshop/readme.md` (e.g. + `workshop/scripts/deploy-spoke-foundry.*`, `workshop/scripts/cleanup-apim-defaults.*`, + `scripts/import-model-pricing.py`). Detect my OS and offer the correct Bash or PowerShell variant. +- The notebooks self-configure from `azd env` values at runtime — do NOT ask me to hand-edit resource + names or endpoints into them. +- Keep a running checklist of progress. After each completed step, restate where we are and what's next. +- If a step fails, consult the Troubleshooting section of `workshop/readme.md` first, propose a fix, + and ask before retrying. +- Keep explanations concise. I want to learn, not read a wall of text. + +## Start now +1. Briefly introduce the workshop and what Citadel is (2–3 sentences, based on `workshop/readme.md`). +2. Show me the numbered lab roadmap. +3. Ask me: which lab/step should we begin with, and am I running on macOS/Linux (Bash) or Windows + (PowerShell), and am I using a local machine or the Devcontainer? +Then wait for my answer before doing anything else. +``` + +--- + +## What to expect + +> In this section, "you" = the human participant. + +- The agent becomes a step-by-step facilitator that explains each action, asks permission before running terminal commands, and **pauses** at every checkpoint. +- **You (the human)** run the Lab 3 Jupyter notebooks yourself (that is by design) — the agent prepares the environment, tells you what to run, then waits and helps you interpret the output. +- **You (the human)** also drive all Azure Portal and observability work in Lab 2 and Lab 4 — the agent gives click-paths, queries, and explanations, then waits for what you observe. It will not browse or query Azure for you on these steps. +- **The agent** runs the terminal/CLI steps for you, but only after showing the command and getting your approval. +- Destructive operations (cleanup, `azd down --purge --force`) always require a separate explicit confirmation before the agent runs them. + +## Tips + +> In this section, "you" = the human participant. + +- If you have already finished some setup, just tell the agent (e.g. "I already ran `azd up`, start me at Lab 2"). +- Paste any error output back to the agent — it will cross-reference the Troubleshooting section of [readme.md](readme.md) and propose a fix. +- You can interrupt at any time with "stop", "go back", or "explain that more". diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/Dockerfile b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/Dockerfile new file mode 100644 index 000000000..5548e22a3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13-slim + +# Install uv from the official distroless image +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +ENV UV_SYSTEM_PYTHON=1 UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy + +WORKDIR /app +COPY requirements.txt . +RUN uv pip install --system --no-cache --prerelease=if-necessary-or-explicit -r requirements.txt +RUN python -c "from pydantic._internal._typing_extra import eval_type_backport; import mcp; from agent_framework_foundry_hosting import ResponsesHostServer; print('dependency smoke ok')" + +COPY main.py . +COPY policies/ ./policies/ + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/agent.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/agent.yaml new file mode 100644 index 000000000..6a9f72522 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/agent.yaml @@ -0,0 +1,19 @@ +kind: hosted +name: citadel-hr-agent +protocols: + - protocol: responses + version: 1.0.0 +resources: + cpu: "1" + memory: 2Gi +environment_variables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: Hub-HR-ChatAgent-DEV-LLM/gpt-4.1 + - name: ENABLE_INSTRUMENTATION + value: "true" + - name: ENABLE_SENSITIVE_DATA + value: "true" + - name: OTEL_SERVICE_NAME + value: citadel-hr-agent + - name: OTEL_RESOURCE_ATTRIBUTES + value: service.name=citadel-hr-agent,service.namespace=citadel,deployment.environment=workshop diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/main.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/main.py new file mode 100644 index 000000000..59097bb50 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/main.py @@ -0,0 +1,383 @@ +import os +from datetime import date, datetime, timedelta, timezone as tz +from typing import Annotated + +import logging +import threading +import time as _time + +from agent_framework import Agent, tool +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.identity import DefaultAzureCredential +from opentelemetry import trace +from pydantic import Field + +# --------------------------------------------------------------------------- +# HR tools (all dummy / deterministic — no external systems). +# --------------------------------------------------------------------------- + +@tool(approval_mode="never_require") +def get_pto_balance( + employee_id: Annotated[str, Field(description="Employee ID, e.g. 'E12345'")] +) -> str: + """Get the remaining PTO balance, days used this year, and accrual rate for an employee.""" + import hashlib + seed = int(hashlib.md5(employee_id.encode()).hexdigest()[:6], 16) + accrued = 20 + (seed % 6) # 20-25 days/yr + used = seed % accrued + remaining = accrued - used + return ( + f"PTO for employee {employee_id}: {remaining} days remaining " + f"({used} used / {accrued} accrued this year, accrual rate " + f"{accrued/12:.2f} days/month)." + ) + + +@tool(approval_mode="never_require") +def get_holiday_schedule( + country: Annotated[str, Field(description="ISO country code: 'US', 'UK', or 'JP'")] +) -> str: + """Get the next 3 upcoming Contoso company holidays for a country.""" + today = date.today() + catalog = { + "US": [("Memorial Day", (5, 27)), ("Independence Day", (7, 4)), + ("Labor Day", (9, 2)), ("Thanksgiving", (11, 28)), + ("Christmas Day", (12, 25))], + "UK": [("Spring Bank Holiday", (5, 27)), ("Summer Bank Holiday", (8, 26)), + ("Christmas Day", (12, 25)), ("Boxing Day", (12, 26))], + "JP": [("Showa Day", (4, 29)), ("Greenery Day", (5, 4)), + ("Marine Day", (7, 15)), ("Mountain Day", (8, 11)), + ("Culture Day", (11, 3))], + } + code = country.upper() + if code not in catalog: + return f"No holiday schedule configured for country '{country}'. Supported: US, UK, JP." + upcoming = [] + for name, (m, d) in catalog[code]: + candidate = date(today.year, m, d) + if candidate < today: + candidate = date(today.year + 1, m, d) + upcoming.append((candidate, name)) + upcoming.sort() + top3 = ", ".join(f"{name} ({d.isoformat()})" for d, name in upcoming[:3]) + return f"Next 3 Contoso holidays in {code}: {top3}." + + +@tool(approval_mode="never_require") +def get_benefits_summary( + plan_type: Annotated[str, Field(description="One of: 'medical', 'dental', 'vision', '401k'")] +) -> str: + """Get a high-level summary of a Contoso benefits plan.""" + summaries = { + "medical": "Medical (Contoso PPO): $250 deductible, $20 PCP copay, 90% in-network coinsurance, $3,000 OOP max.", + "dental": "Dental (Contoso Premier): 100% preventive, 80% basic, 50% major; $2,000 annual maximum.", + "vision": "Vision (Contoso View): annual eye exam $0, $200 frames allowance every 12 months.", + "401k": "401(k): Contoso matches 100% of the first 6% of eligible pay; immediate vesting; Roth + traditional options.", + } + key = plan_type.lower() + if key not in summaries: + return f"Unknown plan '{plan_type}'. Supported: medical, dental, vision, 401k." + return summaries[key] + + +@tool(approval_mode="never_require") +def get_open_enrollment_window() -> str: + """Get the current open enrollment window dates for Contoso benefits.""" + today = date.today() + start = date(today.year, 11, 1) + end = date(today.year, 11, 21) + if today > end: + start = date(today.year + 1, 11, 1) + end = date(today.year + 1, 11, 21) + days = (start - today).days + when = "currently open" if start <= today <= end else f"opens in {days} days" + return f"Contoso open enrollment: {start.isoformat()} → {end.isoformat()} ({when})." + + +@tool(approval_mode="never_require") +def delete_user( + user_id: Annotated[str, Field(description="Employee/user ID to delete, e.g. 'E12345'")] +) -> str: + """Simulate deleting a Contoso HR user account.""" + return f"SIMULATION ONLY: user {user_id} would be deleted." + + +# --------------------------------------------------------------------------- +# OpenTelemetry note. +# +# The Foundry hosted-agent platform auto-configures the global OTel +# TracerProvider / MeterProvider / LoggerProvider via the +# microsoft-opentelemetry distro using the injected +# APPLICATIONINSIGHTS_CONNECTION_STRING. We deliberately do NOT call +# configure_azure_monitor() or agentmesh.governance.enable_otel() here: +# both attempt to override the global providers and trigger +# "Overriding of current TracerProvider is not allowed" warnings, which +# leaves AGT spans attached to a process-local provider that nobody +# exports. +# +# AGT telemetry instead flows through the canonical +# AuditLog.export_cloudevents() surface: we construct a shared AuditLog, +# pass it to create_governance_middleware(audit_log=...), and the existing +# OTel pipeline picks it up. Custom span emission can use: +# tracer = trace.get_tracer(__name__) +# --------------------------------------------------------------------------- + +tracer = trace.get_tracer(__name__) + + +# --------------------------------------------------------------------------- +# AGT AuditLog -> OTel logger flush task. +# +# create_governance_middleware records every policy / capability / rogue +# decision into the in-memory AuditLog. To surface those decisions in +# Application Insights we periodically drain AuditLog.export_cloudevents() +# onto a stdlib logger. The platform distro auto-bridges stdlib logging to +# the OTel LoggerProvider, so each CloudEvent becomes a structured log +# record (visible in App Insights "traces" table with logger name +# `agt.audit`). +# +# Runs in a daemon thread so it does not interfere with the Hypercorn +# event loop owned by ResponsesHostServer.run(). +# --------------------------------------------------------------------------- + +def _start_audit_flusher(audit_log, interval_s: float = 5.0) -> None: + audit_logger = logging.getLogger("agt.audit") + # Force INFO so flusher diagnostics survive any root-level filter the + # platform may install. logger.propagate stays True so the OTel logging + # bridge installed by the distro still picks the records up. + audit_logger.setLevel(logging.INFO) + + # One-shot startup diagnostic: log the actual AuditLog API surface so we + # can see in stderr which method name to call (export_cloudevents vs. + # export vs. something else). + public_methods = sorted(m for m in dir(audit_log) if not m.startswith("_")) + audit_logger.info( + "agt.audit flusher starting (interval=%.1fs); AuditLog API: %s", + interval_s, ", ".join(public_methods), + ) + + def _flush_loop() -> None: + # AGT 3.6.0 AuditLog.export_cloudevents() returns the FULL ordered + # list of events recorded so far (no incremental kwarg). We track how + # many we've already emitted and slice from there each tick. + seen = 0 + tick = 0 + while True: + tick += 1 + drained_this_tick = 0 + err: Exception | None = None + try: + events = audit_log.export_cloudevents() or [] + new_events = events[seen:] + for ce in new_events: + ce_type = ce.get("type") if isinstance(ce, dict) else getattr(ce, "type", None) + audit_logger.info( + "agt.audit.event", + extra={"agt.event.type": ce_type, "agt.cloudevent": ce}, + ) + drained_this_tick += 1 + seen = len(events) + except Exception as exc: # never let the flusher die silently + err = exc + + # Heartbeat once per minute (every 12 ticks at 5s interval) so we + # can confirm the thread is alive even when nothing is drained. + if err is not None: + audit_logger.warning( + "agt.audit flush tick %d failed: %s: %s", + tick, type(err).__name__, err, + ) + elif drained_this_tick > 0: + audit_logger.info( + "agt.audit flush tick %d drained=%d total=%d", + tick, drained_this_tick, seen, + ) + elif tick % 12 == 1: + audit_logger.info( + "agt.audit flush tick %d heartbeat (no new events; total=%d)", + tick, seen, + ) + _time.sleep(interval_s) + + threading.Thread( + target=_flush_loop, + name="agt-audit-flusher", + daemon=True, + ).start() + + +def _create_streaming_governance_middleware( + *, + policy_directory: str, + allowed_tools: list[str], + agent_id: str, + audit_log, +) -> list: + from agent_framework import ( + AgentMiddleware, + AgentResponse, + AgentResponseUpdate, + Content, + Message, + ResponseStream, + ) + from agent_os.integrations.maf_adapter import create_governance_middleware + from agent_os.policies import PolicyEvaluator + + class StreamingGovernancePolicyMiddleware(AgentMiddleware): + def __init__(self, evaluator: PolicyEvaluator, audit_log) -> None: + self.evaluator = evaluator + self.audit_log = audit_log + + async def process(self, context, call_next) -> None: + agent_name = getattr(context.agent, "name", "unknown") + messages = getattr(context, "messages", None) or [] + last_message_text = "" + if messages: + last_msg = messages[-1] + last_message_text = getattr(last_msg, "text", None) or str(last_msg) + + decision = self.evaluator.evaluate( + { + "agent": agent_name, + "message": last_message_text, + "timestamp": _time.time(), + "stream": getattr(context, "stream", False), + "message_count": len(messages), + } + ) + + metadata = getattr(context, "metadata", {}) + metadata["governance_decision"] = decision + + if decision.allowed: + if self.audit_log: + self.audit_log.log( + event_type="policy_evaluation", + agent_did=agent_name, + action="allow", + data={ + "matched_rule": decision.matched_rule, + "message_preview": last_message_text[:200], + }, + outcome="success", + policy_decision=decision.action, + ) + await call_next() + return + + logging.getLogger("agent_os.integrations.maf_adapter").info( + "Policy DENY for agent '%s': %s (rule=%s)", + agent_name, + decision.reason, + decision.matched_rule, + ) + refusal = f"Policy violation: {decision.reason}" + + if self.audit_log: + self.audit_log.log( + event_type="policy_violation", + agent_did=agent_name, + action="deny", + data={ + "reason": decision.reason, + "matched_rule": decision.matched_rule, + "message_preview": last_message_text[:200], + }, + outcome="denied", + policy_decision=decision.action, + ) + + if getattr(context, "stream", False): + async def _deny_stream(): + yield AgentResponseUpdate( + contents=[Content.from_text(refusal)], + role="assistant", + agent_id=agent_name, + ) + + context.result = ResponseStream( + _deny_stream(), + finalizer=AgentResponse.from_updates, + ) + else: + context.result = AgentResponse( + messages=[Message("assistant", [refusal])] + ) + + evaluator = PolicyEvaluator() + evaluator.load_policies(policy_directory) + stack = create_governance_middleware( + policy_directory=None, + allowed_tools=allowed_tools, + agent_id=agent_id, + enable_rogue_detection=True, + audit_log=audit_log, + ) + stack.insert(1, StreamingGovernancePolicyMiddleware(evaluator, audit_log)) + return stack + + +# --------------------------------------------------------------------------- +# Main entrypoint. +# --------------------------------------------------------------------------- + +def main() -> None: + from agentmesh.governance import AuditLog + + tools = [ + get_pto_balance, + get_holiday_schedule, + get_benefits_summary, + get_open_enrollment_window, + delete_user, + ] + allowed_tool_names = [t.name if hasattr(t, "name") else t.__name__ for t in tools] + + agent_name_env = os.environ.get("OTEL_SERVICE_NAME", "citadel-hr-agent") + + # Shared AuditLog — emits OTel-compatible CloudEvents via + # audit_log.export_cloudevents(); plugs into the already-configured + # global LoggerProvider installed by the platform distro. + audit_log = AuditLog() + + governance = _create_streaming_governance_middleware( + policy_directory="policies", + allowed_tools=allowed_tool_names, + agent_id=agent_name_env, + audit_log=audit_log, + ) + + # Drain AGT audit events into the OTel pipeline every 5s. + _start_audit_flusher(audit_log, interval_s=5.0) + + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=DefaultAzureCredential(), + ) + + agent = Agent( + client=client, + name=agent_name_env, + instructions=( + "You are Contoso HR Assistant, a friendly internal HR helper. " + "Use the provided tools to answer questions about an employee's own PTO balance, " + "Contoso company holidays, benefits plans (medical/dental/vision/401k), and the " + "open enrollment window. The delete_user tool exists only to demonstrate governance " + "for explicit user deletion requests. Never disclose another employee's personal data " + "(salary, SSN, compensation). Be concise." + ), + tools=tools, + middleware=governance, + # History is managed by the hosting infrastructure. + default_options={"store": False}, + ) + + server = ResponsesHostServer(agent) + server.run() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/policies/hr-policy.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/policies/hr-policy.yaml new file mode 100644 index 000000000..541e1eeba --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/policies/hr-policy.yaml @@ -0,0 +1,43 @@ +name: contoso-hr-policy +version: "1.0" +description: Governance policy for the Contoso HR hosted agent. + +defaults: + action: allow + +rules: + - name: block-ssn-disclosure + condition: + field: message + operator: matches + value: "\\b\\d{3}-\\d{2}-\\d{4}\\b" + action: deny + message: "SSN detected in request — blocked for PII protection." + priority: 1000 + + - name: block-user-deletion + condition: + field: message + operator: matches + value: "(?i)(\\b(delete|remove|deactivate|disable|terminate)\\b.{0,80}\\b(user|employee|account|profile|E\\d{3,})\\b|\\b(user|employee|account|profile|E\\d{3,})\\b.{0,80}\\b(delete|remove|deactivate|disable|terminate)\\b)" + action: deny + message: "User deletion and deactivation requests are blocked by HR policy." + priority: 950 + + - name: block-third-party-salary-lookup + condition: + field: message + operator: matches + value: "(?i)(?=.*\\b(salary|compensation|pay|bonus|paycheck)\\b)(?=.*\\b(other|coworker|co-worker|colleague|teammate|someone\\s+else|another\\s+employee|employee\\s+[A-Za-z]?\\d+|my\\s+manager|my\\s+boss|my\\s+report)\\b).*" + action: deny + message: "Disclosing another employee's compensation is not permitted by HR policy." + priority: 900 + + - name: audit-benefits-and-pto + condition: + field: message + operator: matches + value: "(?i)(pto|paid\\s+time\\s+off|leave|holiday|benefits?|enrollment|401k|medical|dental|vision)" + action: audit + message: "HR self-service query logged for compliance trail." + priority: 100 diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/requirements.txt new file mode 100644 index 000000000..8dd1456a4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/hosted-agent/requirements.txt @@ -0,0 +1,13 @@ +# Pinned to the verified-good set from the foundry-hosted-agents skill. +# The umbrella `agent-framework` meta-package resolves to versions with +# the AsyncStreamWrapper.parse() regression that breaks streamed tool +# calls in the hosted-agent server. +agent-framework-core~=1.6.0 +agent-framework-foundry~=1.6.0 +agent-framework-foundry-hosting==1.0.0a260521 +mcp~=1.10.0 +pydantic==2.12.5 +azure-identity>=1.19.0,<1.26.0a0 +agent-os-kernel +agentmesh-platform +agent-sre diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/Dockerfile b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/Dockerfile new file mode 100644 index 000000000..4e99cacd8 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/Dockerfile @@ -0,0 +1,26 @@ +# Base images are parameterized so the build can pull them from a registry that +# is not rate-limited (e.g. a copy imported into your ACR with `az acr import`). +# Defaults use the public images directly. +ARG BASE_IMAGE=python:3.13-slim +ARG UV_IMAGE=ghcr.io/astral-sh/uv:latest + +FROM ${UV_IMAGE} AS uv +FROM ${BASE_IMAGE} + +# Bring in uv (fast, reliable installs). +COPY --from=uv /uv /uvx /bin/ + +ENV UV_SYSTEM_PYTHON=1 \ + UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy + +WORKDIR /app +COPY requirements.txt . +RUN uv pip install --system --no-cache --prerelease=if-necessary-or-explicit -r requirements.txt +RUN python -c "import mcp, httpx; from mcp.client.streamable_http import streamable_http_client; from agent_framework import MCPStreamableHTTPTool; from agent_framework.foundry import FoundryChatClient; from agent_framework_foundry_hosting import ResponsesHostServer; print('dependency smoke ok')" + +COPY main.py . + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/agent.yaml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/agent.yaml new file mode 100644 index 000000000..5b992f11d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/agent.yaml @@ -0,0 +1,29 @@ +## HR MCP hosted agent — Foundry hosted-agent manifest (reference). +## +## The workshop notebook deploys this agent with AIProjectClient.create_version() +## and injects the environment_variables below at deploy time (the MCP URL, +## audience, and subscription key are read from azd/APIM). This file documents +## the expected shape; the platform injects FOUNDRY_PROJECT_ENDPOINT automatically. +kind: hosted +name: citadel-hr-mcp-agent +protocols: + - protocol: responses + version: 1.0.0 +resources: + cpu: "1" + memory: 2Gi +environment_variables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: Hub-HR-ChatAgent-DEV-LLM/gpt-4.1 + - name: HR_MCP_APIM_MCP_URL + value: https://.azure-api.net/hr-mcp/mcp + - name: HR_MCP_AUDIENCE + value: api:// + - name: HR_MCP_APIM_SUBSCRIPTION_KEY + value: + - name: ENABLE_INSTRUMENTATION + value: "true" + - name: OTEL_SERVICE_NAME + value: citadel-hr-mcp-agent + - name: OTEL_RESOURCE_ATTRIBUTES + value: service.name=citadel-hr-mcp-agent,service.namespace=citadel,deployment.environment=workshop diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/main.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/main.py new file mode 100644 index 000000000..54bdf1610 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/main.py @@ -0,0 +1,97 @@ +"""Citadel HR MCP hosted agent. + +A Microsoft Agent Framework (MAF) hosted agent (same shape as the workshop +`hosted-agent`) whose tools are served by the HR MCP server *through Azure API +Management*. The agent itself runs in the spoke Foundry project and reasons with +the same Foundry model used by the workshop HR agent. + +Auth to the APIM-published MCP endpoint uses the agent's managed identity: +on every HTTP request to APIM we attach a fresh Entra token for the HR MCP API +audience plus the APIM subscription key. This avoids baking an expiring token +into the image and works for a long-lived hosted agent. + +Required environment variables (set at deploy time): + AZURE_AI_MODEL_DEPLOYMENT_NAME Foundry gateway connection/model (e.g. Hub-HR-ChatAgent-DEV-LLM/gpt-4.1) + FOUNDRY_PROJECT_ENDPOINT Injected automatically by the Foundry hosted-agent platform + HR_MCP_APIM_MCP_URL APIM MCP endpoint, e.g. https://.azure-api.net/hr-mcp/mcp + HR_MCP_AUDIENCE HR MCP API audience, e.g. api:// + HR_MCP_APIM_SUBSCRIPTION_KEY APIM subscription key for the HR MCP access contract +""" + +import os + +from agent_framework import Agent, MCPStreamableHTTPTool +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.identity import DefaultAzureCredential +from httpx import AsyncClient, Request, Timeout + +AGENT_NAME = os.environ.get("OTEL_SERVICE_NAME", "citadel-hr-mcp-agent") +MCP_URL = os.environ["HR_MCP_APIM_MCP_URL"] +SUBSCRIPTION_KEY = os.environ.get("HR_MCP_APIM_SUBSCRIPTION_KEY", "") +AUDIENCE = os.environ.get("HR_MCP_AUDIENCE", "") + +# Single shared credential. azure-identity caches tokens and refreshes them +# near expiry, so calling get_token per request is cheap. +_credential = DefaultAzureCredential() +_token_scope = f"{AUDIENCE}/.default" if AUDIENCE else None + + +def _build_mcp_http_client() -> AsyncClient: + """httpx client that authenticates every request to the APIM MCP endpoint. + + The request hook runs for the MCP handshake (initialize / tools/list) and + for every tool call, so both connection and invocation are authenticated. + """ + + async def _attach_auth(request: Request) -> None: # noqa: RUF029 - hook signature + if SUBSCRIPTION_KEY: + request.headers["Ocp-Apim-Subscription-Key"] = SUBSCRIPTION_KEY + if _token_scope: + token = _credential.get_token(_token_scope).token + request.headers["Authorization"] = f"Bearer {token}" + + client = AsyncClient( + follow_redirects=True, + timeout=Timeout(60.0, read=300.0), + ) + client.event_hooks["request"].append(_attach_auth) + return client + + +def main() -> None: + mcp_tool = MCPStreamableHTTPTool( + name="hr_mcp_via_apim", + url=MCP_URL, + http_client=_build_mcp_http_client(), + request_timeout=60, + ) + + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=_credential, + ) + + agent = Agent( + client=client, + name=AGENT_NAME, + instructions=( + "You are the Contoso HR assistant. Answer HR questions by calling the " + "hr_mcp_via_apim MCP tools, which are published through the Citadel API " + "Management gateway. Use search_employees and get_employee_profile for " + "lookups, recommend_learning_path for development guidance, and " + "submit_pto_request / update_employee_skills for changes. Always rely on " + "the MCP tools for HR data; never invent employee records. Be concise and " + "summarize what the tools returned." + ), + tools=[mcp_tool], + # Conversation history is managed by the hosting infrastructure. + default_options={"store": False}, + ) + + ResponsesHostServer(agent).run() + + +if __name__ == "__main__": + main() diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/requirements.txt new file mode 100644 index 000000000..e0a717c22 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hosted-agent/requirements.txt @@ -0,0 +1,13 @@ +# Microsoft Agent Framework hosted-agent dependency set. +# Pinned to the verified-good versions from the workshop hosted-agent +# (foundry-hosted-agents skill). The umbrella `agent-framework` meta-package +# resolves to versions with the AsyncStreamWrapper.parse() regression that +# breaks streamed tool calls in the hosted-agent server, so pin the split +# packages instead. +agent-framework-core~=1.6.0 +agent-framework-foundry~=1.6.0 +agent-framework-foundry-hosting==1.0.0a260521 +mcp>=1.27.0,<2.0.0 +pydantic==2.12.5 +azure-identity>=1.19.0,<1.26.0a0 +httpx>=0.27.0 diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hr-mcp-implementation-plan.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hr-mcp-implementation-plan.md new file mode 100644 index 000000000..0b0842016 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/hr-mcp-implementation-plan.md @@ -0,0 +1,338 @@ +# HR MCP Server Implementation Plan + +This plan describes a workshop scenario for building, deploying, governing, and validating an HR-focused Model Context Protocol (MCP) server. It is intended for maintainers who need to resume, adapt, or review the implementation in a public repository. + +The scenario deploys a Python MCP server to Azure Container Apps (ACA), secures direct and gateway-mediated tool access with Microsoft Entra delegated authentication, publishes the server through Azure API Management (APIM) with a Citadel-style access contract, and demonstrates consumption from workshop tooling and a notebook. + +--- + +## Goals + +- Provide a self-contained HR MCP workshop scenario under `workshop/`. +- Build the MCP container image remotely with Azure Container Registry (ACR); local Docker builds are not required. +- Expose a public ACA troubleshooting endpoint protected by Microsoft Entra OAuth. +- Route the workshop path through APIM using an MCP API, Citadel access contract, rate limiting, and bounded logging. +- Preserve user-level authorization semantics by validating the same delegated token at APIM and the MCP server. +- Emit server telemetry to Application Insights using OpenTelemetry. +- Include enough validation and operational guidance for future maintainers to extend the scenario safely. + +## Non-goals + +- Replacing the repository's existing Citadel hub deployment. +- Adding runtime dependencies on existing APIM or access-contract samples outside `workshop/`. +- Logging bearer tokens, subscription keys, tenant-specific IDs, subscription IDs, or environment-specific resource names. +- Making the direct ACA endpoint the primary workshop narrative; direct access is for smoke testing and troubleshooting. + +--- + +## Architecture + +```mermaid +flowchart LR + User[Workshop participant] -->|Delegated Entra token| Direct[Public ACA /mcp endpoint] + User -->|Delegated Entra token| APIM[Citadel APIM MCP API] + APIM -->|Private network path, bearer token forwarded| ACA[HR MCP server on ACA] + ACA --> Tools[HR MCP tools and in-memory demo store] + ACA --> AppI[Application Insights] + APIM --> Logs[APIM diagnostics and gateway logs] + Notebook[Workshop notebook] -->|APIM only| APIM + Inspector[MCP Inspector / local clients] -->|Troubleshooting path| Direct +``` + +### Flow summary + +1. Deployment scripts create the HR MCP resource group, telemetry, ACR, a public ACA troubleshooting app, a private/internal ACA backend app, and app registration settings. +2. The server exposes `/health` for probes and `/mcp` for streamable HTTP MCP traffic. +3. Participants acquire a delegated Microsoft Entra token for the HR MCP API scope. +4. Direct troubleshooting calls send the bearer token to the public ACA endpoint. +5. Workshop calls go through APIM, where the access-contract policy validates the same delegated token, enforces tool-call limits, and logs bounded request/response details. +6. APIM forwards traffic to the private/internal ACA app over the Citadel hub VNet path and preserves the bearer token for server-side validation. +7. Application Insights and APIM diagnostics provide correlated evidence for tool calls and policy outcomes. + +--- + +## Scope and Design Decisions + +- All new implementation files for this scenario must remain under `workshop/` and subfolders. +- The implementation may reference existing repository patterns, but the HR MCP scenario should be deployable from its own workshop assets. +- Scripts should follow the workshop script style: deterministic names, idempotent resource creation, explicit CLI checks, `azd env` integration, and clear terminal output. +- Python dependencies are split by runtime. Notebook/helper dependencies use `workshop/pyproject.toml`; MCP server runtime dependencies use `workshop/mcp-hr/server/pyproject.toml`. +- ACA defaults should minimize cost: ACR Basic, app min replicas `0`, app max replicas `2`, and no unnecessary always-on services. +- APIM-to-ACA traffic must use private connectivity through the existing Citadel hub VNet. The deployment scripts discover the hub VNet in the Citadel hub resource group, inspect its address space and existing subnets, then create/use a dedicated ACA-delegated `snet-mcp` subnet from available space. Overrides are available for VNet/subnet names, prefix length, subnet prefix, or subnet resource ID. +- The public ACA endpoint remains available only for authenticated troubleshooting. +- APIM rate limiting applies only to MCP `tools/call` requests, not protocol setup methods. +- APIM body logging must be bounded and must not log tokens or sensitive headers. +- JSON-RPC batch requests should be rejected or explicitly documented until each embedded `tools/call` can be counted accurately. + +--- + +## Planned Folder Layout + +All paths are under `workshop/`. + +| Path | Purpose | +| --- | --- | +| `mcp-hr/server/server.py` | FastAPI app, `/health`, MCP JSON-RPC endpoint, and tool registration. | +| `mcp-hr/server/hr_data.py` | Deterministic demo data, read helpers, and mutable in-memory state. | +| `mcp-hr/server/auth.py` | Microsoft Entra token validation helpers. | +| `mcp-hr/server/telemetry.py` | OpenTelemetry and Application Insights setup. | +| `mcp-hr/server/Dockerfile` | Container image definition for ACR remote build. | +| `mcp-hr/hosted-agent/main.py` | Foundry hosted agent (FoundryChatClient + APIM MCP tool + ResponsesHostServer). | +| `mcp-hr/hosted-agent/requirements.txt` | Pinned hosted-agent dependencies (agent-framework split packages, mcp, httpx). | +| `mcp-hr/hosted-agent/Dockerfile` | Hosted-agent image for ACR remote build. | +| `mcp-hr/hosted-agent/agent.yaml` | Reference hosted-agent manifest (env vars documented). | +| `mcp-hr/infra/main.bicep` | Self-contained APIM MCP API, backend, Citadel-style product, subscription, and access-contract template. | +| `mcp-hr/infra/policies/hr-mcp-api-policy.xml` | API-level backend routing policy that binds the MCP API to the HR MCP APIM backend. | +| `mcp-hr/infra/policies/hr-mcp-product-policy.xml` | JWT validation, MCP tool-call rate limiting, and bounded body logging. | +| `mcp-hr/infra/README.md` | APIM publication inputs, private-backend expectations, and policy streaming notes. | +| `mcp-hr/infra/snippets/` | Optional MCP Inspector and GitHub Copilot configuration snippets. | +| `mcp-hr/scripts/deploy-hr-mcp.sh` | Bash deployment for ACA, ACR, telemetry, auth settings, and direct smoke tests. | +| `mcp-hr/scripts/deploy-hr-mcp.ps1` | PowerShell equivalent of direct deployment. | +| `mcp-hr/scripts/publish-hr-mcp-apim.sh` | Bash APIM publication and access-contract deployment. | +| `mcp-hr/scripts/publish-hr-mcp-apim.ps1` | PowerShell equivalent of APIM publication. | +| `mcp-hr/hr-mcp-implementation-plan.md` | Public implementation plan. | +| `9. publish-and-use-hr-mcp-via-apim.ipynb` | Workshop notebook for APIM-mediated MCP validation and agent use. | + +The MCP container installs from `mcp-hr/server/requirements.txt`, generated from `mcp-hr/server/pyproject.toml` with uv. Do not add MCP-only runtime packages to the broader workshop notebook environment. + +--- + +## HR MCP Server Design + +The server exposes deterministic demo HR data with dynamic results based on request parameters, current dates, and mutable in-memory state. + +### Read-only tools + +| Tool | Behavior | +| --- | --- | +| `search_employees(query, department=None, location=None)` | Returns ranked employee matches with optional department and location filters. | +| `get_employee_profile(employee_id)` | Returns profile details, manager chain, skills, PTO balance, and policy flags. | +| `recommend_learning_path(employee_id, target_role)` | Computes role-readiness gaps and recommends courses from profile and skill data. | + +### Read-write tools + +| Tool | Behavior | +| --- | --- | +| `submit_pto_request(employee_id, start_date, end_date, reason)` | Validates business days, PTO balance, blackout dates, and approval routing before creating a request. | +| `update_employee_skills(employee_id, skills, evidence_note)` | Merges skill changes, records an audit event, and returns before/after deltas. | + +Write tools should return structured JSON with stable error enums such as `NOT_FOUND`, `INSUFFICIENT_BALANCE`, `POLICY_CONFLICT`, and `STATE_CONFLICT`. Tool-level validation failures should be returned as MCP tool payloads rather than protocol-breaking HTTP errors. + +--- + +## Microsoft Entra Authentication Model + +Participants use their own Microsoft Entra identity for every supported client path. + +### App registrations + +- Create or reuse an Entra API app registration for the HR MCP resource. +- Use an Application ID URI pattern such as `api://`. +- Expose a delegated scope such as `Mcp.Access` or `user_impersonation`. +- Create or reuse a public client app registration for local tools and notebooks. +- Save only non-secret configuration values to `azd env`, including tenant, audience, scope, and public client identifiers. + +### Direct ACA access + +- Participants acquire a delegated token for the HR MCP scope. +- Calls to the public ACA `/mcp` endpoint include `Authorization: Bearer `. +- The MCP server validates issuer, audience, signature, expiry, and required delegated scope. +- `/health` may remain unauthenticated for ACA probes, but it must not expose sensitive data. + +### APIM access + +- Participants call the APIM MCP endpoint with the same delegated token. +- APIM validates issuer, audience, expiry, and required delegated scope in the access-contract policy. +- APIM forwards the original bearer token to the private ACA backend. +- The MCP server performs the same validation for direct and APIM-mediated calls. + +### Local clients and notebooks + +- Scripts should print token acquisition commands that use placeholders, for example `az account get-access-token --scope api:///Mcp.Access`. +- MCP Inspector and GitHub Copilot snippets should include the MCP endpoint and an `Authorization` header placeholder. +- The notebook should acquire delegated tokens with `InteractiveBrowserCredential`, `DeviceCodeCredential`, or `AzureCliCredential`. +- Raw tokens must never be printed or logged. + +--- + +## Direct ACA Deployment Plan + +The direct deployment scripts should: + +1. Read defaults from `azd env`, including location, subscription context, and base resource group. +2. Create a separate HR MCP resource group. +3. Create Log Analytics and workspace-based Application Insights. +4. Create or configure Entra app registrations for delegated participant access. +5. Create an ACR Basic registry with admin access disabled. +6. Build the MCP server image remotely with ACR. +7. Create a public ACA environment and Container App on port `8080`. +8. Configure public external ingress for direct troubleshooting. +9. Discover the Citadel hub VNet in the hub resource group and create/use the dedicated ACA-delegated `snet-mcp` subnet by selecting an available prefix from the existing VNet address space. +10. Create a private/internal ACA environment and Container App in that subnet for APIM backend traffic. +11. Configure scaling with min replicas `0` and max replicas `2` for both apps. +12. Set environment variables for authentication, telemetry, and service identity. +13. Add health and startup probes. +14. Save public direct endpoint, private backend endpoint, Citadel VNet/subnet, and non-secret auth values to `azd env`. +15. Run direct smoke tests for `/health`, `initialize`, `tools/list`, one read tool, and one write tool. + +Before the image build, MCP server dependency changes must use the server-local `uv` workflow: + +```bash +cd workshop/mcp-hr/server +uv pip compile pyproject.toml -o requirements.txt +``` + +--- + +## APIM Publishing and Citadel Access Contract + +APIM assets should be self-contained under `workshop/mcp-hr/infra/`. + +### MCP API publication + +- Publish the HR MCP server as an APIM API with `type: mcp`. +- Use a stable path such as `hr-mcp`. +- Configure an APIM backend that reaches the private/internal ACA app over private networking. The publication scripts should prefer `HR_MCP_PRIVATE_BACKEND_URL`, emitted by `deploy-hr-mcp.*`. +- The APIM endpoint is `${gatewayUrl}/hr-mcp/mcp`; the backend value is the ACA base URL without a trailing `/mcp`. +- If private ACA reachability is not yet configured, the publication scripts require `HR_MCP_PRIVATE_BACKEND_URL` or `HR_MCP_APIM_BACKEND_BASE_URL` when `HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND=true` instead of silently publishing a public-only backend. The direct public ACA endpoint remains available for authenticated troubleshooting. +- Preserve MCP streamable HTTP behavior and avoid unsafe response buffering. +- Add correlation headers where they do not interfere with MCP transport. + +### Access contract + +- Create a Citadel-style APIM product such as `MCP-HR-Tools-DEV`. +- Attach the `hr-mcp` API to the product. +- Create a subscription such as `MCP-HR-Tools-DEV-SUB-01` for consistency with existing access-contract conventions. +- Keep participant Entra tokens as the primary identity mechanism. +- Store or output only non-secret metadata consistently with workshop patterns. + +### Product policy + +The product policy should: + +- Validate Entra JWTs for the HR MCP API audience and delegated scope. +- Allow normal MCP protocol setup methods without tool-call rate-limit impact: + - `initialize` + - `notifications/initialized` + - `tools/list` + - `prompts/list` + - `resources/list` + - `logging/setLevel` +- Apply rate limiting only when the JSON-RPC method is `tools/call`. +- Limit each participant to `5` `tools/call` requests per `30` seconds. +- Use a participant-specific counter key, preferably JWT `oid` plus product or subscription context. +- Return HTTP `429` when the limit is exceeded. +- Reject or document JSON-RPC batch requests if APIM cannot accurately count each embedded `tools/call`. +- Forward the original `Authorization: Bearer ...` header to the HR MCP backend so the server can repeat delegated-token validation. + +### APIM logging + +APIM should log bounded details for MCP tool calls: + +- JSON-RPC method name. +- MCP tool name. +- Participant object ID or another non-sensitive user claim. +- Correlation ID. +- Status code. +- Bounded request body snippet. +- Bounded response body snippet. + +Authorization headers, bearer tokens, subscription keys, and other secrets must not be logged. If policy-level response body access interferes with streamable MCP, prefer APIM diagnostic body logging or a documented conditional non-streaming validation path. + +The current product policy inspects JSON request bodies to identify `tools/call` and rejects JSON-RPC batch payloads with a JSON `400` error. Response-body snippets are captured only when the response is not `text/event-stream`, preserving streamable MCP behavior where APIM body buffering would be unsafe. + +--- + +## Notebook Plan + +Create the notebook after direct deployment and APIM validation are implemented. The notebook should follow existing workshop patterns: infrastructure is deployed by scripts first, then notebook cells discover configuration from `azd env` and validate the scenario. + +The notebook should: + +1. Load configuration from `azd env`. +2. Verify Azure CLI login and subscription context. +3. Resolve the APIM MCP endpoint and access-contract settings. +4. Acquire a participant delegated Entra token. +5. Create or validate the Citadel-style APIM access contract for the HR MCP API. +6. Validate APIM MCP protocol calls only. +7. Validate APIM HR read and write tool calls only. +8. Trigger the `5` calls per `30` seconds rate limit and show the `429` response. +9. Show where APIM request/response body logs appear. +10. Build or configure a Microsoft Agent Framework app that uses the APIM-published MCP endpoint. +11. Demonstrate an HR workflow through APIM that combines employee lookup, learning-path recommendation, PTO request submission, and skill update. +12. Show relevant Application Insights and APIM telemetry for MCP tool calls. + +The notebook should not call the direct ACA endpoint. Direct ACA testing remains a deployment-script and troubleshooting responsibility. + +### Microsoft Agent Framework hosted agent + +The notebook deploys a Foundry **hosted agent** (same pattern as workshop notebook #7) whose tools come from the HR MCP server **published through APIM**. Source lives in `workshop/mcp-hr/hosted-agent/` (`main.py`, `requirements.txt`, `Dockerfile`, `agent.yaml`): + +- `FoundryChatClient` drives reasoning with the same Foundry gateway model as #7 (`Hub-HR-ChatAgent-DEV-LLM/gpt-4.1`). +- `MCPStreamableHTTPTool` (`hr_mcp_via_apim`) targets `HR_MCP_APIM_MCP_URL`. A custom `httpx` client request hook attaches, on every request (handshake + tool calls), a fresh Entra token plus the APIM subscription key — so a long-lived hosted agent never uses an expiring token. +- `ResponsesHostServer` exposes the agent over the Foundry responses protocol. + +Authentication is app-to-app via the agent's **managed identity**: + +- The HR MCP API app registration exposes a delegated scope `Mcp.Access` (interactive users) **and** an application role `Mcp.Invoke` (applications). Entra forbids a scope and role sharing the same value, hence the two names. +- The agent acquires an app-only token for `api:///.default`; the token carries `roles: ["Mcp.Invoke"]`. +- The APIM product policy and the MCP server both accept either the delegated scope (`scp` = `Mcp.Access`) or the app role (`roles` = `Mcp.Invoke`), so users and the hosted agent share one access contract. +- `deploy-hr-mcp.*` creates the app role; the notebook grants it (`appRoleAssignedTo`) and `Foundry User` to the agent's instance managed identity after deployment. + +The notebook flow: resolve config → ACR build of `mcp-hr/hosted-agent` → `AIProjectClient.create_version` (injecting `HR_MCP_APIM_MCP_URL`, `HR_MCP_AUDIENCE`, `HR_MCP_APIM_SUBSCRIPTION_KEY`, model) → wait active → grant RBAC/app role → invoke with HR prompts that exercise the MCP tools through APIM. + +--- + +## Validation Plan + +Minimum validation during implementation: + +- Install Python dependencies in an isolated environment. +- Confirm `workshop/mcp-hr/server/requirements.txt` is current after MCP server dependency changes. Regenerate `workshop/requirements.txt` only when notebook/helper dependencies change. +- Run the MCP server locally and call `/health`. +- Call MCP JSON-RPC methods locally: + - `initialize` + - `tools/list` + - at least one read `tools/call` + - at least one write `tools/call` +- Run script syntax checks: + - `bash -n workshop/mcp-hr/scripts/deploy-hr-mcp.sh` + - PowerShell parse or syntax check if `pwsh` is available. +- Run Azure deployment end to end where permissions and quota allow. +- Confirm ACR remote build succeeds. +- Confirm direct ACA `/health` and `/mcp` calls succeed with a participant token. +- Confirm unauthenticated direct ACA `/mcp` calls fail. +- Confirm APIM `/hr-mcp/mcp` calls succeed with a participant token. +- Confirm APIM-to-ACA backend connectivity uses the private path. +- Confirm APIM returns `429` after more than `5` `tools/call` requests in `30` seconds for the same participant. +- Confirm non-tool MCP protocol methods are not counted against the tool-call limit. +- Confirm APIM logs bounded MCP request and response bodies without logging authorization headers. +- Run the notebook through all non-destructive cells and deterministic write-tool cells. +- Test MCP Inspector or GitHub Copilot configuration from a local machine where feasible. + +If Azure permissions, tenant restrictions, or quota block a validation step, document the blocker clearly and keep the implementation runnable for the next validation attempt. + +--- + +## Operational Notes + +- Treat generated endpoint URLs, app IDs, scopes, and tenant values as configuration; do not hard-code environment-specific resource names. +- Store secrets only in approved Azure secret stores or APIM subscription records. Do not print raw keys or bearer tokens. +- Keep direct ACA access enabled for troubleshooting but protected by Entra OAuth. +- Use Application Insights correlation IDs and APIM request IDs to connect client calls, gateway policy decisions, and server telemetry. +- Keep APIM logging bounded to reduce cost and avoid leaking HR payloads beyond the workshop intent. +- Review rate-limit values before production reuse; the workshop value is intentionally small to demonstrate throttling. +- Reset mutable in-memory state by restarting the Container App revision; do not rely on in-memory state for production scenarios. +- Disable diagnostic response headers or verbose logging before adapting the pattern to production. + +--- + +## Maintainer Checklist + +- [ ] Keep all new scenario files under `workshop/`. +- [ ] Update `workshop/mcp-hr/server/pyproject.toml` before adding MCP server runtime dependencies. +- [ ] Regenerate `workshop/mcp-hr/server/requirements.txt` with `uv`. +- [ ] Validate direct ACA smoke tests before APIM publication. +- [ ] Validate APIM policy behavior before creating the final notebook. +- [ ] Confirm no tenant IDs, subscription IDs, bearer tokens, secrets, or environment-specific resource names are committed. +- [ ] Update this plan if design choices change during implementation. diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/README.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/README.md new file mode 100644 index 000000000..e1f53f759 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/README.md @@ -0,0 +1,53 @@ +# HR MCP APIM publication + +This folder is self-contained Bicep for publishing the workshop HR MCP Container App through an existing APIM instance. + +It creates: + +- APIM `type: mcp` API at path `hr-mcp`, exposed as `${gatewayUrl}/hr-mcp/mcp`. +- APIM backend pointing at the HR MCP backend **base URL** (no trailing `/mcp`). +- Citadel-style product/access contract `MCP-HR-Tools-DEV`. +- Deterministic subscription `MCP-HR-Tools-DEV-SUB-01`. +- Product policy with Entra JWT validation, bearer forwarding, `tools/call` throttling, JSON-RPC batch rejection, and bounded trace snippets. + +## Container base images and Docker Hub rate limits + +The HR MCP server image (`mcp-hr/server/Dockerfile`) builds on `ghcr.io/astral-sh/uv:python3.13-bookworm-slim`, a single GHCR image that bundles both Python 3.13 and `uv`. GHCR is not subject to Docker Hub's anonymous pull-rate limit (`toomanyrequests`), which previously caused ACR remote builds to fail or appear to hang while pulling `python:3.13-slim` from Docker Hub. + +To further reduce external pulls, the server Dockerfile parameterizes the base image with a build arg (`BASE_IMAGE`), and the build path first mirrors the image into the target ACR with `az acr import`, then builds against the ACR-local copy: + +- `deploy-hr-mcp.*` imports `ghcr.io/astral-sh/uv:python3.13-bookworm-slim` into the HR MCP ACR and builds the server image with `--build-arg BASE_IMAGE=/astral-sh/uv-python:3.13-bookworm-slim`. + +If `az acr import` is unavailable or fails, the build falls back to the public GHCR image (a warning is printed). The default remains the public GHCR image, so the Dockerfile still works without ACR mirroring. + +## Networking + +`deploy-hr-mcp.*` creates two Container Apps paths: + +- a public, Entra-protected ACA endpoint for direct laptop troubleshooting; +- a private/internal ACA endpoint for APIM backend traffic. + +For the private path, the deploy script discovers the Citadel hub VNet in the hub resource group and creates/uses a dedicated `snet-mcp` subnet delegated to `Microsoft.App/environments`. It inspects the VNet address space and existing subnets, then selects the first available subnet prefix (`/26` by default, configurable with `HR_MCP_ACA_SUBNET_PREFIX_LENGTH`). Override with `HR_MCP_CITADEL_VNET_NAME`, `HR_MCP_ACA_SUBNET_NAME`, `HR_MCP_ACA_SUBNET_PREFIX`, or `HR_MCP_ACA_SUBNET_ID` when needed. The script does not add VNet address space automatically; if no suitable prefix is available, expand the Citadel VNet or provide an available `HR_MCP_ACA_SUBNET_PREFIX`. + +The APIM publication scripts prefer `HR_MCP_PRIVATE_BACKEND_URL`, emitted by `deploy-hr-mcp.*`. The public direct ACA troubleshooting endpoint remains unchanged. + +### Internal environment ingress (important) + +The private Container Apps environment is created with `--internal-only`, so it has no public endpoint and is reachable only from the peered/injected VNet. Within an **internal** environment, the app ingress type still matters: + +- `external: true` publishes the app on the environment's **VNet-facing** internal load balancer. This is what APIM (running in the hub VNet) must reach, and it is still private — there is no internet exposure because the environment itself is internal-only. The app FQDN is `.`. +- `external: false` publishes the app **only inside the environment** (app-to-app). APIM in the VNet cannot reach it and receives an `Azure Container App - Unavailable` 404 from the environment edge. + +Therefore the private HR MCP app is deployed with `--ingress external --transport http`. A custom private DNS zone for the environment `defaultDomain` (wildcard `A` records to the environment static IP) is linked to the hub VNet so APIM can resolve the backend FQDN. + +### Private DNS + +`deploy-hr-mcp.*` creates a private DNS zone for the internal environment `defaultDomain`, links it to the Citadel hub VNet, and adds wildcard `A` records (`*` and `*.internal`) pointing to the environment static IP. APIM resolves the backend FQDN through this zone. + +### Backend Host header + +The APIM API policy forwards a single `Host` header equal to the backend FQDN (`set-header name="Host" exists-action="override"`). This is required because the ACA environment edge routes requests by Host; without the override APIM may forward the gateway hostname and the edge returns a 404 `Unavailable` page. + +## Policy streaming note + +The product policy inspects JSON request bodies so it can identify `tools/call` and reject batches. Response snippets are logged only when the response is not `text/event-stream`; this avoids buffering streamable MCP responses where possible. Authorization headers and bearer tokens are not logged. diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/main.bicep b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/main.bicep new file mode 100644 index 000000000..b68282aec --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/main.bicep @@ -0,0 +1,165 @@ +targetScope = 'resourceGroup' + +@description('Existing APIM service name that will host the HR MCP API.') +param apimName string + +@description('APIM API resource name for the HR MCP publication.') +param apiName string = 'hr-mcp-api' + +@description('APIM API display name.') +param apiDisplayName string = 'HR MCP Tools' + +@description('APIM API path. The MCP endpoint is exposed at /{apiPath}/mcp.') +param apiPath string = 'hr-mcp' + +@description('APIM backend resource name for the HR MCP ACA endpoint.') +param backendName string = 'hr-mcp-aca-backend' + +@description('Backend base URL for the HR MCP Container App, without a trailing /mcp path.') +param backendBaseUrl string + +@description('Deprecated. Host is derived from the APIM backend URL; this parameter is retained for script compatibility.') +param backendHostHeader string = '' + +@description('Citadel-style APIM product/access-contract id.') +param productId string = 'MCP-HR-Tools-DEV' + +@description('Citadel-style APIM product display name.') +param productDisplayName string = 'MCP HR Tools DEV' + +@description('Citadel-style APIM subscription resource name.') +param subscriptionName string = 'MCP-HR-Tools-DEV-SUB-01' + +@description('Citadel-style APIM subscription display name.') +param subscriptionDisplayName string = 'MCP HR Tools DEV SUB 01' + +@description('Entra tenant id used for JWT validation.') +param tenantId string + +@description('Expected JWT audience, for example api://.') +param jwtAudience string + +@description('Required delegated scope claim value in the JWT scp claim, for example Mcp.Access.') +param requiredScope string + +@description('Required application role value in the JWT roles claim (for managed-identity callers), for example Mcp.Invoke.') +param requiredRole string = 'Mcp.Invoke' + +@description('Transport type for the APIM MCP API.') +param mcpTransportType string = 'streamable' + +@description('Set true to require APIM subscription key in addition to JWT.') +param subscriptionRequired bool = true + +@description('Product policy XML template. Placeholders are replaced by this template.') +param productPolicyTemplate string = loadTextContent('policies/hr-mcp-product-policy.xml') + +@description('API policy XML template. The backend placeholder is replaced by this template.') +param apiPolicyTemplate string = loadTextContent('policies/hr-mcp-api-policy.xml') + +var jwtAudienceClientId = last(split(jwtAudience, 'api://')) +var productPolicyXml = replace(replace(replace(replace(replace(productPolicyTemplate, '__TENANT_ID__', tenantId), '__JWT_AUDIENCE__', jwtAudience), '__JWT_AUDIENCE_CLIENT_ID__', jwtAudienceClientId), '__REQUIRED_SCOPE__', requiredScope), '__REQUIRED_ROLE__', requiredRole) +var backendUrlWithoutScheme = last(split(backendBaseUrl, '://')) +var backendUrlHost = first(split(backendUrlWithoutScheme, '/')) +var effectiveBackendHost = empty(backendHostHeader) ? backendUrlHost : backendHostHeader +var apiPolicyXml = replace(replace(apiPolicyTemplate, '__BACKEND_NAME__', backendName), '__BACKEND_HOST__', effectiveBackendHost) + +resource apim 'Microsoft.ApiManagement/service@2024-06-01-preview' existing = { + name: apimName +} + +resource backend 'Microsoft.ApiManagement/service/backends@2024-06-01-preview' = { + parent: apim + name: backendName + properties: { + description: 'HR MCP Azure Container Apps backend. Use a private ACA FQDN here when APIM has private network reachability.' + url: backendBaseUrl + protocol: 'http' + credentials: !empty(backendHostHeader) ? { + header: { + Host: [ + backendHostHeader + ] + } + } : null + } +} + +resource api 'Microsoft.ApiManagement/service/apis@2024-06-01-preview' = { + parent: apim + name: apiName + properties: { + type: 'mcp' + displayName: apiDisplayName + description: 'HR MCP server published through APIM as a Citadel governed MCP API.' + subscriptionRequired: subscriptionRequired + path: apiPath + protocols: [ + 'https' + ] + // A pass-through MCP API (proxying an existing remote MCP server) must reference the + // backend via backendId. APIM rejects the API otherwise: + // "Either BackendId or MCP tools must be set, but not both for MCP API." + #disable-next-line BCP037 + backendId: backend.name + #disable-next-line BCP037 + mcpProperties: { + transportType: mcpTransportType + } + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-06-01-preview' = { + parent: api + name: 'policy' + properties: { + format: 'rawxml' + value: apiPolicyXml + } +} + +resource product 'Microsoft.ApiManagement/service/products@2024-06-01-preview' = { + parent: apim + name: productId + properties: { + displayName: productDisplayName + description: 'Citadel-style access contract for the HR MCP tool server.' + terms: 'Access is limited to approved HR MCP tool consumers with Entra JWT and APIM subscription credentials.' + subscriptionRequired: true + approvalRequired: false + subscriptionsLimit: 10 + state: 'published' + } +} + +resource productApi 'Microsoft.ApiManagement/service/products/apis@2024-06-01-preview' = { + parent: product + name: api.name +} + +resource productPolicy 'Microsoft.ApiManagement/service/products/policies@2024-06-01-preview' = { + parent: product + name: 'policy' + properties: { + format: 'rawxml' + value: productPolicyXml + } +} + +resource subscription 'Microsoft.ApiManagement/service/subscriptions@2024-06-01-preview' = { + parent: apim + name: subscriptionName + properties: { + displayName: subscriptionDisplayName + scope: '/products/${product.name}' + state: 'active' + } +} + +output HR_MCP_APIM_API_NAME string = api.name +output HR_MCP_APIM_PATH string = apiPath +output HR_MCP_APIM_MCP_URL string = '${apim.properties.gatewayUrl}/${apiPath}/mcp' +output HR_MCP_APIM_PRODUCT_ID string = product.name +output HR_MCP_APIM_SUBSCRIPTION_NAME string = subscription.name +output HR_MCP_APIM_BACKEND_NAME string = backend.name +output HR_MCP_APIM_BACKEND_BASE_URL string = backendBaseUrl diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-api-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-api-policy.xml new file mode 100644 index 000000000..b32510dc5 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-api-policy.xml @@ -0,0 +1,18 @@ + + + + + + __BACKEND_HOST__ + + + + + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-product-policy.xml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-product-policy.xml new file mode 100644 index 000000000..692f68909 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/infra/policies/hr-mcp-product-policy.xml @@ -0,0 +1,120 @@ + + + + + + + __JWT_AUDIENCE__ + __JWT_AUDIENCE_CLIENT_ID__ + + + https://login.microsoftonline.com/__TENANT_ID__/v2.0 + https://sts.windows.net/__TENANT_ID__/ + + + + + + + + + + application/json + + {"error":"insufficient_scope","message":"Token is missing the required HR MCP scope or app role.","required":"__REQUIRED_SCOPE__"} + + + + + + @((string)context.Variables["hrMcpBearerToken"]) + + + + + + + + + + + + application/json + + {"jsonrpc":"2.0","error":{"code":-32600,"message":"JSON-RPC batch payloads are rejected by the HR MCP APIM access contract because per-tool rate accounting is enforced only for single requests."},"id":null} + + + + + + + + + + @{ + var body = (string)context.Variables.GetValueOrDefault("hrMcpRequestBody", string.Empty); + var snippet = body.Length > 2048 ? body.Substring(0, 2048) : body; + var tool = string.Empty; + try { tool = Newtonsoft.Json.Linq.JToken.Parse(body).SelectToken("params.name")?.ToString() ?? string.Empty; } catch { } + return "HR MCP tools/call request; tool=" + tool + "; participantKey=" + (string)context.Variables["hrMcpRateLimitKey"] + "; bodySnippet=" + snippet; + } + + + + + + + + + + + + + + + + + + + @{ + var body = (string)context.Variables.GetValueOrDefault("hrMcpResponseBody", string.Empty); + var snippet = body.Length > 2048 ? body.Substring(0, 2048) : body; + return "HR MCP tools/call response; status=" + context.Response.StatusCode + "; bodySnippet=" + snippet; + } + + + + + + + + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.ps1 new file mode 100644 index 000000000..fb6b422a1 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.ps1 @@ -0,0 +1,1205 @@ +[CmdletBinding()] +param( + [string]$ResourceGroupName = $env:HR_MCP_RESOURCE_GROUP, + [string]$AcrName = $env:HR_MCP_ACR_NAME, + [string]$ContainerAppName = $env:HR_MCP_CONTAINER_APP_NAME, + [string]$AcaEnvironmentName = $env:HR_MCP_ACA_ENVIRONMENT_NAME, + [string]$PrivateContainerAppName = $env:HR_MCP_PRIVATE_CONTAINER_APP_NAME, + [string]$PrivateAcaEnvironmentName = $env:HR_MCP_PRIVATE_ACA_ENVIRONMENT_NAME, + [string]$LogAnalyticsWorkspaceName = $env:HR_MCP_LOG_ANALYTICS_NAME, + [string]$AppInsightsName = $env:HR_MCP_APP_INSIGHTS_NAME, + [string]$ApiClientId = $env:HR_MCP_API_CLIENT_ID, + [string]$PublicClientId = $env:HR_MCP_PUBLIC_CLIENT_ID, + [string]$CitadelResourceGroup = $env:HR_MCP_CITADEL_RESOURCE_GROUP, + [string]$CitadelVnetName = $env:HR_MCP_CITADEL_VNET_NAME, + [string]$AcaSubnetName = $(if ($env:HR_MCP_ACA_SUBNET_NAME) { $env:HR_MCP_ACA_SUBNET_NAME } else { 'snet-mcp' }), + [string]$AcaSubnetPrefix = $env:HR_MCP_ACA_SUBNET_PREFIX, + [int]$AcaSubnetPrefixLength = $(if ($env:HR_MCP_ACA_SUBNET_PREFIX_LENGTH) { [int]$env:HR_MCP_ACA_SUBNET_PREFIX_LENGTH } else { 26 }), + [string]$AcaSubnetId = $env:HR_MCP_ACA_SUBNET_ID, + [bool]$ExpandVnet = $(if ($env:HR_MCP_ACA_EXPAND_VNET) { $env:HR_MCP_ACA_EXPAND_VNET -eq 'true' } else { $true }), + [int]$ExpandPrefixLength = $(if ($env:HR_MCP_ACA_EXPAND_PREFIX_LENGTH) { [int]$env:HR_MCP_ACA_EXPAND_PREFIX_LENGTH } else { 0 }), + [string]$ScopeName = $(if ($env:HR_MCP_SCOPE_NAME) { $env:HR_MCP_SCOPE_NAME } else { 'Mcp.Access' }), + [string]$AppRoleValue = $(if ($env:HR_MCP_APP_ROLE_VALUE) { $env:HR_MCP_APP_ROLE_VALUE } else { 'Mcp.Invoke' }) +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +$env:PYTHONWARNINGS = if ($env:PYTHONWARNINGS) { "$($env:PYTHONWARNINGS),ignore" } else { 'ignore' } +$env:AZURE_CORE_ONLY_SHOW_ERRORS = if ($env:AZURE_CORE_ONLY_SHOW_ERRORS) { $env:AZURE_CORE_ONLY_SHOW_ERRORS } else { 'True' } +if ($PSVersionTable.PSVersion.Major -ge 7) { + $PSNativeCommandUseErrorActionPreference = $false +} + +$acrPullRoleId = '7f951dda-4ed3-4680-a7ca-43fe172d538d' +$azureCliClientId = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' +$securityControlTag = 'SecurityControl=Ignore' + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" +} + +function Assert-Command { + param([string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command not found: $Name" + } +} + +function Get-AzdValue { + param([string]$Name) + $value = (& azd env get-value $Name 2>$null) + if ($LASTEXITCODE -ne 0) { return $null } + return ConvertTo-TrimmedCliOutput $value +} + +function ConvertTo-TrimmedCliOutput { + param([AllowNull()]$Value) + if ($null -eq $Value) { return $null } + $text = [string]($Value | Select-Object -First 1) + if ([string]::IsNullOrWhiteSpace($text)) { return $null } + return $text.Trim() +} + +function Get-FirstNonEmpty { + param([AllowNull()][string[]]$Values) + foreach ($value in $Values) { + if (-not [string]::IsNullOrWhiteSpace($value)) { return $value } + } + return $null +} + +function ConvertTo-SafeNamePart { + param([string]$Value) + $name = $Value.ToLowerInvariant() -replace '[^a-z0-9-]', '-' -replace '-+', '-' + $name = $name.Trim('-') + if ([string]::IsNullOrWhiteSpace($name)) { return 'hr-mcp' } + return $name +} + +function ConvertTo-CompactNamePart { + param([string]$Value) + $name = $Value.ToLowerInvariant() -replace '[^a-z0-9]', '' + if ([string]::IsNullOrWhiteSpace($name)) { return 'hrmcp' } + return $name +} + +function Invoke-NativeCommandQuietly { + param([scriptblock]$Command) + $previousErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = 'Continue' + & $Command *> $null + return $LASTEXITCODE + } + finally { + $ErrorActionPreference = $previousErrorActionPreference + } +} + +function Add-RoleAssignmentIfMissing { + param( + [string]$PrincipalId, + [string]$PrincipalType, + [string]$RoleId, + [string]$RoleName, + [string]$Scope + ) + + $assignmentId = $null + try { + $assignmentId = (& az role assignment list ` + --scope $Scope ` + --fill-principal-name false ` + --fill-role-definition-name false ` + --query "[?principalId=='$PrincipalId' && contains(roleDefinitionId, '$RoleId')].id | [0]" ` + -o tsv 2>$null) + } catch { $assignmentId = $null } + + if (-not [string]::IsNullOrWhiteSpace($assignmentId)) { + Write-Host "Role assignment already exists: $RoleName" + return + } + + & az role assignment create ` + --assignee-object-id $PrincipalId ` + --assignee-principal-type $PrincipalType ` + --role $RoleId ` + --scope $Scope ` + --only-show-errors ` + -o none + Write-Host "Assigned role: $RoleName" +} + +function Wait-RoleAssignment { + param( + [string]$PrincipalId, + [string]$RoleId, + [string]$Scope, + [string]$RoleName, + [int]$MaxRetries = 5, + [int]$SleepSeconds = 30 + ) + + for ($i = 1; $i -le $MaxRetries; $i++) { + $role = (& az role assignment list ` + --scope $Scope ` + --fill-principal-name false ` + --fill-role-definition-name false ` + --query "[?principalId=='$PrincipalId' && contains(roleDefinitionId, '$RoleId')].id | [0]" ` + -o tsv 2>$null) + if (-not [string]::IsNullOrWhiteSpace($role)) { + Write-Host "$RoleName role assignment is visible." + return + } + Write-Host "$RoleName role assignment not visible yet (attempt $i/$MaxRetries); waiting ${SleepSeconds}s..." + Start-Sleep -Seconds $SleepSeconds + } + + Write-Warning "$RoleName role assignment did not become visible after waiting. Continuing because role assignment creation succeeded; if image pull fails, wait a few minutes and rerun the script." +} + +function Find-AppObjectIdByDisplayName { + param([string]$DisplayName) + $objectId = (& az ad app list ` + --display-name $DisplayName ` + --query 'length(@) == `1` && [0].id || `MULTIPLE_OR_NONE`' ` + -o tsv 2>$null) + return ConvertTo-TrimmedCliOutput $objectId +} + +function Get-AppClientIdFromObjectId { + param([string]$ObjectId) + return ConvertTo-TrimmedCliOutput (& az ad app show --id $ObjectId --query appId -o tsv) +} + +function Ensure-SingleAppByDisplayName { + param([string]$DisplayName) + $objectId = Find-AppObjectIdByDisplayName -DisplayName $DisplayName + if ($objectId -eq 'MULTIPLE_OR_NONE') { + $count = ConvertTo-TrimmedCliOutput (& az ad app list --display-name $DisplayName --query 'length(@)' -o tsv 2>$null) + if ($count -eq '0') { + $createdId = (& az ad app create ` + --display-name $DisplayName ` + --sign-in-audience AzureADMyOrg ` + --query id ` + -o tsv) + if ($LASTEXITCODE -ne 0) { throw "Could not create app registration '$DisplayName'. You may need Application Developer permissions." } + return ConvertTo-TrimmedCliOutput $createdId + } + throw "Multiple app registrations named '$DisplayName' exist. Set HR_MCP_API_CLIENT_ID or HR_MCP_PUBLIC_CLIENT_ID to disambiguate." + } + return $objectId +} + +function Update-GraphApplication { + param( + [string]$ObjectId, + [string]$Body + ) + & az rest ` + --method PATCH ` + --uri "https://graph.microsoft.com/v1.0/applications/$ObjectId" ` + --headers 'Content-Type=application/json' ` + --body $Body ` + --only-show-errors ` + -o none + if ($LASTEXITCODE -ne 0) { + throw "Could not update Entra app registration $ObjectId. Ensure Microsoft Graph Application.ReadWrite.All or Application Developer permissions are available." + } +} + +function Ensure-ApiAppRegistration { + param( + [string]$DisplayName, + [AllowNull()][string]$RequestedClientId, + [string]$ScopeValue, + [string]$RoleValue + ) + + if (-not [string]::IsNullOrWhiteSpace($RequestedClientId)) { + $objectId = ConvertTo-TrimmedCliOutput (& az ad app show --id $RequestedClientId --query id -o tsv 2>$null) + if ([string]::IsNullOrWhiteSpace($objectId)) { throw "HR_MCP_API_CLIENT_ID '$RequestedClientId' was not found." } + } else { + $objectId = Ensure-SingleAppByDisplayName -DisplayName $DisplayName + } + + $clientId = Get-AppClientIdFromObjectId -ObjectId $objectId + $appIdUri = "api://$clientId" + $existingScopeId = ConvertTo-TrimmedCliOutput (& az ad app show --id $clientId --query "api.oauth2PermissionScopes[?value=='$ScopeValue'].id | [0]" -o tsv 2>$null) + $scopeId = if ([string]::IsNullOrWhiteSpace($existingScopeId)) { [guid]::NewGuid().ToString() } else { $existingScopeId } + + $existingRoleId = ConvertTo-TrimmedCliOutput (& az ad app show --id $clientId --query "appRoles[?value=='$RoleValue'].id | [0]" -o tsv 2>$null) + $roleId = if ([string]::IsNullOrWhiteSpace($existingRoleId)) { [guid]::NewGuid().ToString() } else { $existingRoleId } + + $body = @{ + identifierUris = @($appIdUri) + api = @{ + requestedAccessTokenVersion = 2 + oauth2PermissionScopes = @( + @{ + id = $scopeId + adminConsentDescription = 'Access the HR MCP API.' + adminConsentDisplayName = 'Access HR MCP' + isEnabled = $true + type = 'User' + userConsentDescription = 'Access the HR MCP API on your behalf.' + userConsentDisplayName = 'Access HR MCP' + value = $ScopeValue + } + ) + } + appRoles = @( + @{ + id = $roleId + allowedMemberTypes = @('Application') + description = 'Applications can call the HR MCP API.' + displayName = 'Invoke HR MCP (application)' + isEnabled = $true + value = $RoleValue + } + ) + } | ConvertTo-Json -Depth 8 -Compress + Update-GraphApplication -ObjectId $objectId -Body $body + + return [pscustomobject]@{ + ObjectId = $objectId + ClientId = $clientId + ScopeId = $scopeId + AppRoleId = $roleId + Audience = $appIdUri + Scope = "$appIdUri/$ScopeValue" + } +} + +function Ensure-PublicClientRegistration { + param( + [string]$DisplayName, + [AllowNull()][string]$RequestedClientId, + [string]$ApiClientId, + [string]$ScopeId + ) + + if (-not [string]::IsNullOrWhiteSpace($RequestedClientId)) { + $objectId = ConvertTo-TrimmedCliOutput (& az ad app show --id $RequestedClientId --query id -o tsv 2>$null) + if ([string]::IsNullOrWhiteSpace($objectId)) { throw "HR_MCP_PUBLIC_CLIENT_ID '$RequestedClientId' was not found." } + } else { + $objectId = Ensure-SingleAppByDisplayName -DisplayName $DisplayName + } + + $clientId = Get-AppClientIdFromObjectId -ObjectId $objectId + $body = @{ + isFallbackPublicClient = $true + publicClient = @{ redirectUris = @('http://localhost') } + requiredResourceAccess = @( + @{ + resourceAppId = $ApiClientId + resourceAccess = @(@{ id = $ScopeId; type = 'Scope' }) + } + ) + } | ConvertTo-Json -Depth 8 -Compress + Update-GraphApplication -ObjectId $objectId -Body $body + + return [pscustomobject]@{ + ObjectId = $objectId + ClientId = $clientId + } +} + +function Ensure-ApiServicePrincipal { + param([string]$ApiClientId) + + $existing = ConvertTo-TrimmedCliOutput (& az ad sp show --id $ApiClientId --query id -o tsv 2>$null) + if (-not [string]::IsNullOrWhiteSpace($existing)) { + Write-Host 'Service principal already exists for HR MCP API app.' + return + } + & az ad sp create --id $ApiClientId --only-show-errors -o none + if ($LASTEXITCODE -ne 0) { + throw "Could not create service principal for HR MCP API app '$ApiClientId'. Ensure you have permission to create Enterprise Applications/service principals." + } + Write-Host 'Created service principal for HR MCP API app.' +} + +function Set-ApiPreauthorizedClients { + param( + [string]$ApiObjectId, + [string]$ScopeId, + [string[]]$ClientIds + ) + + $app = (& az ad app show --id $ApiObjectId -o json) | ConvertFrom-Json + $api = if ($app.api) { $app.api } else { [pscustomobject]@{} } + $scopes = @($api.oauth2PermissionScopes) + $preauth = @($api.preAuthorizedApplications) + $byApp = @{} + foreach ($entry in $preauth) { + if ($entry.appId) { $byApp[$entry.appId] = $entry } + } + foreach ($clientId in $ClientIds) { + if ([string]::IsNullOrWhiteSpace($clientId)) { continue } + if (-not $byApp.ContainsKey($clientId)) { + $byApp[$clientId] = [pscustomobject]@{ + appId = $clientId + delegatedPermissionIds = @() + } + } + $ids = @($byApp[$clientId].delegatedPermissionIds) + if ($ids -notcontains $ScopeId) { + $byApp[$clientId].delegatedPermissionIds = @($ids + $ScopeId) + } + } + $body = @{ + api = @{ + requestedAccessTokenVersion = $(if ($api.requestedAccessTokenVersion) { $api.requestedAccessTokenVersion } else { 2 }) + oauth2PermissionScopes = $scopes + preAuthorizedApplications = @($byApp.Values) + } + } | ConvertTo-Json -Depth 12 -Compress + Update-GraphApplication -ObjectId $ApiObjectId -Body $body + Write-Host 'Pre-authorized Azure CLI/public clients for HR MCP delegated scope.' +} + +function Save-AzdValue { + param([string]$Name, [AllowNull()][string]$Value) + if (-not [string]::IsNullOrWhiteSpace($Value)) { + & azd env set $Name $Value *> $null + } +} + +function Add-PrivateDnsARecord { + param( + [string]$ZoneResourceGroup, + [string]$ZoneName, + [string]$RecordName, + [string]$IpAddress + ) + + if ((Invoke-NativeCommandQuietly { & az network private-dns record-set a show --resource-group $ZoneResourceGroup --zone-name $ZoneName --name "$RecordName" }) -ne 0) { + & az network private-dns record-set a create ` + --resource-group $ZoneResourceGroup ` + --zone-name $ZoneName ` + --name "$RecordName" ` + --ttl 30 ` + --only-show-errors ` + -o none + } + + $existing = ConvertTo-TrimmedCliOutput (& az network private-dns record-set a show ` + --resource-group $ZoneResourceGroup ` + --zone-name $ZoneName ` + --name "$RecordName" ` + --query "aRecords[?ipv4Address=='$IpAddress'].ipv4Address | [0]" ` + -o tsv 2>$null) + if ([string]::IsNullOrWhiteSpace($existing)) { + & az network private-dns record-set a add-record ` + --resource-group $ZoneResourceGroup ` + --zone-name $ZoneName ` + --record-set-name "$RecordName" ` + --ipv4-address $IpAddress ` + --only-show-errors ` + -o none + } +} + +function Set-PrivateAcaDns { + param( + [string]$ZoneResourceGroup, + [string]$VnetId, + [string]$DefaultDomain, + [string]$StaticIp + ) + + if ([string]::IsNullOrWhiteSpace($DefaultDomain)) { throw 'Private Container Apps environment defaultDomain is missing; cannot configure private DNS.' } + if ([string]::IsNullOrWhiteSpace($StaticIp)) { throw 'Private Container Apps environment staticIp is missing; cannot configure private DNS.' } + + Write-Step "Configuring private DNS for internal ACA domain: $DefaultDomain" + if ((Invoke-NativeCommandQuietly { & az network private-dns zone show --resource-group $ZoneResourceGroup --name $DefaultDomain }) -ne 0) { + & az network private-dns zone create ` + --resource-group $ZoneResourceGroup ` + --name $DefaultDomain ` + --only-show-errors ` + -o none + } + + $linkName = 'lnk-hr-mcp-aca' + if ((Invoke-NativeCommandQuietly { & az network private-dns link vnet show --resource-group $ZoneResourceGroup --zone-name $DefaultDomain --name $linkName }) -ne 0) { + & az network private-dns link vnet create ` + --resource-group $ZoneResourceGroup ` + --zone-name $DefaultDomain ` + --name $linkName ` + --virtual-network $VnetId ` + --registration-enabled false ` + --only-show-errors ` + -o none + } + + Add-PrivateDnsARecord -ZoneResourceGroup $ZoneResourceGroup -ZoneName $DefaultDomain -RecordName '*' -IpAddress $StaticIp + Add-PrivateDnsARecord -ZoneResourceGroup $ZoneResourceGroup -ZoneName $DefaultDomain -RecordName '*.internal' -IpAddress $StaticIp +} + +function Get-PropertyValue { + param([AllowNull()]$InputObject, [string]$Name) + if ($null -eq $InputObject) { return $null } + $member = $InputObject.PSObject.Properties[$Name] + if ($null -eq $member) { return $null } + return $member.Value +} + +function Test-AcaDelegation { + param($SubnetObject) + + $properties = Get-PropertyValue -InputObject $SubnetObject -Name 'properties' + $delegations = Get-PropertyValue -InputObject $properties -Name 'delegations' + if ($delegations) { + return $null -ne (@($delegations | Where-Object { (Get-PropertyValue -InputObject (Get-PropertyValue -InputObject $_ -Name 'properties') -Name 'serviceName') -eq 'Microsoft.App/environments' }) | Select-Object -First 1) + } + + $delegations = Get-PropertyValue -InputObject $SubnetObject -Name 'delegations' + if ($delegations) { + return $null -ne (@($delegations | Where-Object { (Get-PropertyValue -InputObject $_ -Name 'serviceName') -eq 'Microsoft.App/environments' }) | Select-Object -First 1) + } + return $false +} + +function ConvertTo-IPv4UInt32 { + param([string]$Address) + $bytes = [System.Net.IPAddress]::Parse($Address).GetAddressBytes() + [Array]::Reverse($bytes) + return [BitConverter]::ToUInt32($bytes, 0) +} + +function Test-IsPrivateIPv4Network { + # True only when the entire CIDR block falls inside an RFC 1918 private range. + param([string]$Cidr) + $parts = $Cidr.Split('/') + $ip = [uint32](ConvertTo-IPv4UInt32 $parts[0]) + $prefix = [int]$parts[1] + $size = [uint64]1 -shl (32 - $prefix) + $start = [uint64]$ip + $end = $start + $size - 1 + $ranges = @( + @{ Start = [uint64](ConvertTo-IPv4UInt32 '10.0.0.0'); End = [uint64](ConvertTo-IPv4UInt32 '10.255.255.255') }, + @{ Start = [uint64](ConvertTo-IPv4UInt32 '172.16.0.0'); End = [uint64](ConvertTo-IPv4UInt32 '172.31.255.255') }, + @{ Start = [uint64](ConvertTo-IPv4UInt32 '192.168.0.0'); End = [uint64](ConvertTo-IPv4UInt32 '192.168.255.255') } + ) + foreach ($r in $ranges) { + if ($start -ge $r.Start -and $end -le $r.End) { return $true } + } + return $false +} + +function Test-CidrContains { + param( + [string]$ParentCidr, + [string]$ChildCidr + ) + $parentParts = $ParentCidr.Split('/') + $childParts = $ChildCidr.Split('/') + $parentPrefix = [int]$parentParts[1] + $childPrefix = [int]$childParts[1] + if ($childPrefix -lt $parentPrefix) { return $false } + $parentIp = ConvertTo-IPv4UInt32 $parentParts[0] + $childIp = ConvertTo-IPv4UInt32 $childParts[0] + $mask = if ($parentPrefix -eq 0) { [uint32]0 } else { [uint32]::MaxValue -shl (32 - $parentPrefix) } + return (($parentIp -band $mask) -eq ($childIp -band $mask)) +} + +function Test-PrefixInsideAnyVnetPrefix { + param( + [string]$RequestedPrefix, + [string[]]$VnetPrefixes + ) + foreach ($prefix in $VnetPrefixes) { + if (Test-CidrContains -ParentCidr $prefix -ChildCidr $RequestedPrefix) { + return $true + } + } + return $false +} + +function Test-CidrOverlap { + param( + [string]$CidrA, + [string]$CidrB + ) + return (Test-CidrContains -ParentCidr $CidrA -ChildCidr $CidrB) -or (Test-CidrContains -ParentCidr $CidrB -ChildCidr $CidrA) +} + +function Get-SubnetCandidates { + param( + [string]$ParentCidr, + [int]$NewPrefixLength + ) + $parts = $ParentCidr.Split('/') + $parentIp = ConvertTo-IPv4UInt32 $parts[0] + $parentPrefix = [int]$parts[1] + if ($parentPrefix -gt $NewPrefixLength) { return @() } + $parentSize = [uint64]1 -shl (32 - $parentPrefix) + $candidateSize = [uint64]1 -shl (32 - $NewPrefixLength) + $parentMask = if ($parentPrefix -eq 0) { [uint32]0 } else { [uint32]::MaxValue -shl (32 - $parentPrefix) } + $network = [uint32]($parentIp -band $parentMask) + $results = @() + for ($offset = [uint64]0; $offset -lt $parentSize; $offset += $candidateSize) { + $candidate = [uint32]($network + $offset) + $bytes = [BitConverter]::GetBytes($candidate) + [Array]::Reverse($bytes) + $results += "$([System.Net.IPAddress]::new($bytes))/$NewPrefixLength" + } + return $results +} + +function Find-AvailableSubnetPrefix { + param( + [string[]]$VnetPrefixes, + [string[]]$UsedSubnetPrefixes, + [int]$PrefixLength + ) + foreach ($parent in ($VnetPrefixes | Sort-Object)) { + foreach ($candidate in (Get-SubnetCandidates -ParentCidr $parent -NewPrefixLength $PrefixLength)) { + $overlaps = $false + foreach ($used in $UsedSubnetPrefixes) { + if ($used -and (Test-CidrOverlap -CidrA $candidate -CidrB $used)) { + $overlaps = $true + break + } + } + if (-not $overlaps) { return $candidate } + } + } + return $null +} + +function Get-ExpansionSubnetPrefix { + # When no free subnet exists inside the current address space, compute how to + # expand the VNet: a new address prefix to ADD (sized to match the existing VNet + # block so addressing stays consistent, e.g. extend 10.170.0.0/24 -> add + # 10.170.1.0/24) plus the subnet to carve from it (e.g. 10.170.1.0/26). + param( + [string[]]$VnetPrefixes, + [string[]]$UsedSubnetPrefixes, + [int]$PrefixLength, + [int]$SupernetLength = 0 + ) + $ipv4 = @($VnetPrefixes | Where-Object { $_ -and $_ -notmatch ':' }) + if ($ipv4.Count -eq 0) { return $null } + + if ($SupernetLength -le 0) { + $SupernetLength = ($ipv4 | ForEach-Object { [int]($_.Split('/')[1]) } | Measure-Object -Minimum).Minimum + } + if ($SupernetLength -gt $PrefixLength) { $SupernetLength = $PrefixLength } + + $block = [uint64]1 -shl (32 - $SupernetLength) + $maxEnd = [uint64]0 + foreach ($p in $ipv4) { + $parts = $p.Split('/') + $ip = [uint64](ConvertTo-IPv4UInt32 $parts[0]) + $size = [uint64]1 -shl (32 - [int]$parts[1]) + $end = $ip + $size - 1 + if ($end -gt $maxEnd) { $maxEnd = $end } + } + $candidateInt = [uint64]([math]::Floor(($maxEnd + 1 + $block - 1) / $block)) * $block + + for ($i = 0; $i -lt 4096; $i++) { + if (($candidateInt + $block - 1) -gt [uint64]4294967295) { break } + $bytes = [BitConverter]::GetBytes([uint32]$candidateInt) + [Array]::Reverse($bytes) + $vnetBlock = "$([System.Net.IPAddress]::new($bytes))/$SupernetLength" + if ((Test-IsPrivateIPv4Network -Cidr $vnetBlock)) { + $overlaps = $false + foreach ($existing in ($VnetPrefixes + $UsedSubnetPrefixes)) { + if ($existing -and (Test-CidrOverlap -CidrA $vnetBlock -CidrB $existing)) { $overlaps = $true; break } + } + if (-not $overlaps) { + $subnet = (Get-SubnetCandidates -ParentCidr $vnetBlock -NewPrefixLength $PrefixLength)[0] + return [pscustomobject]@{ VnetPrefix = $vnetBlock; SubnetPrefix = $subnet } + } + } + $candidateInt += $block + } + return $null +} + +function Get-ExpansionVnetPrefixForSubnet { + # Given an explicit subnet prefix outside the current VNet address space, return the + # aligned address-space block (sized to the existing VNet block style) that contains + # it and can be added, e.g. subnet 10.170.1.0/26 -> add 10.170.1.0/24. + param( + [string]$SubnetPrefix, + [string[]]$VnetPrefixes, + [string[]]$UsedSubnetPrefixes, + [int]$SupernetLength = 0 + ) + $subnetParts = $SubnetPrefix.Split('/') + $subnetPrefixLen = [int]$subnetParts[1] + $ipv4 = @($VnetPrefixes | Where-Object { $_ -and $_ -notmatch ':' }) + + if ($SupernetLength -le 0) { + if ($ipv4.Count -gt 0) { + $SupernetLength = ($ipv4 | ForEach-Object { [int]($_.Split('/')[1]) } | Measure-Object -Minimum).Minimum + } else { + $SupernetLength = $subnetPrefixLen + } + } + if ($SupernetLength -gt $subnetPrefixLen) { $SupernetLength = $subnetPrefixLen } + + $subnetIp = [uint32](ConvertTo-IPv4UInt32 $subnetParts[0]) + $mask = if ($SupernetLength -eq 0) { [uint32]0 } else { [uint32]::MaxValue -shl (32 - $SupernetLength) } + $network = [uint32]($subnetIp -band $mask) + $bytes = [BitConverter]::GetBytes($network) + [Array]::Reverse($bytes) + $block = "$([System.Net.IPAddress]::new($bytes))/$SupernetLength" + + if (-not (Test-IsPrivateIPv4Network -Cidr $block)) { return $null } + foreach ($existing in ($VnetPrefixes + $UsedSubnetPrefixes)) { + if ($existing -and (Test-CidrOverlap -CidrA $block -CidrB $existing)) { return $null } + } + return $block +} + +function Resolve-CitadelAcaSubnet { + param( + [string]$HubResourceGroup, + [AllowNull()][string]$RequestedVnetName, + [string]$RequestedSubnetName, + [AllowNull()][string]$RequestedSubnetId, + [AllowNull()][string]$RequestedSubnetPrefix, + [int]$RequestedSubnetPrefixLength, + [bool]$AllowExpandVnet = $true, + [int]$ExpandSupernetLength = 0 + ) + + if (-not [string]::IsNullOrWhiteSpace($RequestedSubnetId)) { + $subnet = & az network vnet subnet show --ids $RequestedSubnetId -o json 2>$null + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($subnet)) { + throw "HR_MCP_ACA_SUBNET_ID was provided but could not be resolved: $RequestedSubnetId" + } + $subnetObj = $subnet | ConvertFrom-Json + $parts = $subnetObj.id -split '/' + $vnetName = $parts[[Array]::IndexOf($parts, 'virtualNetworks') + 1] + if (-not (Test-AcaDelegation -SubnetObject $subnetObj)) { + throw "Subnet '$($subnetObj.name)' is not delegated to Microsoft.App/environments. Set HR_MCP_ACA_SUBNET_ID to a dedicated ACA subnet." + } + return [pscustomobject]@{ + VnetName = $vnetName + SubnetName = $subnetObj.name + SubnetId = $subnetObj.id + AddedVnetPrefix = '' + } + } + + if ([string]::IsNullOrWhiteSpace($RequestedVnetName)) { + $vnetsJson = & az network vnet list --resource-group $HubResourceGroup -o json 2>$null + if ($LASTEXITCODE -ne 0) { throw "Could not list VNets in Citadel hub resource group '$HubResourceGroup'." } + $vnets = @($vnetsJson | ConvertFrom-Json) + if ($vnets.Count -ne 1) { + throw "Expected exactly one Citadel hub VNet in resource group '$HubResourceGroup' but found $($vnets.Count). Set HR_MCP_CITADEL_VNET_NAME or HR_MCP_ACA_SUBNET_ID." + } + $RequestedVnetName = $vnets[0].name + } + + $subnetJson = & az network vnet subnet show ` + --resource-group $HubResourceGroup ` + --vnet-name $RequestedVnetName ` + --name $RequestedSubnetName ` + -o json 2>$null + $addedVnetPrefix = '' + if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($subnetJson)) { + Write-Step "Creating dedicated HR MCP subnet '$RequestedSubnetName' in Citadel hub VNet" + $vnetInfo = & az network vnet show --resource-group $HubResourceGroup --name $RequestedVnetName --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' -o json | ConvertFrom-Json + $prefixes = @($vnetInfo.addressPrefixes) + $usedSubnetPrefixes = @($vnetInfo.subnetPrefixes) + if ([string]::IsNullOrWhiteSpace($RequestedSubnetPrefix)) { + $RequestedSubnetPrefix = Find-AvailableSubnetPrefix -VnetPrefixes $prefixes -UsedSubnetPrefixes $usedSubnetPrefixes -PrefixLength $RequestedSubnetPrefixLength + if ([string]::IsNullOrWhiteSpace($RequestedSubnetPrefix)) { + if (-not $AllowExpandVnet) { + throw "Could not find an available /$RequestedSubnetPrefixLength subnet in VNet '$RequestedVnetName'. Set HR_MCP_ACA_SUBNET_PREFIX to an available, non-overlapping prefix, or set HR_MCP_ACA_EXPAND_VNET=true to auto-expand the Citadel hub VNet address space." + } + $expansion = Get-ExpansionSubnetPrefix -VnetPrefixes $prefixes -UsedSubnetPrefixes $usedSubnetPrefixes -PrefixLength $RequestedSubnetPrefixLength -SupernetLength $ExpandSupernetLength + if ($null -eq $expansion) { + throw "No free /$RequestedSubnetPrefixLength subnet inside VNet '$RequestedVnetName' and could not compute a non-overlapping prefix to expand it. Set HR_MCP_ACA_SUBNET_PREFIX explicitly." + } + $addedVnetPrefix = $expansion.VnetPrefix + $RequestedSubnetPrefix = $expansion.SubnetPrefix + } + } elseif (-not (Test-PrefixInsideAnyVnetPrefix -RequestedPrefix $RequestedSubnetPrefix -VnetPrefixes $prefixes)) { + if (-not $AllowExpandVnet) { + throw "Requested HR_MCP_ACA_SUBNET_PREFIX '$RequestedSubnetPrefix' is outside VNet '$RequestedVnetName'. Choose a prefix inside the existing VNet address space, or set HR_MCP_ACA_EXPAND_VNET=true to add it to the VNet." + } + $addedVnetPrefix = Get-ExpansionVnetPrefixForSubnet -SubnetPrefix $RequestedSubnetPrefix -VnetPrefixes $prefixes -UsedSubnetPrefixes $usedSubnetPrefixes -SupernetLength $ExpandSupernetLength + if ([string]::IsNullOrWhiteSpace($addedVnetPrefix)) { + throw "Requested HR_MCP_ACA_SUBNET_PREFIX '$RequestedSubnetPrefix' is outside VNet '$RequestedVnetName' and could not be aligned to a non-overlapping address space block. Choose a different prefix." + } + } + + if (-not [string]::IsNullOrWhiteSpace($addedVnetPrefix)) { + Write-Step "No free /$RequestedSubnetPrefixLength subnet in VNet '$RequestedVnetName'; extending address space with '$addedVnetPrefix' and carving subnet '$RequestedSubnetPrefix'" + & az network vnet update ` + --resource-group $HubResourceGroup ` + --name $RequestedVnetName ` + --add addressSpace.addressPrefixes $addedVnetPrefix ` + --only-show-errors ` + -o none + if ($LASTEXITCODE -ne 0) { + throw "Could not add address prefix '$addedVnetPrefix' to VNet '$RequestedVnetName' (it may overlap a peered network). Set HR_MCP_ACA_SUBNET_PREFIX to a non-overlapping prefix." + } + } + & az network vnet subnet create ` + --resource-group $HubResourceGroup ` + --vnet-name $RequestedVnetName ` + --name $RequestedSubnetName ` + --address-prefixes $RequestedSubnetPrefix ` + --delegations Microsoft.App/environments ` + --only-show-errors ` + -o none + if ($LASTEXITCODE -ne 0) { + throw "Could not create subnet '$RequestedSubnetName'. Set HR_MCP_ACA_SUBNET_PREFIX to an available, non-overlapping prefix." + } + $subnetJson = & az network vnet subnet show ` + --resource-group $HubResourceGroup ` + --vnet-name $RequestedVnetName ` + --name $RequestedSubnetName ` + -o json + } + + $subnetObj = $subnetJson | ConvertFrom-Json + if (-not (Test-AcaDelegation -SubnetObject $subnetObj)) { + throw "Subnet '$RequestedSubnetName' is not delegated to Microsoft.App/environments. Use a dedicated ACA subnet or set HR_MCP_ACA_SUBNET_ID to one." + } + + return [pscustomobject]@{ + VnetName = $RequestedVnetName + SubnetName = $subnetObj.name + SubnetId = $subnetObj.id + AddedVnetPrefix = $addedVnetPrefix + } +} + +Assert-Command az +Assert-Command azd + +if ((Invoke-NativeCommandQuietly { & az account show }) -ne 0) { + throw 'Azure CLI is not logged in. Run az login, then rerun this script.' +} + +Write-Step 'Ensuring Azure CLI extensions' +& az extension add --name application-insights --upgrade --only-show-errors *> $null +& az extension add --name containerapp --upgrade --only-show-errors *> $null + +$azdResourceGroup = Get-AzdValue -Name 'AZURE_RESOURCE_GROUP' +$azdLocation = Get-AzdValue -Name 'AZURE_LOCATION' +$azdEnvName = Get-AzdValue -Name 'AZURE_ENV_NAME' +$azdSubscriptionId = Get-AzdValue -Name 'AZURE_SUBSCRIPTION_ID' + +$location = Get-FirstNonEmpty @($env:AZURE_LOCATION, $azdLocation) +if ([string]::IsNullOrWhiteSpace($location)) { throw 'AZURE_LOCATION is missing. Set AZURE_LOCATION or select an azd environment with AZURE_LOCATION.' } + +$subscriptionId = Get-FirstNonEmpty @($env:AZURE_SUBSCRIPTION_ID, $azdSubscriptionId) +if (-not [string]::IsNullOrWhiteSpace($subscriptionId)) { + & az account set --subscription $subscriptionId +} else { + $subscriptionId = ConvertTo-TrimmedCliOutput (& az account show --query id -o tsv) +} + +$tenantId = ConvertTo-TrimmedCliOutput (& az account show --query tenantId -o tsv) +$envName = Get-FirstNonEmpty @($env:AZURE_ENV_NAME, $azdEnvName, 'hrmcp') +$seedSource = Get-FirstNonEmpty @($env:HR_MCP_NAME_SEED, $envName, $azdResourceGroup) +$nameSeed = ConvertTo-SafeNamePart $seedSource +$compactSeed = ConvertTo-CompactNamePart $seedSource +$subscriptionSuffix = ($subscriptionId -replace '-', '').Substring(0, 8) +$tagEnvName = if ([string]::IsNullOrWhiteSpace($envName)) { 'unknown' } else { $envName } + +if ([string]::IsNullOrWhiteSpace($ResourceGroupName)) { $ResourceGroupName = "rg-$nameSeed-hr-mcp" } +if ($ResourceGroupName.Length -gt 90) { $ResourceGroupName = $ResourceGroupName.Substring(0, 90).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($LogAnalyticsWorkspaceName)) { $LogAnalyticsWorkspaceName = "law-$nameSeed-hr-$subscriptionSuffix" } +if ($LogAnalyticsWorkspaceName.Length -gt 63) { $LogAnalyticsWorkspaceName = $LogAnalyticsWorkspaceName.Substring(0, 63).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($AppInsightsName)) { $AppInsightsName = "appi-$nameSeed-hr-$subscriptionSuffix" } +if ($AppInsightsName.Length -gt 255) { $AppInsightsName = $AppInsightsName.Substring(0, 255).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($AcrName)) { $AcrName = "acr${compactSeed}hr$subscriptionSuffix" } +$AcrName = $AcrName -replace '[^a-zA-Z0-9]', '' +if ($AcrName.Length -gt 50) { $AcrName = $AcrName.Substring(0, 50) } +if ([string]::IsNullOrWhiteSpace($AcaEnvironmentName)) { $AcaEnvironmentName = "cae-$nameSeed-hr-mcp" } +if ($AcaEnvironmentName.Length -gt 32) { $AcaEnvironmentName = $AcaEnvironmentName.Substring(0, 32).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($ContainerAppName)) { $ContainerAppName = "ca-$nameSeed-hr-mcp" } +if ($ContainerAppName.Length -gt 32) { $ContainerAppName = $ContainerAppName.Substring(0, 32).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($PrivateAcaEnvironmentName)) { $PrivateAcaEnvironmentName = "cae-$nameSeed-hr-mcp-int" } +if ($PrivateAcaEnvironmentName.Length -gt 32) { $PrivateAcaEnvironmentName = $PrivateAcaEnvironmentName.Substring(0, 32).TrimEnd('-') } +if ([string]::IsNullOrWhiteSpace($PrivateContainerAppName)) { $PrivateContainerAppName = "ca-$nameSeed-hr-mcp-int" } +if ($PrivateContainerAppName.Length -gt 32) { $PrivateContainerAppName = $PrivateContainerAppName.Substring(0, 32).TrimEnd('-') } + +$apiDisplayName = Get-FirstNonEmpty @($env:HR_MCP_API_APP_DISPLAY_NAME, "$envName-hr-mcp-api") +$publicDisplayName = Get-FirstNonEmpty @($env:HR_MCP_PUBLIC_CLIENT_DISPLAY_NAME, "$envName-hr-mcp-public-client") +$imageRepository = Get-FirstNonEmpty @($env:HR_MCP_IMAGE_REPOSITORY, 'hr-mcp') +$imageTag = Get-FirstNonEmpty @($env:HR_MCP_IMAGE_TAG, ('build-' + (Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmss'))) +$issuer = Get-FirstNonEmpty @($env:HR_MCP_ISSUER, "https://login.microsoftonline.com/$tenantId/v2.0") + +$scriptDir = Split-Path -Parent $PSCommandPath +$workshopDir = Resolve-Path (Join-Path $scriptDir '..' '..') +$dockerfilePath = Join-Path $workshopDir 'mcp-hr/server/Dockerfile' +if (-not (Test-Path $dockerfilePath)) { throw "Dockerfile not found: $dockerfilePath" } + +if ([string]::IsNullOrWhiteSpace($CitadelResourceGroup)) { + $CitadelResourceGroup = Get-FirstNonEmpty @($env:AZURE_RESOURCE_GROUP, $azdResourceGroup) +} +if ([string]::IsNullOrWhiteSpace($CitadelResourceGroup)) { + throw 'Citadel hub resource group is missing. Set HR_MCP_CITADEL_RESOURCE_GROUP or ensure AZURE_RESOURCE_GROUP is available in azd env.' +} +$acaSubnet = Resolve-CitadelAcaSubnet -HubResourceGroup $CitadelResourceGroup -RequestedVnetName $CitadelVnetName -RequestedSubnetName $AcaSubnetName -RequestedSubnetId $AcaSubnetId -RequestedSubnetPrefix $AcaSubnetPrefix -RequestedSubnetPrefixLength $AcaSubnetPrefixLength -AllowExpandVnet $ExpandVnet -ExpandSupernetLength $ExpandPrefixLength + +Write-Step 'Creating HR MCP resource group' +& az group create ` + --name $ResourceGroupName ` + --location $location ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + -o none + +Write-Step 'Creating Log Analytics workspace' +if ((Invoke-NativeCommandQuietly { & az monitor log-analytics workspace show --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName }) -ne 0) { + & az monitor log-analytics workspace create ` + --resource-group $ResourceGroupName ` + --workspace-name $LogAnalyticsWorkspaceName ` + --location $location ` + --sku PerGB2018 ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + -o none +} else { + Write-Host "Log Analytics workspace already exists: $LogAnalyticsWorkspaceName" +} +$logAnalyticsWorkspaceId = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace show --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName --query id -o tsv) + +Write-Step 'Creating workspace-based Application Insights' +if ((Invoke-NativeCommandQuietly { & az monitor app-insights component show --app $AppInsightsName --resource-group $ResourceGroupName }) -ne 0) { + & az monitor app-insights component create ` + --app $AppInsightsName ` + --location $location ` + --resource-group $ResourceGroupName ` + --workspace $logAnalyticsWorkspaceId ` + --kind web ` + --application-type web ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + -o none +} else { + Write-Host "Application Insights already exists: $AppInsightsName" +} +$appInsightsConnectionString = ConvertTo-TrimmedCliOutput (& az monitor app-insights component show --app $AppInsightsName --resource-group $ResourceGroupName --query connectionString -o tsv) + +Write-Step 'Creating or configuring Entra app registrations' +$apiApp = Ensure-ApiAppRegistration -DisplayName $apiDisplayName -RequestedClientId $ApiClientId -ScopeValue $ScopeName -RoleValue $AppRoleValue +$publicApp = Ensure-PublicClientRegistration -DisplayName $publicDisplayName -RequestedClientId $PublicClientId -ApiClientId $apiApp.ClientId -ScopeId $apiApp.ScopeId +Ensure-ApiServicePrincipal -ApiClientId $apiApp.ClientId +Set-ApiPreauthorizedClients -ApiObjectId $apiApp.ObjectId -ScopeId $apiApp.ScopeId -ClientIds @($azureCliClientId, $publicApp.ClientId) + +Write-Step 'Creating Azure Container Registry' +if ((Invoke-NativeCommandQuietly { & az acr show --name $AcrName --resource-group $ResourceGroupName }) -ne 0) { + & az acr create ` + --name $AcrName ` + --resource-group $ResourceGroupName ` + --location $location ` + --sku Basic ` + --admin-enabled false ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + -o none +} else { + Write-Host "Container Registry already exists: $AcrName" +} +$acrId = ConvertTo-TrimmedCliOutput (& az acr show --name $AcrName --resource-group $ResourceGroupName --query id -o tsv) +$acrLoginServer = ConvertTo-TrimmedCliOutput (& az acr show --name $AcrName --resource-group $ResourceGroupName --query loginServer -o tsv) +$imageName = "${acrLoginServer}/${imageRepository}:${imageTag}" + +Write-Step 'Building HR MCP image remotely with ACR' +# The base image bundles Python 3.13 and uv and is hosted on GHCR, which is not +# subject to Docker Hub's anonymous pull-rate limit. +$baseImageSource = 'ghcr.io/astral-sh/uv:python3.13-bookworm-slim' +$baseImageRepo = 'astral-sh/uv-python:3.13-bookworm-slim' +$baseImageRef = $baseImageSource +& az acr import --name $AcrName --source $baseImageSource --image $baseImageRepo --force --only-show-errors -o none 2>$null +if ($LASTEXITCODE -eq 0) { $baseImageRef = "$acrLoginServer/$baseImageRepo" } else { Write-Warning "Could not import $baseImageSource into $AcrName; building from GHCR directly." } +Write-Host 'Note: ACR remote builds run on a shared agent pool. On Basic/Standard SKUs the run can sit in "Queued" (no log output) for several minutes before it starts; this is normal and not a hang. The build itself takes well under a minute.' +# Build from inside the small server folder so ACR only uploads ~250 KB of +# source instead of the entire workshop tree (e.g. a multi-hundred-MB .venv). +# `--file` is resolved relative to the current directory, so cd into the +# context root and pass "." as the build context. +Push-Location (Join-Path $workshopDir 'mcp-hr/server') +try { + & az acr build ` + --registry $AcrName ` + --image "${imageRepository}:${imageTag}" ` + --file 'Dockerfile' ` + --build-arg "BASE_IMAGE=$baseImageRef" ` + '.' ` + --only-show-errors +} +finally { + Pop-Location +} + +Write-Step 'Creating Container Apps environment' +if ((Invoke-NativeCommandQuietly { & az containerapp env show --name $AcaEnvironmentName --resource-group $ResourceGroupName }) -ne 0) { + $workspaceCustomerId = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace show --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName --query customerId -o tsv) + $workspaceKey = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace get-shared-keys --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName --query primarySharedKey -o tsv) + & az containerapp env create ` + --name $AcaEnvironmentName ` + --resource-group $ResourceGroupName ` + --location $location ` + --logs-workspace-id $workspaceCustomerId ` + --logs-workspace-key $workspaceKey ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + --only-show-errors ` + -o none +} else { + Write-Host "Container Apps environment already exists: $AcaEnvironmentName" +} + +Write-Step 'Creating private Container Apps environment in the Citadel hub VNet' +if ((Invoke-NativeCommandQuietly { & az containerapp env show --name $PrivateAcaEnvironmentName --resource-group $ResourceGroupName }) -ne 0) { + $workspaceCustomerId = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace show --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName --query customerId -o tsv) + $workspaceKey = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace get-shared-keys --resource-group $ResourceGroupName --workspace-name $LogAnalyticsWorkspaceName --query primarySharedKey -o tsv) + & az containerapp env create ` + --name $PrivateAcaEnvironmentName ` + --resource-group $ResourceGroupName ` + --location $location ` + --logs-workspace-id $workspaceCustomerId ` + --logs-workspace-key $workspaceKey ` + --infrastructure-subnet-resource-id $acaSubnet.SubnetId ` + --internal-only ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + --only-show-errors ` + -o none +} else { + Write-Host "Private Container Apps environment already exists: $PrivateAcaEnvironmentName" +} + +$privateEnvDefaultDomain = ConvertTo-TrimmedCliOutput (& az containerapp env show --name $PrivateAcaEnvironmentName --resource-group $ResourceGroupName --query properties.defaultDomain -o tsv) +$privateEnvStaticIp = ConvertTo-TrimmedCliOutput (& az containerapp env show --name $PrivateAcaEnvironmentName --resource-group $ResourceGroupName --query properties.staticIp -o tsv) +$citadelVnetId = ConvertTo-TrimmedCliOutput (& az network vnet show --resource-group $CitadelResourceGroup --name $acaSubnet.VnetName --query id -o tsv) +Set-PrivateAcaDns -ZoneResourceGroup $CitadelResourceGroup -VnetId $citadelVnetId -DefaultDomain $privateEnvDefaultDomain -StaticIp $privateEnvStaticIp + +$otelResourceAttributes = "service.namespace=workshop,deployment.environment=$tagEnvName,azure.resource_group=$ResourceGroupName" +$commonEnvVars = @( + 'AUTH_ENABLED=true', + "ENTRA_TENANT_ID=$tenantId", + "ENTRA_AUDIENCE=$($apiApp.Audience)", + "ENTRA_REQUIRED_SCOPE=$ScopeName", + "ENTRA_REQUIRED_ROLE=$AppRoleValue", + "ENTRA_ISSUER=$issuer", + "APPLICATIONINSIGHTS_CONNECTION_STRING=$appInsightsConnectionString", + 'OTEL_SERVICE_NAME=hr-mcp', + "OTEL_RESOURCE_ATTRIBUTES=$otelResourceAttributes", + 'PORT=8080' +) + +Write-Step 'Creating or updating HR MCP Container App' +if ((Invoke-NativeCommandQuietly { & az containerapp show --name $ContainerAppName --resource-group $ResourceGroupName }) -ne 0) { + & az containerapp create ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --environment $AcaEnvironmentName ` + --image 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' ` + --target-port 80 ` + --ingress external ` + --min-replicas 0 ` + --max-replicas 2 ` + --system-assigned ` + --env-vars @commonEnvVars ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp' $securityControlTag ` + --only-show-errors ` + -o none +} else { + Write-Host "Container App already exists: $ContainerAppName" + & az containerapp identity assign ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --system-assigned ` + --only-show-errors ` + -o none *> $null +} + +$containerAppPrincipalId = ConvertTo-TrimmedCliOutput (& az containerapp show --name $ContainerAppName --resource-group $ResourceGroupName --query identity.principalId -o tsv) +if ([string]::IsNullOrWhiteSpace($containerAppPrincipalId)) { throw 'Could not resolve Container App managed identity principalId.' } +Add-RoleAssignmentIfMissing -PrincipalId $containerAppPrincipalId -PrincipalType ServicePrincipal -RoleId $acrPullRoleId -RoleName 'AcrPull (HR MCP Container App)' -Scope $acrId +Wait-RoleAssignment -PrincipalId $containerAppPrincipalId -RoleId $acrPullRoleId -Scope $acrId -RoleName 'AcrPull' + +& az containerapp registry set ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --server $acrLoginServer ` + --identity system ` + --only-show-errors ` + -o none + +& az containerapp ingress update ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --target-port 8080 ` + --only-show-errors ` + -o none + +& az containerapp update ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --image $imageName ` + --set-env-vars @commonEnvVars ` + --min-replicas 0 ` + --max-replicas 2 ` + --only-show-errors ` + -o none + +Write-Step 'Creating or updating private HR MCP Container App for APIM backend traffic' +if ((Invoke-NativeCommandQuietly { & az containerapp show --name $PrivateContainerAppName --resource-group $ResourceGroupName }) -ne 0) { + & az containerapp create ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --environment $PrivateAcaEnvironmentName ` + --image 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' ` + --target-port 80 ` + --ingress external ` + --min-replicas 0 ` + --max-replicas 2 ` + --system-assigned ` + --env-vars @commonEnvVars ` + --tags "azd-env-name=$tagEnvName" 'workload=hr-mcp-private' $securityControlTag ` + --only-show-errors ` + -o none +} else { + Write-Host "Private Container App already exists: $PrivateContainerAppName" + & az containerapp identity assign ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --system-assigned ` + --only-show-errors ` + -o none *> $null +} + +$privateContainerAppPrincipalId = ConvertTo-TrimmedCliOutput (& az containerapp show --name $PrivateContainerAppName --resource-group $ResourceGroupName --query identity.principalId -o tsv) +if ([string]::IsNullOrWhiteSpace($privateContainerAppPrincipalId)) { throw 'Could not resolve private Container App managed identity principalId.' } +Add-RoleAssignmentIfMissing -PrincipalId $privateContainerAppPrincipalId -PrincipalType ServicePrincipal -RoleId $acrPullRoleId -RoleName 'AcrPull (Private HR MCP Container App)' -Scope $acrId +Wait-RoleAssignment -PrincipalId $privateContainerAppPrincipalId -RoleId $acrPullRoleId -Scope $acrId -RoleName 'AcrPull (private app)' + +& az containerapp registry set ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --server $acrLoginServer ` + --identity system ` + --only-show-errors ` + -o none + +& az containerapp ingress update ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --target-port 8080 ` + --type external ` + --transport http ` + --only-show-errors ` + -o none + +& az containerapp update ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --image $imageName ` + --set-env-vars @commonEnvVars ` + --min-replicas 0 ` + --max-replicas 2 ` + --only-show-errors ` + -o none + +$containerAppUpdateHelp = (& az containerapp update -h 2>$null) -join "`n" +if ($containerAppUpdateHelp -match '--startup-probe') { + Write-Step 'Configuring health probes' + & az containerapp update ` + --name $ContainerAppName ` + --resource-group $ResourceGroupName ` + --startup-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=10,periodSeconds=10,failureThreshold=12' ` + --liveness-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=30,periodSeconds=30,failureThreshold=3' ` + --only-show-errors ` + -o none + if ($LASTEXITCODE -ne 0) { + Write-Warning 'Probe configuration failed. The app is deployed; configure /health probes from the Container App portal if your CLI version lacks compatible probe syntax.' + } + & az containerapp update ` + --name $PrivateContainerAppName ` + --resource-group $ResourceGroupName ` + --startup-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=10,periodSeconds=10,failureThreshold=12' ` + --liveness-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=30,periodSeconds=30,failureThreshold=3' ` + --only-show-errors ` + -o none + if ($LASTEXITCODE -ne 0) { + Write-Warning 'Private app probe configuration failed. The app is deployed; configure /health probes from the Container App portal if your CLI version lacks compatible probe syntax.' + } +} else { + Write-Warning 'Azure CLI containerapp extension does not expose probe flags. The app is configured correctly; add /health startup/liveness probes later if desired.' +} + +$containerAppFqdn = ConvertTo-TrimmedCliOutput (& az containerapp show --name $ContainerAppName --resource-group $ResourceGroupName --query properties.configuration.ingress.fqdn -o tsv) +$directUrl = "https://$containerAppFqdn" +$directMcpUrl = "$directUrl/mcp" +$privateContainerAppFqdn = ConvertTo-TrimmedCliOutput (& az containerapp show --name $PrivateContainerAppName --resource-group $ResourceGroupName --query properties.configuration.ingress.fqdn -o tsv) +$privateBackendUrl = "https://$privateContainerAppFqdn" +$privateMcpUrl = "$privateBackendUrl/mcp" +$privateBackendIpUrl = "http://$privateEnvStaticIp" + +Write-Step 'Saving HR MCP outputs to azd environment' +Save-AzdValue HR_MCP_RESOURCE_GROUP $ResourceGroupName +Save-AzdValue HR_MCP_ACR_NAME $AcrName +Save-AzdValue HR_MCP_ACR_LOGIN_SERVER $acrLoginServer +Save-AzdValue HR_MCP_CONTAINER_APP_NAME $ContainerAppName +Save-AzdValue HR_MCP_PRIVATE_ACA_ENVIRONMENT_NAME $PrivateAcaEnvironmentName +Save-AzdValue HR_MCP_PRIVATE_CONTAINER_APP_NAME $PrivateContainerAppName +Save-AzdValue HR_MCP_DIRECT_URL $directUrl +Save-AzdValue HR_MCP_DIRECT_MCP_URL $directMcpUrl +Save-AzdValue HR_MCP_PRIVATE_BACKEND_URL $privateBackendUrl +Save-AzdValue HR_MCP_PRIVATE_BACKEND_IP_URL $privateBackendIpUrl +Save-AzdValue HR_MCP_APIM_BACKEND_HOST_HEADER $privateContainerAppFqdn +Save-AzdValue HR_MCP_PRIVATE_MCP_URL $privateMcpUrl +Save-AzdValue HR_MCP_ACA_INTERNAL_FQDN $privateContainerAppFqdn +Save-AzdValue HR_MCP_PRIVATE_DNS_ZONE $privateEnvDefaultDomain +Save-AzdValue HR_MCP_PRIVATE_DNS_RESOURCE_GROUP $CitadelResourceGroup +Save-AzdValue HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND 'true' +Save-AzdValue HR_MCP_CITADEL_RESOURCE_GROUP $CitadelResourceGroup +Save-AzdValue HR_MCP_CITADEL_VNET_NAME $acaSubnet.VnetName +Save-AzdValue HR_MCP_ACA_SUBNET_NAME $acaSubnet.SubnetName +Save-AzdValue HR_MCP_ACA_SUBNET_ID $acaSubnet.SubnetId +Save-AzdValue HR_MCP_ACA_ADDED_VNET_PREFIX $acaSubnet.AddedVnetPrefix +Save-AzdValue HR_MCP_APP_INSIGHTS_NAME $AppInsightsName +Save-AzdValue HR_MCP_TENANT_ID $tenantId +Save-AzdValue HR_MCP_API_CLIENT_ID $apiApp.ClientId +Save-AzdValue HR_MCP_PUBLIC_CLIENT_ID $publicApp.ClientId +Save-AzdValue HR_MCP_AUDIENCE $apiApp.Audience +Save-AzdValue HR_MCP_SCOPE $apiApp.Scope +Save-AzdValue HR_MCP_APP_ROLE_ID $apiApp.AppRoleId +Save-AzdValue HR_MCP_APP_ROLE_VALUE $AppRoleValue +Save-AzdValue HR_MCP_API_OBJECT_ID $apiApp.ObjectId +Save-AzdValue HR_MCP_REQUIRED_SCOPE_CLAIM $ScopeName + +Write-Host "`nHR MCP ACA deployment is ready." +Write-Host "Resource group: $ResourceGroupName" +Write-Host "Container App: $ContainerAppName" +Write-Host "Direct URL: $directUrl" +Write-Host "MCP endpoint: $directMcpUrl" +Write-Host "Private Container App: $PrivateContainerAppName" +Write-Host "Private backend URL for APIM: $privateBackendUrl" +Write-Host "Private backend IP URL fallback: $privateBackendIpUrl" +Write-Host "Private ACA DNS zone: $privateEnvDefaultDomain ($CitadelResourceGroup)" +Write-Host "Citadel hub VNet/subnet: $($acaSubnet.VnetName)/$($acaSubnet.SubnetName)" +Write-Host "API app client id: $($apiApp.ClientId)" +Write-Host "Public client id: $($publicApp.ClientId)" +Write-Host "Audience: $($apiApp.Audience)" +Write-Host "Scope: $($apiApp.Scope)" +Write-Host "`nToken acquisition examples (these print tokens; use only in your own shell):" +Write-Host "az login --tenant `"$tenantId`" --use-device-code" +Write-Host "az account get-access-token --tenant `"$tenantId`" --scope `"$($apiApp.Scope)`" --query accessToken -o tsv" +Write-Host "`nSmoke test (acquires a token with Azure CLI; does not print it):" +Write-Host 'cd workshop; uv run python mcp-hr/scripts/test-hr-mcp-direct.py' +Write-Host "`nMCP config snippet (replace ; never store real tokens in source):" +Write-Host @" +{ + "mcpServers": { + "hr-mcp-direct": { + "type": "http", + "url": "$directMcpUrl", + "headers": { + "Authorization": "Bearer " + } + } + } +} +"@ diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.sh new file mode 100755 index 000000000..7e5977df8 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/deploy-hr-mcp.sh @@ -0,0 +1,1105 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Deploys the workshop HR MCP server to Azure Container Apps using only Azure CLI, +# Azure Developer CLI environment values, and ACR remote builds. Local Docker is not required. + +export PYTHONWARNINGS="${PYTHONWARNINGS:+$PYTHONWARNINGS,}ignore" +export AZURE_CORE_ONLY_SHOW_ERRORS="${AZURE_CORE_ONLY_SHOW_ERRORS:-True}" + +ACR_PULL_ROLE_ID="7f951dda-4ed3-4680-a7ca-43fe172d538d" +AZURE_CLI_CLIENT_ID="04b07795-8ddb-461a-bbee-02f9e1bf7b46" +SECURITY_CONTROL_TAG="SecurityControl=Ignore" +DEFAULT_SCOPE_VALUE="Mcp.Access" +DEFAULT_APP_ROLE_VALUE="Mcp.Invoke" + +log() { + printf '\n==> %s\n' "$1" +} + +warn() { + printf 'Warning: %s\n' "$1" >&2 +} + +fail() { + printf 'Error: %s\n' "$1" >&2 + exit 1 +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + fail "Required command not found: $1" + fi +} + +azd_get_optional() { + local value + if value="$(azd env get-value "$1" 2>/dev/null)"; then + printf '%s' "$value" + fi +} + +first_non_empty() { + local value + for value in "$@"; do + if [[ -n "${value:-}" ]]; then + printf '%s' "$value" + return 0 + fi + done + return 0 +} + +safe_name_part() { + local value="$1" + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g; s/--*/-/g; s/^-*//; s/-*$//')" + if [[ -z "$value" ]]; then + value="hr-mcp" + fi + printf '%s' "$value" +} + +compact_name_part() { + local value="$1" + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]//g')" + if [[ -z "$value" ]]; then + value="hrmcp" + fi + printf '%s' "$value" +} + +trim_trailing_hyphen() { + sed 's/-*$//' +} + +assign_role_if_missing() { + local principal_id="$1" + local principal_type="$2" + local role_id="$3" + local role_name="$4" + local scope="$5" + + local assignment_id + assignment_id="$(az role assignment list \ + --scope "$scope" \ + --fill-principal-name false \ + --fill-role-definition-name false \ + --query "[?principalId=='${principal_id}' && contains(roleDefinitionId, '${role_id}')].id | [0]" \ + -o tsv 2>/dev/null || true)" + + if [[ -n "$assignment_id" ]]; then + printf 'Role assignment already exists: %s\n' "$role_name" + return + fi + + az role assignment create \ + --assignee-object-id "$principal_id" \ + --assignee-principal-type "$principal_type" \ + --role "$role_id" \ + --scope "$scope" \ + --only-show-errors \ + -o none + printf 'Assigned role: %s\n' "$role_name" +} + +wait_for_role_assignment() { + local principal_id="$1" + local role_id="$2" + local scope="$3" + local role_name="$4" + local max_retries="${5:-5}" + local sleep_seconds="${6:-30}" + local role + + for i in $(seq 1 "$max_retries"); do + role="$(az role assignment list \ + --scope "$scope" \ + --fill-principal-name false \ + --fill-role-definition-name false \ + --query "[?principalId=='${principal_id}' && contains(roleDefinitionId, '${role_id}')].id | [0]" \ + -o tsv 2>/dev/null || true)" + if [[ -n "$role" ]]; then + printf '%s role assignment is visible.\n' "$role_name" + return 0 + fi + printf '%s role assignment not visible yet (attempt %s/%s); waiting %ss...\n' "$role_name" "$i" "$max_retries" "$sleep_seconds" + sleep "$sleep_seconds" + done + + warn "$role_name role assignment did not become visible after waiting. Continuing because role assignment creation succeeded; if image pull fails, wait a few minutes and rerun the script." + return 0 +} + +ensure_extension() { + local name="$1" + log "Ensuring Azure CLI extension: $name" + az extension add --name "$name" --upgrade --only-show-errors >/dev/null +} + +# Import the container base image into the target ACR so remote builds do not +# pull from a public registry on every build. The image bundles Python 3.13 and +# uv and is hosted on GHCR, which is not subject to Docker Hub's anonymous +# pull-rate limit. Sets HR_MCP_BASE_IMAGE_REF; falls back to the public ref. +ensure_acr_base_images() { + local acr="$1" + local acr_login="$2" + local source_ref="ghcr.io/astral-sh/uv:python3.13-bookworm-slim" + local target_repo="astral-sh/uv-python:3.13-bookworm-slim" + HR_MCP_BASE_IMAGE_REF="$source_ref" + [[ -n "$acr_login" ]] || return 0 + + if az acr import --name "$acr" --source "$source_ref" --image "$target_repo" --force --only-show-errors -o none 2>/dev/null; then + HR_MCP_BASE_IMAGE_REF="${acr_login}/${target_repo}" + else + warn "Could not import ${source_ref} into ${acr}; building from GHCR directly." + fi +} + +ensure_private_dns_a_record() { + local zone_rg="$1" + local zone_name="$2" + local record_name="$3" + local ip="$4" + local existing + + if ! az network private-dns record-set a show \ + --resource-group "$zone_rg" \ + --zone-name "$zone_name" \ + --name "$record_name" >/dev/null 2>&1; then + az network private-dns record-set a create \ + --resource-group "$zone_rg" \ + --zone-name "$zone_name" \ + --name "$record_name" \ + --ttl 30 \ + --only-show-errors \ + -o none >/dev/null + fi + + existing="$(az network private-dns record-set a show \ + --resource-group "$zone_rg" \ + --zone-name "$zone_name" \ + --name "$record_name" \ + --query "aRecords[?ipv4Address=='${ip}'].ipv4Address | [0]" \ + -o tsv 2>/dev/null || true)" + if [[ -z "$existing" ]]; then + az network private-dns record-set a add-record \ + --resource-group "$zone_rg" \ + --zone-name "$zone_name" \ + --record-set-name "$record_name" \ + --ipv4-address "$ip" \ + --only-show-errors \ + -o none + fi +} + +ensure_private_aca_dns() { + local zone_rg="$1" + local vnet_id="$2" + local default_domain="$3" + local static_ip="$4" + local link_name + + [[ -n "$default_domain" ]] || fail 'Private Container Apps environment defaultDomain is missing; cannot configure private DNS.' + [[ -n "$static_ip" ]] || fail 'Private Container Apps environment staticIp is missing; cannot configure private DNS.' + + log "Configuring private DNS for internal ACA domain: $default_domain" + if ! az network private-dns zone show \ + --resource-group "$zone_rg" \ + --name "$default_domain" >/dev/null 2>&1; then + az network private-dns zone create \ + --resource-group "$zone_rg" \ + --name "$default_domain" \ + --only-show-errors \ + -o none >/dev/null + fi + + link_name="lnk-hr-mcp-aca" + if ! az network private-dns link vnet show \ + --resource-group "$zone_rg" \ + --zone-name "$default_domain" \ + --name "$link_name" >/dev/null 2>&1; then + az network private-dns link vnet create \ + --resource-group "$zone_rg" \ + --zone-name "$default_domain" \ + --name "$link_name" \ + --virtual-network "$vnet_id" \ + --registration-enabled false \ + --only-show-errors \ + -o none + fi + + ensure_private_dns_a_record "$zone_rg" "$default_domain" "*" "$static_ip" + ensure_private_dns_a_record "$zone_rg" "$default_domain" "*.internal" "$static_ip" +} + +prefix_inside_any_vnet_prefix() { + local requested_prefix="$1" + shift + python3 - "$requested_prefix" "$@" <<'PY' +import ipaddress +import sys + +requested = ipaddress.ip_network(sys.argv[1], strict=False) +for prefix in sys.argv[2:]: + if requested.subnet_of(ipaddress.ip_network(prefix, strict=False)): + raise SystemExit(0) +raise SystemExit(1) +PY +} + +resolve_available_subnet_prefix() { + local hub_rg="$1" + local vnet_name="$2" + local prefix_length="$3" + + python3 - "$prefix_length" \ + "$(az network vnet show \ + --resource-group "$hub_rg" \ + --name "$vnet_name" \ + --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' \ + -o json)" <<'PY' +import ipaddress +import json +import sys + +prefix_length = int(sys.argv[1]) +data = json.loads(sys.argv[2]) +vnet_prefixes = [ipaddress.ip_network(p, strict=False) for p in data.get("addressPrefixes", [])] +used = [ipaddress.ip_network(p, strict=False) for p in data.get("subnetPrefixes", []) if p] + +for parent in sorted(vnet_prefixes, key=lambda n: (n.version, int(n.network_address), n.prefixlen)): + if parent.version != 4 or parent.prefixlen > prefix_length: + continue + for candidate in parent.subnets(new_prefix=prefix_length): + if all(not candidate.overlaps(existing) for existing in used): + print(candidate) + raise SystemExit(0) + +raise SystemExit(1) +PY +} + +# Computes how to expand the VNet when no free subnet exists inside the current +# address space. It walks forward from the end of the highest existing VNet prefix +# and returns TWO space-separated values: +# 1. a new VNet address prefix to ADD (sized to match the existing VNet block, so +# addressing stays consistent, e.g. extend 10.170.0.0/24 -> add 10.170.1.0/24) +# 2. the subnet prefix to carve from it (e.g. 10.170.1.0/26) +# Both are private, aligned, and non-overlapping with existing prefixes/subnets. +# The supernet block size defaults to the largest existing VNet block and can be +# overridden with HR_MCP_ACA_EXPAND_PREFIX_LENGTH. +compute_expansion_subnet_prefix() { + local hub_rg="$1" + local vnet_name="$2" + local prefix_length="$3" + local supernet_length="${4:-}" + + python3 - "$prefix_length" "$supernet_length" \ + "$(az network vnet show \ + --resource-group "$hub_rg" \ + --name "$vnet_name" \ + --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' \ + -o json)" <<'PY' +import ipaddress +import json +import sys + +prefix_length = int(sys.argv[1]) +supernet_arg = sys.argv[2].strip() +data = json.loads(sys.argv[3]) +vnet_prefixes = [ipaddress.ip_network(p, strict=False) for p in data.get("addressPrefixes", []) if p] +subnet_prefixes = [ipaddress.ip_network(p, strict=False) for p in data.get("subnetPrefixes", []) if p] + +ipv4_vnet = [n for n in vnet_prefixes if n.version == 4] +if not ipv4_vnet: + raise SystemExit(1) + +# Size the new VNet address prefix to match the existing block style so addressing +# stays consistent (e.g. a /24 VNet is extended with another /24). Never larger +# (numerically smaller) than the subnet prefix length we need to carve. +if supernet_arg: + supernet_length = int(supernet_arg) +else: + supernet_length = min(n.prefixlen for n in ipv4_vnet) +supernet_length = min(supernet_length, prefix_length) + +block = 1 << (32 - supernet_length) +max_end = max(int(n.broadcast_address) for n in ipv4_vnet) +# Align the first candidate up to the next / boundary past existing space. +candidate_int = ((max_end + 1 + block - 1) // block) * block + +# Bound the search so we never loop unexpectedly; 4096 blocks is far more than needed. +for _ in range(4096): + if candidate_int + block - 1 > 0xFFFFFFFF: + break + vnet_block = ipaddress.ip_network((candidate_int, supernet_length)) + if vnet_block.is_private and \ + all(not vnet_block.overlaps(n) for n in vnet_prefixes) and \ + all(not vnet_block.overlaps(s) for s in subnet_prefixes): + subnet = next(vnet_block.subnets(new_prefix=prefix_length)) + print(vnet_block, subnet) + raise SystemExit(0) + candidate_int += block + +raise SystemExit(1) +PY +} + +# Given an explicit subnet prefix that falls OUTSIDE the current VNet address space, +# returns the aligned address-space block (sized to the existing VNet block style) that +# contains it and can be added to the VNet, e.g. subnet 10.170.1.0/26 -> add 10.170.1.0/24. +# The block must be private and not overlap any existing VNet prefix or subnet. +compute_expansion_vnet_prefix_for_subnet() { + local hub_rg="$1" + local vnet_name="$2" + local subnet_prefix="$3" + local supernet_length="${4:-}" + + python3 - "$subnet_prefix" "$supernet_length" \ + "$(az network vnet show \ + --resource-group "$hub_rg" \ + --name "$vnet_name" \ + --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' \ + -o json)" <<'PY' +import ipaddress +import json +import sys + +subnet = ipaddress.ip_network(sys.argv[1], strict=False) +supernet_arg = sys.argv[2].strip() +data = json.loads(sys.argv[3]) +vnet_prefixes = [ipaddress.ip_network(p, strict=False) for p in data.get("addressPrefixes", []) if p] +subnet_prefixes = [ipaddress.ip_network(p, strict=False) for p in data.get("subnetPrefixes", []) if p] + +if subnet.version != 4: + raise SystemExit(1) + +ipv4_vnet = [n for n in vnet_prefixes if n.version == 4] +if supernet_arg: + supernet_length = int(supernet_arg) +elif ipv4_vnet: + supernet_length = min(n.prefixlen for n in ipv4_vnet) +else: + supernet_length = subnet.prefixlen +supernet_length = min(supernet_length, subnet.prefixlen) + +block = subnet.supernet(new_prefix=supernet_length) + +if not block.is_private: + raise SystemExit(1) +if any(block.overlaps(n) for n in vnet_prefixes): + raise SystemExit(1) +if any(block.overlaps(s) for s in subnet_prefixes): + raise SystemExit(1) + +print(block) +raise SystemExit(0) +PY +} + +resolve_citadel_aca_subnet() { + local hub_rg="$1" + local requested_vnet="$2" + local requested_subnet="$3" + local requested_subnet_id="$4" + local requested_prefix="$5" + local prefix_length="$6" + local vnet_count delegation subnet_id + local needs_expansion=0 + local vnet_prefixes=() + local expansion_vnet_prefix="" + + if [[ -n "$requested_subnet_id" ]]; then + HR_MCP_CITADEL_VNET_NAME_VALUE="$(az network vnet list --resource-group "$hub_rg" --query "[?contains('${requested_subnet_id}', id)].name | [0]" -o tsv 2>/dev/null || true)" + HR_MCP_ACA_SUBNET_NAME_VALUE="$(basename "$requested_subnet_id")" + HR_MCP_ACA_SUBNET_ID_VALUE="$requested_subnet_id" + delegation="$(az network vnet subnet show \ + --ids "$HR_MCP_ACA_SUBNET_ID_VALUE" \ + --query "delegations[?serviceName=='Microsoft.App/environments'].serviceName | [0]" \ + -o tsv 2>/dev/null || true)" + if [[ -z "$delegation" ]]; then + fail "Subnet '$HR_MCP_ACA_SUBNET_NAME_VALUE' is not delegated to Microsoft.App/environments. Set HR_MCP_ACA_SUBNET_ID to a dedicated ACA subnet." + fi + return + fi + + if [[ -n "$requested_vnet" ]]; then + HR_MCP_CITADEL_VNET_NAME_VALUE="$requested_vnet" + else + vnet_count="$(az network vnet list --resource-group "$hub_rg" --query 'length(@)' -o tsv 2>/dev/null || printf '0')" + if [[ "$vnet_count" != "1" ]]; then + fail "Expected exactly one Citadel hub VNet in resource group '$hub_rg' but found $vnet_count. Set HR_MCP_CITADEL_VNET_NAME or HR_MCP_ACA_SUBNET_ID." + fi + HR_MCP_CITADEL_VNET_NAME_VALUE="$(az network vnet list --resource-group "$hub_rg" --query '[0].name' -o tsv)" + fi + + HR_MCP_ACA_SUBNET_NAME_VALUE="${requested_subnet:-snet-mcp}" + subnet_id="$(az network vnet subnet show \ + --resource-group "$hub_rg" \ + --vnet-name "$HR_MCP_CITADEL_VNET_NAME_VALUE" \ + --name "$HR_MCP_ACA_SUBNET_NAME_VALUE" \ + --query id \ + -o tsv 2>/dev/null || true)" + + if [[ -z "$subnet_id" ]]; then + vnet_prefixes=() + while IFS= read -r prefix; do + [[ -n "$prefix" ]] && vnet_prefixes+=("$prefix") + done < <(az network vnet show \ + --resource-group "$hub_rg" \ + --name "$HR_MCP_CITADEL_VNET_NAME_VALUE" \ + --query 'addressSpace.addressPrefixes[]' \ + -o tsv) + + if [[ -z "$requested_prefix" ]]; then + requested_prefix="$(resolve_available_subnet_prefix "$hub_rg" "$HR_MCP_CITADEL_VNET_NAME_VALUE" "$prefix_length" 2>/dev/null || true)" + if [[ -z "$requested_prefix" ]]; then + if [[ "${HR_MCP_ACA_EXPAND_VNET:-true}" != "true" ]]; then + fail "Could not find an available /${prefix_length} subnet in VNet '$HR_MCP_CITADEL_VNET_NAME_VALUE'. Set HR_MCP_ACA_SUBNET_PREFIX to an available, non-overlapping prefix, or set HR_MCP_ACA_EXPAND_VNET=true to auto-expand the Citadel hub VNet address space." + fi + read -r expansion_vnet_prefix requested_prefix < <(compute_expansion_subnet_prefix "$hub_rg" "$HR_MCP_CITADEL_VNET_NAME_VALUE" "$prefix_length" "${HR_MCP_ACA_EXPAND_PREFIX_LENGTH:-}" 2>/dev/null || true) + [[ -n "$requested_prefix" && -n "$expansion_vnet_prefix" ]] || fail "No free /${prefix_length} subnet inside VNet '$HR_MCP_CITADEL_VNET_NAME_VALUE' and could not compute a non-overlapping prefix to expand it. Set HR_MCP_ACA_SUBNET_PREFIX explicitly." + needs_expansion=1 + fi + elif ! prefix_inside_any_vnet_prefix "$requested_prefix" "${vnet_prefixes[@]}"; then + if [[ "${HR_MCP_ACA_EXPAND_VNET:-true}" != "true" ]]; then + fail "Requested HR_MCP_ACA_SUBNET_PREFIX '$requested_prefix' is outside VNet '$HR_MCP_CITADEL_VNET_NAME_VALUE'. Choose a prefix inside the existing VNet address space, or set HR_MCP_ACA_EXPAND_VNET=true to add it to the VNet." + fi + expansion_vnet_prefix="$(compute_expansion_vnet_prefix_for_subnet "$hub_rg" "$HR_MCP_CITADEL_VNET_NAME_VALUE" "$requested_prefix" "${HR_MCP_ACA_EXPAND_PREFIX_LENGTH:-}" 2>/dev/null || true)" + [[ -n "$expansion_vnet_prefix" ]] || fail "Requested HR_MCP_ACA_SUBNET_PREFIX '$requested_prefix' is outside VNet '$HR_MCP_CITADEL_VNET_NAME_VALUE' and could not be aligned to a non-overlapping address space block. Choose a different prefix." + needs_expansion=1 + fi + + if [[ "$needs_expansion" == "1" ]]; then + log "No free /${prefix_length} subnet in VNet '${HR_MCP_CITADEL_VNET_NAME_VALUE}'; extending address space with '${expansion_vnet_prefix}' and carving subnet '${requested_prefix}'" + az network vnet update \ + --resource-group "$hub_rg" \ + --name "$HR_MCP_CITADEL_VNET_NAME_VALUE" \ + --add addressSpace.addressPrefixes "$expansion_vnet_prefix" \ + --only-show-errors \ + -o none || fail "Could not add address prefix '${expansion_vnet_prefix}' to VNet '${HR_MCP_CITADEL_VNET_NAME_VALUE}' (it may overlap a peered network). Set HR_MCP_ACA_SUBNET_PREFIX to a non-overlapping prefix." + vnet_prefixes+=("$expansion_vnet_prefix") + # Record the prefix we added so teardown can remove it once the subnet is gone. + HR_MCP_ACA_ADDED_VNET_PREFIX_VALUE="$expansion_vnet_prefix" + fi + + log "Creating dedicated HR MCP subnet '${HR_MCP_ACA_SUBNET_NAME_VALUE}' in Citadel hub VNet" + prefix_inside_any_vnet_prefix "$requested_prefix" "${vnet_prefixes[@]}" || fail "Resolved subnet prefix '$requested_prefix' is outside VNet '$HR_MCP_CITADEL_VNET_NAME_VALUE'. Set HR_MCP_ACA_SUBNET_PREFIX to an available prefix inside the Citadel hub VNet address space." + + az network vnet subnet create \ + --resource-group "$hub_rg" \ + --vnet-name "$HR_MCP_CITADEL_VNET_NAME_VALUE" \ + --name "$HR_MCP_ACA_SUBNET_NAME_VALUE" \ + --address-prefixes "$requested_prefix" \ + --delegations Microsoft.App/environments \ + --only-show-errors \ + -o none || fail "Could not create subnet '$HR_MCP_ACA_SUBNET_NAME_VALUE'. Set HR_MCP_ACA_SUBNET_PREFIX to an available, non-overlapping prefix." + subnet_id="$(az network vnet subnet show \ + --resource-group "$hub_rg" \ + --vnet-name "$HR_MCP_CITADEL_VNET_NAME_VALUE" \ + --name "$HR_MCP_ACA_SUBNET_NAME_VALUE" \ + --query id \ + -o tsv)" + fi + + HR_MCP_ACA_SUBNET_ID_VALUE="$subnet_id" + + delegation="$(az network vnet subnet show \ + --ids "$HR_MCP_ACA_SUBNET_ID_VALUE" \ + --query "delegations[?serviceName=='Microsoft.App/environments'].serviceName | [0]" \ + -o tsv 2>/dev/null || true)" + if [[ -z "$delegation" ]]; then + fail "Subnet '$HR_MCP_ACA_SUBNET_NAME_VALUE' is not delegated to Microsoft.App/environments. Use a dedicated ACA subnet or set HR_MCP_ACA_SUBNET_ID to one." + fi +} + +find_app_object_id_by_display_name() { + local display_name="$1" + az ad app list \ + --display-name "$display_name" \ + --query 'length(@) == `1` && [0].id || `MULTIPLE_OR_NONE`' \ + -o tsv 2>/dev/null || true +} + +get_app_client_id_from_object_id() { + local object_id="$1" + az ad app show --id "$object_id" --query appId -o tsv +} + +ensure_single_app_by_display_name() { + local display_name="$1" + local object_id + object_id="$(find_app_object_id_by_display_name "$display_name")" + + if [[ "$object_id" == "MULTIPLE_OR_NONE" ]]; then + local count + count="$(az ad app list --display-name "$display_name" --query 'length(@)' -o tsv 2>/dev/null || printf '0')" + if [[ "$count" == "0" ]]; then + object_id="$(az ad app create \ + --display-name "$display_name" \ + --sign-in-audience AzureADMyOrg \ + --query id \ + -o tsv)" || fail "Could not create app registration '$display_name'. You may need Application Developer permissions." + printf '%s' "$object_id" + return + fi + fail "Multiple app registrations named '$display_name' exist. Set HR_MCP_API_CLIENT_ID or HR_MCP_PUBLIC_CLIENT_ID to disambiguate." + fi + + printf '%s' "$object_id" +} + +patch_graph_application() { + local object_id="$1" + local body="$2" + az rest \ + --method PATCH \ + --uri "https://graph.microsoft.com/v1.0/applications/${object_id}" \ + --headers 'Content-Type=application/json' \ + --body "$body" \ + --only-show-errors \ + -o none || fail "Could not update Entra app registration ${object_id}. Ensure Microsoft Graph Application.ReadWrite.All or Application Developer permissions are available." +} + +validate_scope_name() { + local value="$1" + if [[ ! "$value" =~ ^[A-Za-z0-9._-]+$ ]]; then + fail "HR_MCP_SCOPE_NAME must contain only letters, numbers, '.', '_' or '-'." + fi +} + +ensure_api_app_registration() { + local display_name="$1" + local requested_client_id="$2" + local scope_value="$3" + local role_value="$4" + local object_id client_id app_id_uri existing_scope_id scope_id body + + if [[ -n "$requested_client_id" ]]; then + object_id="$(az ad app show --id "$requested_client_id" --query id -o tsv 2>/dev/null)" || fail "HR_MCP_API_CLIENT_ID '$requested_client_id' was not found." + else + object_id="$(ensure_single_app_by_display_name "$display_name")" + fi + + client_id="$(get_app_client_id_from_object_id "$object_id")" + app_id_uri="api://${client_id}" + + existing_scope_id="$(az ad app show --id "$client_id" --query "api.oauth2PermissionScopes[?value=='${scope_value}'].id | [0]" -o tsv 2>/dev/null || true)" + if [[ -n "$existing_scope_id" ]]; then + scope_id="$existing_scope_id" + else + scope_id="$(uuidgen | tr '[:upper:]' '[:lower:]')" + fi + + # Application role for managed-identity callers (the hosted agent). Entra + # forbids an app role and a delegated scope from sharing the same value, so + # the role uses a distinct value (default Mcp.Invoke) that both APIM and the + # MCP server accept in the 'roles' claim. + local existing_role_id role_id + existing_role_id="$(az ad app show --id "$client_id" --query "appRoles[?value=='${role_value}'].id | [0]" -o tsv 2>/dev/null || true)" + if [[ -n "$existing_role_id" ]]; then + role_id="$existing_role_id" + else + role_id="$(uuidgen | tr '[:upper:]' '[:lower:]')" + fi + + body="$(cat </dev/null)" || fail "HR_MCP_PUBLIC_CLIENT_ID '$requested_client_id' was not found." + else + object_id="$(ensure_single_app_by_display_name "$display_name")" + fi + + client_id="$(get_app_client_id_from_object_id "$object_id")" + body="$(cat </dev/null 2>&1; then + printf 'Service principal already exists for HR MCP API app.\n' + return + fi + az ad sp create --id "$api_client_id" --only-show-errors -o none \ + || fail "Could not create service principal for HR MCP API app '$api_client_id'. Ensure you have permission to create Enterprise Applications/service principals." + printf 'Created service principal for HR MCP API app.\n' +} + +preauthorize_api_clients() { + local api_object_id="$1" + local scope_id="$2" + shift 2 + local app_json body + app_json="$(az ad app show --id "$api_object_id" -o json)" + body="$(python3 -c ' +import json +import sys + +scope_id = sys.argv[1] +app = json.loads(sys.argv[2]) +client_ids = [c for c in sys.argv[3:] if c] +api = app.get("api") or {} +scopes = api.get("oauth2PermissionScopes") or [] +preauth = api.get("preAuthorizedApplications") or [] +by_app = {item.get("appId"): item for item in preauth if item.get("appId")} +for client_id in client_ids: + entry = by_app.setdefault(client_id, {"appId": client_id, "delegatedPermissionIds": []}) + ids = entry.setdefault("delegatedPermissionIds", []) + if scope_id not in ids: + ids.append(scope_id) +payload = { + "api": { + "requestedAccessTokenVersion": api.get("requestedAccessTokenVersion") or 2, + "oauth2PermissionScopes": scopes, + "preAuthorizedApplications": list(by_app.values()), + } +} +print(json.dumps(payload, separators=(",", ":"))) +' "$scope_id" "$app_json" "$@")" + patch_graph_application "$api_object_id" "$body" + printf 'Pre-authorized Azure CLI/public clients for HR MCP delegated scope.\n' +} + +save_azd_value() { + local key="$1" + local value="$2" + if [[ -n "$value" ]]; then + azd env set "$key" "$value" >/dev/null + fi +} + +require_command az +require_command azd +require_command python3 +require_command uuidgen + +if ! az account show >/dev/null 2>&1; then + fail 'Azure CLI is not logged in. Run az login, then rerun this script.' +fi + +ensure_extension application-insights +ensure_extension containerapp + +azd_resource_group="$(azd_get_optional AZURE_RESOURCE_GROUP)" +azd_location="$(azd_get_optional AZURE_LOCATION)" +azd_env_name="$(azd_get_optional AZURE_ENV_NAME)" +azd_subscription_id="$(azd_get_optional AZURE_SUBSCRIPTION_ID)" + +location="$(first_non_empty "${AZURE_LOCATION:-}" "$azd_location")" +[[ -n "$location" ]] || fail 'AZURE_LOCATION is missing. Set AZURE_LOCATION or select an azd environment with AZURE_LOCATION.' + +subscription_id="$(first_non_empty "${AZURE_SUBSCRIPTION_ID:-}" "$azd_subscription_id")" +if [[ -n "$subscription_id" ]]; then + az account set --subscription "$subscription_id" +else + subscription_id="$(az account show --query id -o tsv)" +fi + +tenant_id="$(az account show --query tenantId -o tsv)" +env_name="$(first_non_empty "${AZURE_ENV_NAME:-}" "$azd_env_name" "hrmcp")" +seed_source="$(first_non_empty "${HR_MCP_NAME_SEED:-}" "$env_name" "$azd_resource_group")" +name_seed="$(safe_name_part "$seed_source")" +compact_seed="$(compact_name_part "$seed_source")" +subscription_suffix="$(printf '%s' "$subscription_id" | tr -d '-' | cut -c1-8)" +tag_env_name="${env_name:-unknown}" + +hr_mcp_resource_group="$(first_non_empty "${HR_MCP_RESOURCE_GROUP:-}" "$(printf 'rg-%s-hr-mcp' "$name_seed" | cut -c1-90 | trim_trailing_hyphen)")" +log_analytics_workspace_name="$(first_non_empty "${HR_MCP_LOG_ANALYTICS_NAME:-}" "$(printf 'law-%s-hr-%s' "$name_seed" "$subscription_suffix" | cut -c1-63 | trim_trailing_hyphen)")" +app_insights_name="$(first_non_empty "${HR_MCP_APP_INSIGHTS_NAME:-}" "$(printf 'appi-%s-hr-%s' "$name_seed" "$subscription_suffix" | cut -c1-255 | trim_trailing_hyphen)")" +acr_name="$(first_non_empty "${HR_MCP_ACR_NAME:-}" "$(printf 'acr%shr%s' "$compact_seed" "$subscription_suffix" | sed 's/[^a-zA-Z0-9]//g' | cut -c1-50)")" +aca_environment_name="$(first_non_empty "${HR_MCP_ACA_ENVIRONMENT_NAME:-}" "$(printf 'cae-%s-hr-mcp' "$name_seed" | cut -c1-32 | trim_trailing_hyphen)")" +container_app_name="$(first_non_empty "${HR_MCP_CONTAINER_APP_NAME:-}" "$(printf 'ca-%s-hr-mcp' "$name_seed" | cut -c1-32 | trim_trailing_hyphen)")" +private_aca_environment_name="$(first_non_empty "${HR_MCP_PRIVATE_ACA_ENVIRONMENT_NAME:-}" "$(printf 'cae-%s-hr-mcp-int' "$name_seed" | cut -c1-32 | trim_trailing_hyphen)")" +private_container_app_name="$(first_non_empty "${HR_MCP_PRIVATE_CONTAINER_APP_NAME:-}" "$(printf 'ca-%s-hr-mcp-int' "$name_seed" | cut -c1-32 | trim_trailing_hyphen)")" +api_display_name="$(first_non_empty "${HR_MCP_API_APP_DISPLAY_NAME:-}" "${env_name}-hr-mcp-api")" +public_display_name="$(first_non_empty "${HR_MCP_PUBLIC_CLIENT_DISPLAY_NAME:-}" "${env_name}-hr-mcp-public-client")" +scope_name="$(first_non_empty "${HR_MCP_SCOPE_NAME:-}" "$DEFAULT_SCOPE_VALUE")" +app_role_value="$(first_non_empty "${HR_MCP_APP_ROLE_VALUE:-}" "$DEFAULT_APP_ROLE_VALUE")" +validate_scope_name "$scope_name" +image_repository="$(first_non_empty "${HR_MCP_IMAGE_REPOSITORY:-}" "hr-mcp")" +image_tag="$(first_non_empty "${HR_MCP_IMAGE_TAG:-}" "build-$(date -u +%Y%m%d%H%M%S)")" +issuer="$(first_non_empty "${HR_MCP_ISSUER:-}" "https://login.microsoftonline.com/${tenant_id}/v2.0")" + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)" +workshop_dir="$(cd -- "${script_dir}/../.." && pwd)" +dockerfile_path="${workshop_dir}/mcp-hr/server/Dockerfile" +[[ -f "$dockerfile_path" ]] || fail "Dockerfile not found: $dockerfile_path" + +citadel_hub_resource_group="$(first_non_empty "${HR_MCP_CITADEL_RESOURCE_GROUP:-}" "${AZURE_RESOURCE_GROUP:-}" "$azd_resource_group")" +[[ -n "$citadel_hub_resource_group" ]] || fail 'Citadel hub resource group is missing. Set HR_MCP_CITADEL_RESOURCE_GROUP or ensure AZURE_RESOURCE_GROUP is available in azd env.' +HR_MCP_ACA_ADDED_VNET_PREFIX_VALUE="" +resolve_citadel_aca_subnet \ + "$citadel_hub_resource_group" \ + "${HR_MCP_CITADEL_VNET_NAME:-}" \ + "${HR_MCP_ACA_SUBNET_NAME:-snet-mcp}" \ + "${HR_MCP_ACA_SUBNET_ID:-}" \ + "${HR_MCP_ACA_SUBNET_PREFIX:-}" \ + "${HR_MCP_ACA_SUBNET_PREFIX_LENGTH:-26}" + +log 'Creating HR MCP resource group' +az group create \ + --name "$hr_mcp_resource_group" \ + --location "$location" \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + -o none + +log 'Creating Log Analytics workspace' +if ! az monitor log-analytics workspace show --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" >/dev/null 2>&1; then + az monitor log-analytics workspace create \ + --resource-group "$hr_mcp_resource_group" \ + --workspace-name "$log_analytics_workspace_name" \ + --location "$location" \ + --sku PerGB2018 \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + -o none +else + printf 'Log Analytics workspace already exists: %s\n' "$log_analytics_workspace_name" +fi +log_analytics_workspace_id="$(az monitor log-analytics workspace show --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" --query id -o tsv)" + +log 'Creating workspace-based Application Insights' +if ! az monitor app-insights component show --app "$app_insights_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az monitor app-insights component create \ + --app "$app_insights_name" \ + --location "$location" \ + --resource-group "$hr_mcp_resource_group" \ + --workspace "$log_analytics_workspace_id" \ + --kind web \ + --application-type web \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + -o none +else + printf 'Application Insights already exists: %s\n' "$app_insights_name" +fi +app_insights_connection_string="$(az monitor app-insights component show --app "$app_insights_name" --resource-group "$hr_mcp_resource_group" --query connectionString -o tsv)" + +log 'Creating or configuring Entra app registrations' +ensure_api_app_registration "$api_display_name" "${HR_MCP_API_CLIENT_ID:-}" "$scope_name" "$app_role_value" +ensure_public_client_registration "$public_display_name" "${HR_MCP_PUBLIC_CLIENT_ID:-}" "$HR_MCP_API_CLIENT_ID_VALUE" "$HR_MCP_SCOPE_ID" +ensure_api_service_principal "$HR_MCP_API_CLIENT_ID_VALUE" +preauthorize_api_clients "$HR_MCP_API_OBJECT_ID" "$HR_MCP_SCOPE_ID" "$AZURE_CLI_CLIENT_ID" "$HR_MCP_PUBLIC_CLIENT_ID_VALUE" + +log 'Creating Azure Container Registry' +if ! az acr show --name "$acr_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az acr create \ + --name "$acr_name" \ + --resource-group "$hr_mcp_resource_group" \ + --location "$location" \ + --sku Basic \ + --admin-enabled false \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + -o none +else + printf 'Container Registry already exists: %s\n' "$acr_name" +fi +acr_id="$(az acr show --name "$acr_name" --resource-group "$hr_mcp_resource_group" --query id -o tsv)" +acr_login_server="$(az acr show --name "$acr_name" --resource-group "$hr_mcp_resource_group" --query loginServer -o tsv)" +image_name="${acr_login_server}/${image_repository}:${image_tag}" + +log 'Building HR MCP image remotely with ACR' +ensure_acr_base_images "$acr_name" "$acr_login_server" +printf 'Note: ACR remote builds run on a shared agent pool. On Basic/Standard SKUs the run can sit in "Queued" (no log output) for several minutes before it starts; this is normal and not a hang. The build itself takes well under a minute.\n' +( + # Build from inside the small server folder so ACR only uploads ~250 KB of + # source instead of the entire workshop tree (e.g. a multi-hundred-MB .venv). + # `--file` is resolved relative to the current directory, so cd into the + # context root and pass "." as the build context. + cd "$workshop_dir/mcp-hr/server" + az acr build \ + --registry "$acr_name" \ + --image "${image_repository}:${image_tag}" \ + --file "Dockerfile" \ + --build-arg "BASE_IMAGE=${HR_MCP_BASE_IMAGE_REF}" \ + "." \ + --only-show-errors +) + +log 'Creating Container Apps environment' +if ! az containerapp env show --name "$aca_environment_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az containerapp env create \ + --name "$aca_environment_name" \ + --resource-group "$hr_mcp_resource_group" \ + --location "$location" \ + --logs-workspace-id "$(az monitor log-analytics workspace show --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" --query customerId -o tsv)" \ + --logs-workspace-key "$(az monitor log-analytics workspace get-shared-keys --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" --query primarySharedKey -o tsv)" \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + --only-show-errors \ + -o none +else + printf 'Container Apps environment already exists: %s\n' "$aca_environment_name" +fi + +log 'Creating private Container Apps environment in the Citadel hub VNet' +if ! az containerapp env show --name "$private_aca_environment_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az containerapp env create \ + --name "$private_aca_environment_name" \ + --resource-group "$hr_mcp_resource_group" \ + --location "$location" \ + --logs-workspace-id "$(az monitor log-analytics workspace show --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" --query customerId -o tsv)" \ + --logs-workspace-key "$(az monitor log-analytics workspace get-shared-keys --resource-group "$hr_mcp_resource_group" --workspace-name "$log_analytics_workspace_name" --query primarySharedKey -o tsv)" \ + --infrastructure-subnet-resource-id "$HR_MCP_ACA_SUBNET_ID_VALUE" \ + --internal-only \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + --only-show-errors \ + -o none +else + printf 'Private Container Apps environment already exists: %s\n' "$private_aca_environment_name" +fi + +private_env_default_domain="$(az containerapp env show --name "$private_aca_environment_name" --resource-group "$hr_mcp_resource_group" --query properties.defaultDomain -o tsv)" +private_env_static_ip="$(az containerapp env show --name "$private_aca_environment_name" --resource-group "$hr_mcp_resource_group" --query properties.staticIp -o tsv)" +citadel_vnet_id="$(az network vnet show --resource-group "$citadel_hub_resource_group" --name "$HR_MCP_CITADEL_VNET_NAME_VALUE" --query id -o tsv)" +ensure_private_aca_dns "$citadel_hub_resource_group" "$citadel_vnet_id" "$private_env_default_domain" "$private_env_static_ip" + +common_env_vars=( + "AUTH_ENABLED=true" + "ENTRA_TENANT_ID=${tenant_id}" + "ENTRA_AUDIENCE=${HR_MCP_AUDIENCE_VALUE}" + "ENTRA_REQUIRED_SCOPE=${scope_name}" + "ENTRA_REQUIRED_ROLE=${app_role_value}" + "ENTRA_ISSUER=${issuer}" + "APPLICATIONINSIGHTS_CONNECTION_STRING=${app_insights_connection_string}" + "OTEL_SERVICE_NAME=hr-mcp" + "OTEL_RESOURCE_ATTRIBUTES=service.namespace=workshop,deployment.environment=${tag_env_name},azure.resource_group=${hr_mcp_resource_group}" + "PORT=8080" +) + +log 'Creating or updating HR MCP Container App' +if ! az containerapp show --name "$container_app_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az containerapp create \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --environment "$aca_environment_name" \ + --image 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' \ + --target-port 80 \ + --ingress external \ + --min-replicas 0 \ + --max-replicas 2 \ + --system-assigned \ + --env-vars "${common_env_vars[@]}" \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp" "$SECURITY_CONTROL_TAG" \ + --only-show-errors \ + -o none +else + printf 'Container App already exists: %s\n' "$container_app_name" + az containerapp identity assign \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --system-assigned \ + --only-show-errors \ + -o none >/dev/null +fi + +container_app_principal_id="$(az containerapp show --name "$container_app_name" --resource-group "$hr_mcp_resource_group" --query identity.principalId -o tsv)" +[[ -n "$container_app_principal_id" ]] || fail 'Could not resolve Container App managed identity principalId.' +assign_role_if_missing "$container_app_principal_id" ServicePrincipal "$ACR_PULL_ROLE_ID" 'AcrPull (HR MCP Container App)' "$acr_id" +wait_for_role_assignment "$container_app_principal_id" "$ACR_PULL_ROLE_ID" "$acr_id" 'AcrPull' + +az containerapp registry set \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --server "$acr_login_server" \ + --identity system \ + --only-show-errors \ + -o none + +az containerapp ingress update \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --target-port 8080 \ + --only-show-errors \ + -o none + +az containerapp update \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --image "$image_name" \ + --set-env-vars "${common_env_vars[@]}" \ + --min-replicas 0 \ + --max-replicas 2 \ + --only-show-errors \ + -o none + +log 'Creating or updating private HR MCP Container App for APIM backend traffic' +if ! az containerapp show --name "$private_container_app_name" --resource-group "$hr_mcp_resource_group" >/dev/null 2>&1; then + az containerapp create \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --environment "$private_aca_environment_name" \ + --image 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' \ + --target-port 80 \ + --ingress external \ + --min-replicas 0 \ + --max-replicas 2 \ + --system-assigned \ + --env-vars "${common_env_vars[@]}" \ + --tags "azd-env-name=${tag_env_name}" "workload=hr-mcp-private" "$SECURITY_CONTROL_TAG" \ + --only-show-errors \ + -o none +else + printf 'Private Container App already exists: %s\n' "$private_container_app_name" + az containerapp identity assign \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --system-assigned \ + --only-show-errors \ + -o none >/dev/null +fi + +private_container_app_principal_id="$(az containerapp show --name "$private_container_app_name" --resource-group "$hr_mcp_resource_group" --query identity.principalId -o tsv)" +[[ -n "$private_container_app_principal_id" ]] || fail 'Could not resolve private Container App managed identity principalId.' +assign_role_if_missing "$private_container_app_principal_id" ServicePrincipal "$ACR_PULL_ROLE_ID" 'AcrPull (Private HR MCP Container App)' "$acr_id" +wait_for_role_assignment "$private_container_app_principal_id" "$ACR_PULL_ROLE_ID" "$acr_id" 'AcrPull (private app)' + +az containerapp registry set \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --server "$acr_login_server" \ + --identity system \ + --only-show-errors \ + -o none + +az containerapp ingress update \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --target-port 8080 \ + --type external \ + --transport http \ + --only-show-errors \ + -o none + +az containerapp update \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --image "$image_name" \ + --set-env-vars "${common_env_vars[@]}" \ + --min-replicas 0 \ + --max-replicas 2 \ + --only-show-errors \ + -o none + +if az containerapp update -h 2>/dev/null | grep -q -- '--startup-probe'; then + log 'Configuring health probes' + az containerapp update \ + --name "$container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --startup-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=10,periodSeconds=10,failureThreshold=12' \ + --liveness-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=30,periodSeconds=30,failureThreshold=3' \ + --only-show-errors \ + -o none || warn 'Probe configuration failed. The app is deployed; configure /health probes from the Container App portal if your CLI version lacks compatible probe syntax.' + az containerapp update \ + --name "$private_container_app_name" \ + --resource-group "$hr_mcp_resource_group" \ + --startup-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=10,periodSeconds=10,failureThreshold=12' \ + --liveness-probe 'path=/health,port=8080,transport=HTTP,initialDelaySeconds=30,periodSeconds=30,failureThreshold=3' \ + --only-show-errors \ + -o none || warn 'Private app probe configuration failed. The app is deployed; configure /health probes from the Container App portal if your CLI version lacks compatible probe syntax.' +else + warn 'Azure CLI containerapp extension does not expose probe flags. The app is configured correctly; add /health startup/liveness probes later if desired.' +fi + +container_app_fqdn="$(az containerapp show --name "$container_app_name" --resource-group "$hr_mcp_resource_group" --query properties.configuration.ingress.fqdn -o tsv)" +direct_url="https://${container_app_fqdn}" +direct_mcp_url="${direct_url}/mcp" +private_container_app_fqdn="$(az containerapp show --name "$private_container_app_name" --resource-group "$hr_mcp_resource_group" --query properties.configuration.ingress.fqdn -o tsv)" +private_backend_url="https://${private_container_app_fqdn}" +private_mcp_url="${private_backend_url}/mcp" +private_backend_ip_url="http://${private_env_static_ip}" + +log 'Saving HR MCP outputs to azd environment' +save_azd_value HR_MCP_RESOURCE_GROUP "$hr_mcp_resource_group" +save_azd_value HR_MCP_ACR_NAME "$acr_name" +save_azd_value HR_MCP_ACR_LOGIN_SERVER "$acr_login_server" +save_azd_value HR_MCP_CONTAINER_APP_NAME "$container_app_name" +save_azd_value HR_MCP_PRIVATE_ACA_ENVIRONMENT_NAME "$private_aca_environment_name" +save_azd_value HR_MCP_PRIVATE_CONTAINER_APP_NAME "$private_container_app_name" +save_azd_value HR_MCP_DIRECT_URL "$direct_url" +save_azd_value HR_MCP_DIRECT_MCP_URL "$direct_mcp_url" +save_azd_value HR_MCP_PRIVATE_BACKEND_URL "$private_backend_url" +save_azd_value HR_MCP_PRIVATE_BACKEND_IP_URL "$private_backend_ip_url" +save_azd_value HR_MCP_APIM_BACKEND_HOST_HEADER "$private_container_app_fqdn" +save_azd_value HR_MCP_PRIVATE_MCP_URL "$private_mcp_url" +save_azd_value HR_MCP_ACA_INTERNAL_FQDN "$private_container_app_fqdn" +save_azd_value HR_MCP_PRIVATE_DNS_ZONE "$private_env_default_domain" +save_azd_value HR_MCP_PRIVATE_DNS_RESOURCE_GROUP "$citadel_hub_resource_group" +save_azd_value HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND "true" +save_azd_value HR_MCP_CITADEL_RESOURCE_GROUP "$citadel_hub_resource_group" +save_azd_value HR_MCP_CITADEL_VNET_NAME "$HR_MCP_CITADEL_VNET_NAME_VALUE" +save_azd_value HR_MCP_ACA_SUBNET_NAME "$HR_MCP_ACA_SUBNET_NAME_VALUE" +save_azd_value HR_MCP_ACA_SUBNET_ID "$HR_MCP_ACA_SUBNET_ID_VALUE" +save_azd_value HR_MCP_ACA_ADDED_VNET_PREFIX "$HR_MCP_ACA_ADDED_VNET_PREFIX_VALUE" +save_azd_value HR_MCP_APP_INSIGHTS_NAME "$app_insights_name" +save_azd_value HR_MCP_TENANT_ID "$tenant_id" +save_azd_value HR_MCP_API_CLIENT_ID "$HR_MCP_API_CLIENT_ID_VALUE" +save_azd_value HR_MCP_PUBLIC_CLIENT_ID "$HR_MCP_PUBLIC_CLIENT_ID_VALUE" +save_azd_value HR_MCP_AUDIENCE "$HR_MCP_AUDIENCE_VALUE" +save_azd_value HR_MCP_SCOPE "$HR_MCP_SCOPE_VALUE" +save_azd_value HR_MCP_APP_ROLE_ID "$HR_MCP_APP_ROLE_ID" +save_azd_value HR_MCP_APP_ROLE_VALUE "$app_role_value" +save_azd_value HR_MCP_API_OBJECT_ID "$HR_MCP_API_OBJECT_ID" +save_azd_value HR_MCP_REQUIRED_SCOPE_CLAIM "$scope_name" + +cat <; never store real tokens in source): +{ + "mcpServers": { + "hr-mcp-direct": { + "type": "http", + "url": "$direct_mcp_url", + "headers": { + "Authorization": "Bearer " + } + } + } +} +EOF diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.ps1 new file mode 100644 index 000000000..e5bc0cf4d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.ps1 @@ -0,0 +1,173 @@ +[CmdletBinding()] +param( + [string]$ApimName = $env:HR_MCP_APIM_NAME, + [string]$ApimResourceGroup = $env:HR_MCP_APIM_RESOURCE_GROUP, + [string]$BackendBaseUrl = $env:HR_MCP_APIM_BACKEND_BASE_URL, + [string]$ApiName = $env:HR_MCP_APIM_API_NAME, + [string]$ApiPath = $env:HR_MCP_APIM_PATH, + [string]$BackendName = $env:HR_MCP_APIM_BACKEND_NAME, + [string]$ProductId = $env:HR_MCP_APIM_PRODUCT_ID, + [string]$SubscriptionName = $env:HR_MCP_APIM_SUBSCRIPTION_NAME, + [string]$RequiredScopeClaim = $env:HR_MCP_REQUIRED_SCOPE_CLAIM, + [string]$RequiredRole = $(if ($env:HR_MCP_APP_ROLE_VALUE) { $env:HR_MCP_APP_ROLE_VALUE } else { 'Mcp.Invoke' }), + [string]$BackendHostHeader = $env:HR_MCP_APIM_BACKEND_HOST_HEADER, + [switch]$RequirePrivateBackend +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +if ($PSVersionTable.PSVersion.Major -ge 7) { $PSNativeCommandUseErrorActionPreference = $false } + +function Write-Step { param([string]$Message) Write-Host "`n==> $Message" } +function Assert-Command { param([string]$Name) if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { throw "Required command not found: $Name" } } +function ConvertTo-TrimmedCliOutput { param([AllowNull()]$Value) if ($null -eq $Value) { return $null }; $text = [string]($Value | Select-Object -First 1); if ([string]::IsNullOrWhiteSpace($text)) { return $null }; return $text.Trim() } +function Get-AzdValue { param([string]$Name) $value = (& azd env get-value $Name 2>$null); if ($LASTEXITCODE -ne 0) { return $null }; return ConvertTo-TrimmedCliOutput $value } +function Get-FirstNonEmpty { param([AllowNull()][string[]]$Values) foreach ($value in $Values) { if (-not [string]::IsNullOrWhiteSpace($value)) { return $value } }; return $null } +function Save-AzdValue { param([string]$Name, [AllowNull()][string]$Value) if (-not [string]::IsNullOrWhiteSpace($Value)) { & azd env set $Name $Value *> $null } } +function Remove-McpSuffix { param([AllowNull()][string]$Url) if ([string]::IsNullOrWhiteSpace($Url)) { return $null }; $u = $Url.TrimEnd('/'); if ($u.EndsWith('/mcp', [StringComparison]::OrdinalIgnoreCase)) { return $u.Substring(0, $u.Length - 4).TrimEnd('/') }; return $u } +function Test-Truthy { param([AllowNull()][string]$Value) return $Value -in @('1','true','TRUE','True','yes','YES','Yes') } + +Assert-Command az +Assert-Command azd +& az account show *> $null +if ($LASTEXITCODE -ne 0) { throw 'Azure CLI is not logged in. Run az login, then rerun this script.' } + +$azdResourceGroup = Get-AzdValue AZURE_RESOURCE_GROUP +$azdSubscriptionId = Get-AzdValue AZURE_SUBSCRIPTION_ID +$azdTenantId = Get-AzdValue AZURE_TENANT_ID +$subscriptionId = Get-FirstNonEmpty @($env:AZURE_SUBSCRIPTION_ID, $azdSubscriptionId) +if ($subscriptionId) { & az account set --subscription $subscriptionId } + +$tenantId = Get-FirstNonEmpty @($env:HR_MCP_TENANT_ID, (Get-AzdValue HR_MCP_TENANT_ID), $env:AZURE_TENANT_ID, $azdTenantId, (ConvertTo-TrimmedCliOutput (& az account show --query tenantId -o tsv))) +$ApimName = Get-FirstNonEmpty @($ApimName, $env:APIM_NAME, $env:AZURE_APIM_NAME, (Get-AzdValue HR_MCP_APIM_NAME), (Get-AzdValue APIM_NAME), (Get-AzdValue AZURE_APIM_NAME)) +$ApimResourceGroup = Get-FirstNonEmpty @($ApimResourceGroup, $env:APIM_RESOURCE_GROUP, $env:AZURE_APIM_RESOURCE_GROUP, (Get-AzdValue HR_MCP_APIM_RESOURCE_GROUP), (Get-AzdValue APIM_RESOURCE_GROUP), (Get-AzdValue AZURE_APIM_RESOURCE_GROUP), $azdResourceGroup) + +if ([string]::IsNullOrWhiteSpace($ApimName)) { + if (-not [string]::IsNullOrWhiteSpace($ApimResourceGroup)) { + $countInRg = ConvertTo-TrimmedCliOutput (& az apim list --resource-group $ApimResourceGroup --query 'length(@)' -o tsv 2>$null) + if ($countInRg -eq '1') { + $ApimName = ConvertTo-TrimmedCliOutput (& az apim list --resource-group $ApimResourceGroup --query '[0].name' -o tsv) + Write-Warning "APIM name was not provided; using the only APIM instance in resource group '$ApimResourceGroup': $ApimName" + } + } + if ([string]::IsNullOrWhiteSpace($ApimName)) { + $count = ConvertTo-TrimmedCliOutput (& az apim list --query 'length(@)' -o tsv 2>$null) + if ($count -eq '1') { + $ApimName = ConvertTo-TrimmedCliOutput (& az apim list --query '[0].name' -o tsv) + $ApimResourceGroup = ConvertTo-TrimmedCliOutput (& az apim list --query '[0].resourceGroup' -o tsv) + Write-Warning "APIM name was not provided; using the only APIM instance in the subscription: $ApimName" + } else { + throw 'Could not determine APIM instance. Set HR_MCP_APIM_NAME and HR_MCP_APIM_RESOURCE_GROUP, or azd env values APIM_NAME/AZURE_APIM_NAME.' + } + } +} + +& az apim show --name $ApimName --resource-group $ApimResourceGroup *> $null +if ($LASTEXITCODE -ne 0) { + $matchesJson = (& az apim list --query "[?name=='$ApimName']" -o json 2>$null) + $matches = $matchesJson | ConvertFrom-Json + if ($matches.Count -eq 1) { + $ApimResourceGroup = $matches[0].resourceGroup + Write-Warning "Resolved APIM resource group for ${ApimName}: $ApimResourceGroup" + } else { + throw "APIM '$ApimName' was not found in resource group '$ApimResourceGroup'. Set HR_MCP_APIM_RESOURCE_GROUP explicitly." + } +} + +$privateRequired = (Test-Truthy (Get-FirstNonEmpty @($env:HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND, (Get-AzdValue HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND)))) -or $RequirePrivateBackend.IsPresent +$privateBackend = Get-FirstNonEmpty @($env:HR_MCP_PRIVATE_BACKEND_URL, (Get-AzdValue HR_MCP_PRIVATE_BACKEND_URL)) +$acaInternalFqdn = Get-FirstNonEmpty @($env:HR_MCP_ACA_INTERNAL_FQDN, (Get-AzdValue HR_MCP_ACA_INTERNAL_FQDN)) +if ([string]::IsNullOrWhiteSpace($privateBackend) -and -not [string]::IsNullOrWhiteSpace($acaInternalFqdn)) { $privateBackend = "https://$acaInternalFqdn" } +$directUrl = Get-FirstNonEmpty @($env:HR_MCP_DIRECT_URL, (Get-AzdValue HR_MCP_DIRECT_URL)) +$directMcpUrl = Get-FirstNonEmpty @($env:HR_MCP_DIRECT_MCP_URL, (Get-AzdValue HR_MCP_DIRECT_MCP_URL)) + +if ($privateRequired) { + # $BackendBaseUrl defaults to $env:HR_MCP_APIM_BACKEND_BASE_URL (an explicit override for THIS run only). + # The azd-persisted value is deliberately not consulted, so the freshly deployed $privateBackend wins + # after a teardown + redeploy when the ACA managed-environment domain changes. + $BackendBaseUrl = Get-FirstNonEmpty @($BackendBaseUrl, $privateBackend) + if ([string]::IsNullOrWhiteSpace($BackendBaseUrl)) { throw 'Private APIM-to-ACA backend was requested. Set HR_MCP_PRIVATE_BACKEND_URL or HR_MCP_APIM_BACKEND_BASE_URL after VNet/DNS routing is ready.' } +} else { + $BackendBaseUrl = Get-FirstNonEmpty @($BackendBaseUrl, $privateBackend, $directUrl, (Remove-McpSuffix $directMcpUrl)) +} +$BackendBaseUrl = Remove-McpSuffix $BackendBaseUrl +if ([string]::IsNullOrWhiteSpace($BackendBaseUrl)) { throw 'Could not determine HR MCP backend base URL. Set HR_MCP_DIRECT_URL, HR_MCP_DIRECT_MCP_URL, or HR_MCP_APIM_BACKEND_BASE_URL.' } + +$backendHost = ($BackendBaseUrl -replace '^[a-zA-Z]+://', '') -replace '/.*$', '' +if ($backendHost -match '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$') { + # IP backend: APIM must send the ACA internal FQDN as the Host header for ingress routing. + if ([string]::IsNullOrWhiteSpace($BackendHostHeader)) { + $BackendHostHeader = Get-FirstNonEmpty @($acaInternalFqdn, (Get-AzdValue HR_MCP_ACA_INTERNAL_FQDN)) + } +} else { + # FQDN backend: always tie the Host header to the chosen backend host so a stale persisted + # HR_MCP_APIM_BACKEND_HOST_HEADER can never disagree with the backend URL after a redeploy. + $BackendHostHeader = $backendHost +} + +$audience = Get-FirstNonEmpty @($env:HR_MCP_AUDIENCE, (Get-AzdValue HR_MCP_AUDIENCE)) +$scope = Get-FirstNonEmpty @($env:HR_MCP_SCOPE, (Get-AzdValue HR_MCP_SCOPE)) +if ([string]::IsNullOrWhiteSpace($RequiredScopeClaim) -and -not [string]::IsNullOrWhiteSpace($scope)) { $RequiredScopeClaim = ($scope -split '/')[-1] } +if ([string]::IsNullOrWhiteSpace($tenantId)) { throw 'HR_MCP_TENANT_ID is missing.' } +if ([string]::IsNullOrWhiteSpace($audience)) { throw 'HR_MCP_AUDIENCE is missing. Run deploy-hr-mcp first or set it explicitly.' } +if ([string]::IsNullOrWhiteSpace($RequiredScopeClaim)) { throw 'HR_MCP_SCOPE is missing. Run deploy-hr-mcp first or set HR_MCP_REQUIRED_SCOPE_CLAIM.' } + +$ApiName = Get-FirstNonEmpty @($ApiName, (Get-AzdValue HR_MCP_APIM_API_NAME), 'hr-mcp-api') +$ApiPath = Get-FirstNonEmpty @($ApiPath, (Get-AzdValue HR_MCP_APIM_PATH), 'hr-mcp') +$BackendName = Get-FirstNonEmpty @($BackendName, (Get-AzdValue HR_MCP_APIM_BACKEND_NAME), 'hr-mcp-aca-backend') +$ProductId = Get-FirstNonEmpty @($ProductId, (Get-AzdValue HR_MCP_APIM_PRODUCT_ID), 'MCP-HR-Tools-DEV') +$SubscriptionName = Get-FirstNonEmpty @($SubscriptionName, (Get-AzdValue HR_MCP_APIM_SUBSCRIPTION_NAME), 'MCP-HR-Tools-DEV-SUB-01') + +$scriptDir = Split-Path -Parent $PSCommandPath +$infraDir = Resolve-Path (Join-Path $scriptDir '..' 'infra') +$gatewayUrl = ConvertTo-TrimmedCliOutput (& az apim show --name $ApimName --resource-group $ApimResourceGroup --query gatewayUrl -o tsv) +$mcpUrl = "$($gatewayUrl.TrimEnd('/'))/$ApiPath/mcp" + +Write-Step 'Publishing HR MCP API, backend, product, policy, and subscription to APIM' +& az deployment group create ` + --name 'publish-hr-mcp-apim' ` + --resource-group $ApimResourceGroup ` + --template-file (Join-Path $infraDir 'main.bicep') ` + --parameters ` + apimName=$ApimName ` + apiName=$ApiName ` + apiPath=$ApiPath ` + backendName=$BackendName ` + backendBaseUrl=$BackendBaseUrl ` + productId=$ProductId ` + subscriptionName=$SubscriptionName ` + tenantId=$tenantId ` + jwtAudience=$audience ` + requiredScope=$RequiredScopeClaim ` + requiredRole=$RequiredRole ` + backendHostHeader=$BackendHostHeader ` + --only-show-errors ` + -o none +if ($LASTEXITCODE -ne 0) { throw 'APIM publication deployment failed.' } + +Write-Step 'Saving HR MCP APIM outputs to azd environment' +Save-AzdValue HR_MCP_APIM_NAME $ApimName +Save-AzdValue HR_MCP_APIM_RESOURCE_GROUP $ApimResourceGroup +Save-AzdValue HR_MCP_APIM_API_NAME $ApiName +Save-AzdValue HR_MCP_APIM_PATH $ApiPath +Save-AzdValue HR_MCP_APIM_MCP_URL $mcpUrl +Save-AzdValue HR_MCP_APIM_PRODUCT_ID $ProductId +Save-AzdValue HR_MCP_APIM_SUBSCRIPTION_NAME $SubscriptionName +Save-AzdValue HR_MCP_APIM_BACKEND_NAME $BackendName +Save-AzdValue HR_MCP_APIM_BACKEND_BASE_URL $BackendBaseUrl +Save-AzdValue HR_MCP_APIM_BACKEND_HOST_HEADER $BackendHostHeader + +Write-Host "`nHR MCP APIM publication is ready." +Write-Host "APIM: $ApimName ($ApimResourceGroup)" +Write-Host "Backend base URL: $BackendBaseUrl" +Write-Host "MCP endpoint: $mcpUrl" +Write-Host "Product: $ProductId" +Write-Host "Subscription: $SubscriptionName" +Write-Host 'Use the APIM subscription key plus Authorization: Bearer when calling the APIM MCP endpoint.' +Write-Host '' +Write-Host 'Smoke test from the repository root:' +Write-Host ' cd workshop; uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py' +Write-Host '' +Write-Host 'If automatic subscription-key retrieval is unavailable, set it without printing it:' +Write-Host " `$env:HR_MCP_APIM_SUBSCRIPTION_KEY = az apim subscription show --resource-group '$ApimResourceGroup' --service-name '$ApimName' --sid '$SubscriptionName' --query primaryKey -o tsv" +Write-Host ' cd workshop; uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.sh new file mode 100755 index 000000000..b1ce2363d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/publish-hr-mcp-apim.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +set -euo pipefail + +log() { printf '\n==> %s\n' "$1"; } +fail() { printf 'Error: %s\n' "$1" >&2; exit 1; } +warn() { printf 'Warning: %s\n' "$1" >&2; } +require_command() { command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"; } +azd_get_optional() { + local value + if value="$(azd env get-value "$1" 2>/dev/null)"; then + printf '%s' "$value" + fi +} +first_non_empty() { local v; for v in "$@"; do [[ -n "${v:-}" ]] && { printf '%s' "$v"; return 0; }; done; return 0; } +save_azd_value() { + local k="$1" v="$2" + if [[ -n "$v" ]]; then + azd env set "$k" "$v" >/dev/null + fi +} +trim_mcp_suffix() { local url="${1%/}"; printf '%s' "${url%/mcp}"; } +truthy() { case "${1:-}" in 1|true|TRUE|True|yes|YES|Yes) return 0;; *) return 1;; esac; } + +require_command az +require_command azd + +if ! az account show >/dev/null 2>&1; then + fail 'Azure CLI is not logged in. Run az login, then rerun this script.' +fi + +azd_resource_group="$(azd_get_optional AZURE_RESOURCE_GROUP)" +azd_subscription_id="$(azd_get_optional AZURE_SUBSCRIPTION_ID)" +azd_tenant_id="$(azd_get_optional AZURE_TENANT_ID)" + +subscription_id="$(first_non_empty "${AZURE_SUBSCRIPTION_ID:-}" "$azd_subscription_id")" +if [[ -n "$subscription_id" ]]; then + az account set --subscription "$subscription_id" +fi + +tenant_id="$(first_non_empty "${HR_MCP_TENANT_ID:-}" "$(azd_get_optional HR_MCP_TENANT_ID)" "${AZURE_TENANT_ID:-}" "$azd_tenant_id" "$(az account show --query tenantId -o tsv)")" + +apim_name="$(first_non_empty "${HR_MCP_APIM_NAME:-}" "${APIM_NAME:-}" "${AZURE_APIM_NAME:-}" "$(azd_get_optional HR_MCP_APIM_NAME)" "$(azd_get_optional APIM_NAME)" "$(azd_get_optional AZURE_APIM_NAME)")" +apim_rg="$(first_non_empty "${HR_MCP_APIM_RESOURCE_GROUP:-}" "${APIM_RESOURCE_GROUP:-}" "${AZURE_APIM_RESOURCE_GROUP:-}" "$(azd_get_optional HR_MCP_APIM_RESOURCE_GROUP)" "$(azd_get_optional APIM_RESOURCE_GROUP)" "$(azd_get_optional AZURE_APIM_RESOURCE_GROUP)" "$azd_resource_group")" + +if [[ -z "$apim_name" ]]; then + if [[ -n "$apim_rg" ]]; then + count="$(az apim list --resource-group "$apim_rg" --query 'length(@)' -o tsv 2>/dev/null || printf '0')" + if [[ "$count" == "1" ]]; then + apim_name="$(az apim list --resource-group "$apim_rg" --query '[0].name' -o tsv)" + warn "APIM name was not provided; using the only APIM instance in resource group '$apim_rg': $apim_name" + fi + fi + if [[ -z "$apim_name" ]]; then + count="$(az apim list --query 'length(@)' -o tsv 2>/dev/null || printf '0')" + if [[ "$count" == "1" ]]; then + apim_name="$(az apim list --query '[0].name' -o tsv)" + apim_rg="$(az apim list --query '[0].resourceGroup' -o tsv)" + warn "APIM name was not provided; using the only APIM instance in the subscription: $apim_name" + else + fail 'Could not determine APIM instance. Set HR_MCP_APIM_NAME and HR_MCP_APIM_RESOURCE_GROUP, or azd env values APIM_NAME/AZURE_APIM_NAME.' + fi + fi +fi + +if [[ -z "$apim_rg" ]] || ! az apim show --name "$apim_name" --resource-group "$apim_rg" >/dev/null 2>&1; then + matches="$(az apim list --query "[?name=='${apim_name}']" -o json 2>/dev/null || printf '[]')" + match_count="$(python3 -c 'import json,sys; print(len(json.load(sys.stdin)))' <<<"$matches")" + if [[ "$match_count" == "1" ]]; then + apim_rg="$(python3 -c 'import json,sys; print(json.load(sys.stdin)[0]["resourceGroup"])' <<<"$matches")" + warn "Resolved APIM resource group for $apim_name: $apim_rg" + else + fail "APIM '$apim_name' was not found in resource group '$apim_rg'. Set HR_MCP_APIM_RESOURCE_GROUP explicitly." + fi +fi + +private_required="$(first_non_empty "${HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND:-}" "$(azd_get_optional HR_MCP_APIM_REQUIRE_PRIVATE_BACKEND)")" +# Honor HR_MCP_APIM_BACKEND_BASE_URL only when it is set explicitly for THIS run. The azd-persisted +# value is intentionally NOT read here: after a teardown + redeploy the ACA managed-environment +# domain changes, and a stale persisted override would otherwise win over the freshly deployed +# HR_MCP_PRIVATE_BACKEND_URL and silently point APIM at a backend host that no longer resolves. +backend_override="${HR_MCP_APIM_BACKEND_BASE_URL:-}" +private_backend="$(first_non_empty "${HR_MCP_PRIVATE_BACKEND_URL:-}" "$(azd_get_optional HR_MCP_PRIVATE_BACKEND_URL)")" +private_backend_ip="$(first_non_empty "${HR_MCP_PRIVATE_BACKEND_IP_URL:-}" "$(azd_get_optional HR_MCP_PRIVATE_BACKEND_IP_URL)")" +backend_host_header="$(first_non_empty "${HR_MCP_APIM_BACKEND_HOST_HEADER:-}" "$(azd_get_optional HR_MCP_APIM_BACKEND_HOST_HEADER)")" +aca_internal_fqdn="$(first_non_empty "${HR_MCP_ACA_INTERNAL_FQDN:-}" "$(azd_get_optional HR_MCP_ACA_INTERNAL_FQDN)")" +direct_url="$(first_non_empty "${HR_MCP_DIRECT_URL:-}" "$(azd_get_optional HR_MCP_DIRECT_URL)")" +direct_mcp_url="$(first_non_empty "${HR_MCP_DIRECT_MCP_URL:-}" "$(azd_get_optional HR_MCP_DIRECT_MCP_URL)")" + +if [[ -z "$private_backend" && -n "$aca_internal_fqdn" ]]; then + private_backend="https://${aca_internal_fqdn}" +fi + +# Prefer the HTTPS FQDN backend ($private_backend) over the HTTP IP backend ($private_backend_ip). +# The ACA internal load balancer has allowInsecure=false, so an http:// IP backend triggers a +# 301 redirect to https://, which clients cannot resolve. HTTPS to the FQDN also +# provides the correct SNI for ACA ingress routing. +if truthy "$private_required"; then + backend_base_url="$(first_non_empty "$backend_override" "$private_backend" "$private_backend_ip")" + [[ -n "$backend_base_url" ]] || fail 'Private APIM-to-ACA backend was requested. Set HR_MCP_PRIVATE_BACKEND_URL or HR_MCP_APIM_BACKEND_BASE_URL after VNet/DNS routing is ready.' +else + backend_base_url="$(first_non_empty "$backend_override" "$private_backend" "$private_backend_ip" "$direct_url" "$(trim_mcp_suffix "$direct_mcp_url")")" +fi +backend_base_url="$(trim_mcp_suffix "$backend_base_url")" +[[ -n "$backend_base_url" ]] || fail 'Could not determine HR MCP backend base URL. Set HR_MCP_DIRECT_URL, HR_MCP_DIRECT_MCP_URL, or HR_MCP_APIM_BACKEND_BASE_URL.' +backend_host="$(printf '%s' "$backend_base_url" | sed -E 's#^[a-zA-Z]+://##; s#/.*$##')" +if [[ "$backend_host" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # IP backend: APIM must send the ACA internal FQDN as the Host header for ingress routing. + if [[ -z "$backend_host_header" ]]; then + backend_host_header="$(first_non_empty "$aca_internal_fqdn" "$(azd_get_optional HR_MCP_ACA_INTERNAL_FQDN)")" + fi +else + # FQDN backend: always tie the Host header to the chosen backend host so a stale persisted + # HR_MCP_APIM_BACKEND_HOST_HEADER can never disagree with the backend URL after a redeploy. + backend_host_header="$backend_host" +fi + +audience="$(first_non_empty "${HR_MCP_AUDIENCE:-}" "$(azd_get_optional HR_MCP_AUDIENCE)")" +scope="$(first_non_empty "${HR_MCP_SCOPE:-}" "$(azd_get_optional HR_MCP_SCOPE)")" +required_scope="$(first_non_empty "${HR_MCP_REQUIRED_SCOPE_CLAIM:-}" "$(azd_get_optional HR_MCP_REQUIRED_SCOPE_CLAIM)")" +if [[ -z "$required_scope" && -n "$scope" ]]; then + required_scope="${scope##*/}" +fi +[[ -n "$tenant_id" ]] || fail 'HR_MCP_TENANT_ID is missing.' +[[ -n "$audience" ]] || fail 'HR_MCP_AUDIENCE is missing. Run deploy-hr-mcp first or set it explicitly.' +[[ -n "$required_scope" ]] || fail 'HR_MCP_SCOPE is missing. Run deploy-hr-mcp first or set HR_MCP_REQUIRED_SCOPE_CLAIM.' +required_role="$(first_non_empty "${HR_MCP_APP_ROLE_VALUE:-}" "$(azd_get_optional HR_MCP_APP_ROLE_VALUE)" 'Mcp.Invoke')" + +api_name="$(first_non_empty "${HR_MCP_APIM_API_NAME:-}" "$(azd_get_optional HR_MCP_APIM_API_NAME)" 'hr-mcp-api')" +api_path="$(first_non_empty "${HR_MCP_APIM_PATH:-}" "$(azd_get_optional HR_MCP_APIM_PATH)" 'hr-mcp')" +backend_name="$(first_non_empty "${HR_MCP_APIM_BACKEND_NAME:-}" "$(azd_get_optional HR_MCP_APIM_BACKEND_NAME)" 'hr-mcp-aca-backend')" +product_id="$(first_non_empty "${HR_MCP_APIM_PRODUCT_ID:-}" "$(azd_get_optional HR_MCP_APIM_PRODUCT_ID)" 'MCP-HR-Tools-DEV')" +subscription_name="$(first_non_empty "${HR_MCP_APIM_SUBSCRIPTION_NAME:-}" "$(azd_get_optional HR_MCP_APIM_SUBSCRIPTION_NAME)" 'MCP-HR-Tools-DEV-SUB-01')" + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +infra_dir="$(cd -- "${script_dir}/../infra" && pwd)" + +gateway_url="$(az apim show --name "$apim_name" --resource-group "$apim_rg" --query gatewayUrl -o tsv)" +mcp_url="${gateway_url%/}/${api_path}/mcp" + +log 'Publishing HR MCP API, backend, product, policy, and subscription to APIM' +az deployment group create \ + --name 'publish-hr-mcp-apim' \ + --resource-group "$apim_rg" \ + --template-file "${infra_dir}/main.bicep" \ + --parameters \ + apimName="$apim_name" \ + apiName="$api_name" \ + apiPath="$api_path" \ + backendName="$backend_name" \ + backendBaseUrl="$backend_base_url" \ + backendHostHeader="$backend_host_header" \ + productId="$product_id" \ + subscriptionName="$subscription_name" \ + tenantId="$tenant_id" \ + jwtAudience="$audience" \ + requiredScope="$required_scope" \ + requiredRole="$required_role" \ + --only-show-errors \ + -o none + +log 'Saving HR MCP APIM outputs to azd environment' +save_azd_value HR_MCP_APIM_NAME "$apim_name" +save_azd_value HR_MCP_APIM_RESOURCE_GROUP "$apim_rg" +save_azd_value HR_MCP_APIM_API_NAME "$api_name" +save_azd_value HR_MCP_APIM_PATH "$api_path" +save_azd_value HR_MCP_APIM_MCP_URL "$mcp_url" +save_azd_value HR_MCP_APIM_PRODUCT_ID "$product_id" +save_azd_value HR_MCP_APIM_SUBSCRIPTION_NAME "$subscription_name" +save_azd_value HR_MCP_APIM_BACKEND_NAME "$backend_name" +save_azd_value HR_MCP_APIM_BACKEND_BASE_URL "$backend_base_url" +save_azd_value HR_MCP_APIM_BACKEND_HOST_HEADER "$backend_host_header" + +cat < when calling the APIM MCP endpoint. + +Smoke test from the repository root: + cd workshop && uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py + +If automatic subscription-key retrieval is unavailable, set it without printing it: + export HR_MCP_APIM_SUBSCRIPTION_KEY="\$(az apim subscription show --resource-group "$apim_rg" --service-name "$apim_name" --sid "$subscription_name" --query primaryKey -o tsv)" + cd workshop && uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py +EOF diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.ps1 new file mode 100644 index 000000000..06e106e86 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.ps1 @@ -0,0 +1,275 @@ +[CmdletBinding()] +param( + [switch]$Yes, + [switch]$NoWait, + [switch]$KeepAzdEnv, + [switch]$SkipApim, + [switch]$SkipDns, + [switch]$SkipRg, + [switch]$SkipSubnet +) + +# Tear down the HR MCP deployment created by deploy-hr-mcp.ps1 and published by +# publish-hr-mcp-apim.ps1. Every step is best-effort and idempotent: objects that +# were already removed (for example, deleted by hand in the portal) are skipped +# with a notice instead of failing the run. If deploy-hr-mcp expanded the Citadel +# hub VNet (HR_MCP_ACA_ADDED_VNET_PREFIX), that added address space is removed after +# the dedicated subnet is deleted. Finally, all HR_MCP_* values are cleared from the +# active azd environment so a stale backend URL can never leak into a later +# deploy/publish cycle. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +if ($PSVersionTable.PSVersion.Major -ge 7) { $PSNativeCommandUseErrorActionPreference = $false } + +function Write-Step { param([string]$Message) Write-Host "`n==> $Message" } +function Write-Note { param([string]$Message) Write-Host " - $Message" } +function Assert-Command { param([string]$Name) if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { throw "Required command not found: $Name" } } +function ConvertTo-TrimmedCliOutput { param([AllowNull()]$Value) if ($null -eq $Value) { return $null }; $text = [string]($Value | Select-Object -First 1); if ([string]::IsNullOrWhiteSpace($text)) { return $null }; return $text.Trim() } +function Get-AzdValue { param([string]$Name) $value = (& azd env get-value $Name 2>$null); if ($LASTEXITCODE -ne 0) { return $null }; return ConvertTo-TrimmedCliOutput $value } +function Get-FirstNonEmpty { param([AllowNull()][string[]]$Values) foreach ($value in $Values) { if (-not [string]::IsNullOrWhiteSpace($value)) { return $value } }; return $null } + +function ConvertTo-IPv4UInt32 { + param([string]$Address) + $bytes = [System.Net.IPAddress]::Parse($Address).GetAddressBytes() + [Array]::Reverse($bytes) + return [BitConverter]::ToUInt32($bytes, 0) +} + +function Test-CidrContains { + # True when ChildCidr is fully contained within ParentCidr (IPv4 only). + param([string]$ParentCidr, [string]$ChildCidr) + if ($ParentCidr -match ':' -or $ChildCidr -match ':') { return $false } + $parentParts = $ParentCidr.Split('/') + $childParts = $ChildCidr.Split('/') + $parentPrefix = [int]$parentParts[1] + $childPrefix = [int]$childParts[1] + if ($childPrefix -lt $parentPrefix) { return $false } + $parentIp = ConvertTo-IPv4UInt32 $parentParts[0] + $childIp = ConvertTo-IPv4UInt32 $childParts[0] + $mask = if ($parentPrefix -eq 0) { [uint32]0 } else { [uint32]::MaxValue -shl (32 - $parentPrefix) } + return (($parentIp -band $mask) -eq ($childIp -band $mask)) +} + +Assert-Command az +Assert-Command azd +& az account show *> $null +if ($LASTEXITCODE -ne 0) { throw 'Azure CLI is not logged in. Run az login, then rerun this script.' } + +$subscriptionId = Get-FirstNonEmpty @($env:AZURE_SUBSCRIPTION_ID, (Get-AzdValue AZURE_SUBSCRIPTION_ID), (ConvertTo-TrimmedCliOutput (& az account show --query id -o tsv 2>$null))) +if ($subscriptionId) { & az account set --subscription $subscriptionId *> $null } + +$mcpRg = Get-FirstNonEmpty @($env:HR_MCP_RESOURCE_GROUP, (Get-AzdValue HR_MCP_RESOURCE_GROUP)) +$apimName = Get-FirstNonEmpty @($env:HR_MCP_APIM_NAME, (Get-AzdValue HR_MCP_APIM_NAME), $env:APIM_NAME, (Get-AzdValue APIM_NAME)) +$apimRg = Get-FirstNonEmpty @($env:HR_MCP_APIM_RESOURCE_GROUP, (Get-AzdValue HR_MCP_APIM_RESOURCE_GROUP), (Get-AzdValue AZURE_RESOURCE_GROUP)) +$apiName = Get-FirstNonEmpty @($env:HR_MCP_APIM_API_NAME, (Get-AzdValue HR_MCP_APIM_API_NAME), 'hr-mcp-api') +$backendName = Get-FirstNonEmpty @($env:HR_MCP_APIM_BACKEND_NAME, (Get-AzdValue HR_MCP_APIM_BACKEND_NAME), 'hr-mcp-aca-backend') +$productId = Get-FirstNonEmpty @($env:HR_MCP_APIM_PRODUCT_ID, (Get-AzdValue HR_MCP_APIM_PRODUCT_ID), 'MCP-HR-Tools-DEV') +$subscriptionName = Get-FirstNonEmpty @($env:HR_MCP_APIM_SUBSCRIPTION_NAME, (Get-AzdValue HR_MCP_APIM_SUBSCRIPTION_NAME), 'MCP-HR-Tools-DEV-SUB-01') +$dnsRg = Get-FirstNonEmpty @($env:HR_MCP_PRIVATE_DNS_RESOURCE_GROUP, (Get-AzdValue HR_MCP_PRIVATE_DNS_RESOURCE_GROUP)) +$dnsZone = Get-FirstNonEmpty @($env:HR_MCP_PRIVATE_DNS_ZONE, (Get-AzdValue HR_MCP_PRIVATE_DNS_ZONE)) +$dnsLinkName = 'lnk-hr-mcp-aca' + +# Dedicated ACA subnet that deploy-hr-mcp creates inside the shared Citadel hub VNet. It is +# delegated to Microsoft.App/environments, so it can only be removed after the resource group +# (and its ACA environment) is gone. +$subnetId = Get-FirstNonEmpty @($env:HR_MCP_ACA_SUBNET_ID, (Get-AzdValue HR_MCP_ACA_SUBNET_ID)) +$hubRg = Get-FirstNonEmpty @($env:HR_MCP_CITADEL_RESOURCE_GROUP, (Get-AzdValue HR_MCP_CITADEL_RESOURCE_GROUP)) +$vnetName = Get-FirstNonEmpty @($env:HR_MCP_CITADEL_VNET_NAME, (Get-AzdValue HR_MCP_CITADEL_VNET_NAME)) +$subnetName = Get-FirstNonEmpty @($env:HR_MCP_ACA_SUBNET_NAME, (Get-AzdValue HR_MCP_ACA_SUBNET_NAME)) +# Address prefix deploy-hr-mcp added to the hub VNet when no free subnet was available. +# It is removed only after the dedicated subnet is gone, leaving the original space intact. +$addedVnetPrefix = Get-FirstNonEmpty @($env:HR_MCP_ACA_ADDED_VNET_PREFIX, (Get-AzdValue HR_MCP_ACA_ADDED_VNET_PREFIX)) +if ($subnetId) { + if (-not $hubRg -and $subnetId -match '/resourceGroups/([^/]+)/') { $hubRg = $Matches[1] } + if (-not $vnetName -and $subnetId -match '/virtualNetworks/([^/]+)/') { $vnetName = $Matches[1] } + if (-not $subnetName) { $subnetName = ($subnetId -split '/')[-1] } +} + +Write-Step 'HR MCP teardown plan' +Write-Note ("Subscription: {0}" -f ($(if ($subscriptionId) { $subscriptionId } else { '' }))) +if ($SkipApim) { + Write-Note 'APIM publication: SKIPPED (-SkipApim)' +} elseif ($apimName -and $apimRg) { + Write-Note "APIM service: $apimName ($apimRg)" + Write-Note " - subscription: $subscriptionName" + Write-Note " - product: $productId" + Write-Note " - API: $apiName" + Write-Note " - backend: $backendName" +} else { + Write-Note 'APIM publication: SKIPPED (APIM name/resource group not resolved)' +} +if ($SkipDns) { + Write-Note 'Private DNS zone: SKIPPED (-SkipDns)' +} elseif ($dnsRg -and $dnsZone) { + Write-Note "Private DNS zone: $dnsZone ($dnsRg)" +} else { + Write-Note 'Private DNS zone: SKIPPED (zone/resource group not resolved)' +} +if ($SkipRg) { + Write-Note 'Infrastructure RG: SKIPPED (-SkipRg)' +} elseif ($mcpRg) { + Write-Note "Infrastructure RG: $mcpRg (DELETE)" +} else { + Write-Note 'Infrastructure RG: SKIPPED (HR_MCP_RESOURCE_GROUP not resolved)' +} +if ($SkipSubnet) { + Write-Note 'ACA hub subnet: SKIPPED (-SkipSubnet)' +} elseif ($hubRg -and $vnetName -and $subnetName) { + Write-Note "ACA hub subnet: $subnetName in $vnetName ($hubRg)" + if ($addedVnetPrefix) { + Write-Note "Added VNet address space: $addedVnetPrefix (REMOVE after subnet)" + } +} else { + Write-Note 'ACA hub subnet: SKIPPED (subnet/VNet not resolved)' +} +if ($KeepAzdEnv) { + Write-Note 'azd HR_MCP_* values: KEPT (-KeepAzdEnv)' +} else { + Write-Note 'azd HR_MCP_* values: CLEARED' +} + +if (-not $Yes) { + $reply = Read-Host "`nProceed with teardown? Type 'yes' to continue" + if ($reply -ne 'yes') { + Write-Host "`nTeardown cancelled. No changes were made." -ForegroundColor Yellow + exit 0 + } +} + +function Remove-ApimChild { + # Best-effort DELETE of an APIM child resource by relative path. A 404 (already + # gone) or any other failure is reported and skipped rather than aborting. + param([string]$Relative, [string]$Label, [string]$ExtraQuery = '') + $url = "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$apimRg/providers/Microsoft.ApiManagement/service/$apimName/$Relative" + "?api-version=2024-06-01-preview$ExtraQuery" + & az rest --method delete --url $url --only-show-errors -o none *> $null + if ($LASTEXITCODE -eq 0) { Write-Note "Deleted APIM $Label" } else { Write-Note "APIM $Label already absent or not deletable (continuing)" } +} + +if (-not $SkipApim -and $apimName -and $apimRg -and $subscriptionId) { + & az apim show --name $apimName --resource-group $apimRg *> $null + if ($LASTEXITCODE -eq 0) { + Write-Step "Removing HR MCP publication objects from APIM: $apimName" + Remove-ApimChild "subscriptions/$subscriptionName" "subscription '$subscriptionName'" + Remove-ApimChild "products/$productId" "product '$productId'" '&deleteSubscriptions=true' + Remove-ApimChild "apis/$apiName" "API '$apiName'" + Remove-ApimChild "backends/$backendName" "backend '$backendName'" + } else { + Write-Step "APIM service '$apimName' not found in '$apimRg'; skipping APIM cleanup." + } +} + +if (-not $SkipDns -and $dnsRg -and $dnsZone) { + & az network private-dns zone show --resource-group $dnsRg --name $dnsZone *> $null + if ($LASTEXITCODE -eq 0) { + Write-Step "Removing private DNS zone: $dnsZone" + & az network private-dns link vnet show --resource-group $dnsRg --zone-name $dnsZone --name $dnsLinkName *> $null + if ($LASTEXITCODE -eq 0) { + & az network private-dns link vnet delete --resource-group $dnsRg --zone-name $dnsZone --name $dnsLinkName --yes --only-show-errors -o none *> $null + if ($LASTEXITCODE -eq 0) { Write-Note "Deleted vnet link '$dnsLinkName'" } else { Write-Note "Could not delete vnet link '$dnsLinkName' (continuing)" } + } else { + Write-Note "Vnet link '$dnsLinkName' already absent (continuing)" + } + & az network private-dns zone delete --resource-group $dnsRg --name $dnsZone --yes --only-show-errors -o none *> $null + if ($LASTEXITCODE -eq 0) { Write-Note "Deleted private DNS zone '$dnsZone'" } else { Write-Note "Could not delete private DNS zone '$dnsZone' (continuing)" } + } else { + Write-Step "Private DNS zone '$dnsZone' already absent (continuing)." + } +} + +$rgDeleteAsync = $false +if (-not $SkipRg -and $mcpRg) { + & az group show --name $mcpRg *> $null + if ($LASTEXITCODE -eq 0) { + if ($NoWait) { + Write-Step "Deleting resource group (no wait): $mcpRg" + & az group delete --name $mcpRg --yes --no-wait -o none + Write-Note 'Deletion started in the background.' + $rgDeleteAsync = $true + } else { + Write-Step "Deleting resource group (this can take several minutes): $mcpRg" + & az group delete --name $mcpRg --yes -o none + Write-Note 'Resource group deleted.' + } + } else { + Write-Step "Resource group '$mcpRg' already absent (continuing)." + } +} + +if (-not $SkipSubnet -and $hubRg -and $vnetName -and $subnetName) { + & az network vnet subnet show --resource-group $hubRg --vnet-name $vnetName --name $subnetName *> $null + if ($LASTEXITCODE -eq 0) { + if ($rgDeleteAsync) { + Write-Step "Deferring ACA subnet '$subnetName' deletion" + Write-Note 'The subnet is still delegated to the ACA environment while the resource group deletes in the background.' + Write-Note "Rerun: teardown-hr-mcp.ps1 -SkipApim -SkipDns -SkipRg -KeepAzdEnv (after '$mcpRg' is gone)" + } else { + Write-Step "Deleting ACA subnet '$subnetName' from Citadel hub VNet '$vnetName'" + & az network vnet subnet delete --resource-group $hubRg --vnet-name $vnetName --name $subnetName --only-show-errors -o none *> $null + if ($LASTEXITCODE -eq 0) { + Write-Note "Deleted subnet '$subnetName'" + # Subnet is gone; now remove the address prefix deploy added (if any), but only + # when no other subnet still sits inside it, so we never shrink space in use. + if ($addedVnetPrefix) { + $vnetInfoJson = (& az network vnet show --resource-group $hubRg --name $vnetName --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' -o json 2>$null) + $removeIndex = $null + if ($vnetInfoJson) { + $vnetInfo = $vnetInfoJson | ConvertFrom-Json + $remainingPrefixes = @($vnetInfo.addressPrefixes) + $remainingSubnets = @($vnetInfo.subnetPrefixes) + $inUse = $false + foreach ($s in $remainingSubnets) { + if ($s -and (Test-CidrContains -ParentCidr $addedVnetPrefix -ChildCidr $s)) { $inUse = $true; break } + } + if ($inUse) { + Write-Note "Address prefix '$addedVnetPrefix' still has subnets; leaving it on the VNet." + } else { + for ($i = 0; $i -lt $remainingPrefixes.Count; $i++) { + if ($remainingPrefixes[$i] -eq $addedVnetPrefix) { $removeIndex = $i; break } + } + if ($null -eq $removeIndex) { + Write-Note "Address prefix '$addedVnetPrefix' already absent from VNet (continuing)." + } else { + & az network vnet update --resource-group $hubRg --name $vnetName --remove addressSpace.addressPrefixes $removeIndex --only-show-errors -o none *> $null + if ($LASTEXITCODE -eq 0) { Write-Note "Removed added VNet address space '$addedVnetPrefix'" } else { Write-Warning "Could not remove added VNet address space '$addedVnetPrefix' (continuing). Remove it manually if no longer needed." } + } + } + } + } + } else { + Write-Warning "Could not delete subnet '$subnetName'. It may still be in use by the ACA environment; rerun this script once '$mcpRg' has finished deleting." + } + } + } else { + Write-Step "Subnet '$subnetName' already absent (continuing)." + } +} + +if (-not $KeepAzdEnv) { + Write-Step 'Clearing HR_MCP_* values from the active azd environment' + $envListJson = (& azd env list -o json 2>$null) + $dotenvPath = $null + if ($LASTEXITCODE -eq 0 -and $envListJson) { + try { + $envs = $envListJson | ConvertFrom-Json + foreach ($e in $envs) { + $isDefault = $e.PSObject.Properties | Where-Object { $_.Name -ieq 'IsDefault' } | Select-Object -First 1 + if ($isDefault -and $isDefault.Value) { + $pathProp = $e.PSObject.Properties | Where-Object { $_.Name -ieq 'DotEnvPath' } | Select-Object -First 1 + if ($pathProp) { $dotenvPath = [string]$pathProp.Value } + break + } + } + } catch { $dotenvPath = $null } + } + if ($dotenvPath -and (Test-Path $dotenvPath)) { + $kept = Get-Content -LiteralPath $dotenvPath | Where-Object { $_ -notmatch '^HR_MCP_[A-Z0-9_]+=' } + Set-Content -LiteralPath $dotenvPath -Value $kept + Write-Note "Removed HR_MCP_* entries from $dotenvPath" + } else { + Write-Warning 'Could not locate the active azd .env file; HR_MCP_* values were left in place. Select an azd environment or clear them manually.' + } +} + +Write-Step 'HR MCP teardown complete.' +Write-Host 'Re-run deploy-hr-mcp then publish-hr-mcp-apim to recreate a clean deployment.' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.sh new file mode 100755 index 000000000..ebe91f66d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/teardown-hr-mcp.sh @@ -0,0 +1,299 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Tear down the HR MCP deployment created by deploy-hr-mcp.sh and published by +# publish-hr-mcp-apim.sh. Every step is best-effort and idempotent: objects that +# were already removed (for example, deleted by hand in the portal) are skipped +# with a notice instead of failing the run. Finally, all HR_MCP_* values are +# cleared from the active azd environment so a stale backend URL can never leak +# into a later deploy/publish cycle. + +log() { printf '\n==> %s\n' "$1"; } +note() { printf ' - %s\n' "$1"; } +warn() { printf 'Warning: %s\n' "$1" >&2; } +fail() { printf 'Error: %s\n' "$1" >&2; exit 1; } +require_command() { command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"; } +azd_get_optional() { local v; if v="$(azd env get-value "$1" 2>/dev/null)"; then printf '%s' "$v"; fi; } +first_non_empty() { local v; for v in "$@"; do [[ -n "${v:-}" ]] && { printf '%s' "$v"; return 0; }; done; return 0; } + +assume_yes=false +no_wait=false +keep_azd_env=false +skip_apim=false +skip_dns=false +skip_rg=false +skip_subnet=false + +usage() { + cat <<'EOF' +Usage: teardown-hr-mcp.sh [options] + +Removes the HR MCP infrastructure resource group, its APIM publication objects +(subscription, product, API, backend), and the per-deployment private DNS zone, +then clears HR_MCP_* values from the active azd environment. If deploy-hr-mcp +expanded the Citadel hub VNet (HR_MCP_ACA_ADDED_VNET_PREFIX), that added address +space is removed after the dedicated subnet is deleted. + +Options: + -y, --yes Do not prompt for confirmation. + --no-wait Start the resource group deletion without waiting for it to finish. + --keep-azd-env Leave HR_MCP_* values in the azd environment. + --skip-apim Do not touch the APIM publication objects. + --skip-dns Do not touch the private DNS zone. + --skip-rg Do not delete the HR MCP infrastructure resource group. + --skip-subnet Do not delete the dedicated ACA subnet (and added VNet address space). + -h, --help Show this help. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -y|--yes) assume_yes=true ;; + --no-wait) no_wait=true ;; + --keep-azd-env) keep_azd_env=true ;; + --skip-apim) skip_apim=true ;; + --skip-dns) skip_dns=true ;; + --skip-rg) skip_rg=true ;; + --skip-subnet) skip_subnet=true ;; + -h|--help) usage; exit 0 ;; + *) fail "Unknown option: $1" ;; + esac + shift +done + +require_command az +require_command azd +if ! az account show >/dev/null 2>&1; then + fail 'Azure CLI is not logged in. Run az login, then rerun this script.' +fi + +subscription_id="$(first_non_empty "${AZURE_SUBSCRIPTION_ID:-}" "$(azd_get_optional AZURE_SUBSCRIPTION_ID)" "$(az account show --query id -o tsv 2>/dev/null)")" +if [[ -n "$subscription_id" ]]; then + az account set --subscription "$subscription_id" >/dev/null 2>&1 || true +fi + +mcp_rg="$(first_non_empty "${HR_MCP_RESOURCE_GROUP:-}" "$(azd_get_optional HR_MCP_RESOURCE_GROUP)")" +apim_name="$(first_non_empty "${HR_MCP_APIM_NAME:-}" "$(azd_get_optional HR_MCP_APIM_NAME)" "${APIM_NAME:-}" "$(azd_get_optional APIM_NAME)")" +apim_rg="$(first_non_empty "${HR_MCP_APIM_RESOURCE_GROUP:-}" "$(azd_get_optional HR_MCP_APIM_RESOURCE_GROUP)" "$(azd_get_optional AZURE_RESOURCE_GROUP)")" +api_name="$(first_non_empty "${HR_MCP_APIM_API_NAME:-}" "$(azd_get_optional HR_MCP_APIM_API_NAME)" 'hr-mcp-api')" +backend_name="$(first_non_empty "${HR_MCP_APIM_BACKEND_NAME:-}" "$(azd_get_optional HR_MCP_APIM_BACKEND_NAME)" 'hr-mcp-aca-backend')" +product_id="$(first_non_empty "${HR_MCP_APIM_PRODUCT_ID:-}" "$(azd_get_optional HR_MCP_APIM_PRODUCT_ID)" 'MCP-HR-Tools-DEV')" +subscription_name="$(first_non_empty "${HR_MCP_APIM_SUBSCRIPTION_NAME:-}" "$(azd_get_optional HR_MCP_APIM_SUBSCRIPTION_NAME)" 'MCP-HR-Tools-DEV-SUB-01')" +dns_rg="$(first_non_empty "${HR_MCP_PRIVATE_DNS_RESOURCE_GROUP:-}" "$(azd_get_optional HR_MCP_PRIVATE_DNS_RESOURCE_GROUP)")" +dns_zone="$(first_non_empty "${HR_MCP_PRIVATE_DNS_ZONE:-}" "$(azd_get_optional HR_MCP_PRIVATE_DNS_ZONE)")" +dns_link_name='lnk-hr-mcp-aca' + +# Dedicated ACA subnet that deploy-hr-mcp creates inside the shared Citadel hub VNet. It is +# delegated to Microsoft.App/environments, so it can only be removed after the resource group +# (and its ACA environment) is gone. +subnet_id="$(first_non_empty "${HR_MCP_ACA_SUBNET_ID:-}" "$(azd_get_optional HR_MCP_ACA_SUBNET_ID)")" +hub_rg="$(first_non_empty "${HR_MCP_CITADEL_RESOURCE_GROUP:-}" "$(azd_get_optional HR_MCP_CITADEL_RESOURCE_GROUP)")" +vnet_name="$(first_non_empty "${HR_MCP_CITADEL_VNET_NAME:-}" "$(azd_get_optional HR_MCP_CITADEL_VNET_NAME)")" +subnet_name="$(first_non_empty "${HR_MCP_ACA_SUBNET_NAME:-}" "$(azd_get_optional HR_MCP_ACA_SUBNET_NAME)")" +# Address prefix deploy-hr-mcp added to the hub VNet when no free subnet was available. +# It is removed only after the dedicated subnet is gone, leaving the original space intact. +added_vnet_prefix="$(first_non_empty "${HR_MCP_ACA_ADDED_VNET_PREFIX:-}" "$(azd_get_optional HR_MCP_ACA_ADDED_VNET_PREFIX)")" +if [[ -n "$subnet_id" ]]; then + [[ -n "$hub_rg" ]] || hub_rg="$(printf '%s' "$subnet_id" | sed -E 's#.*/resourceGroups/([^/]+)/.*#\1#')" + [[ -n "$vnet_name" ]] || vnet_name="$(printf '%s' "$subnet_id" | sed -E 's#.*/virtualNetworks/([^/]+)/.*#\1#')" + [[ -n "$subnet_name" ]] || subnet_name="$(basename "$subnet_id")" +fi + +log 'HR MCP teardown plan' +note "Subscription: ${subscription_id:-}" +if [[ "$skip_apim" == true ]]; then + note 'APIM publication: SKIPPED (--skip-apim)' +elif [[ -n "$apim_name" && -n "$apim_rg" ]]; then + note "APIM service: $apim_name ($apim_rg)" + note " - subscription: $subscription_name" + note " - product: $product_id" + note " - API: $api_name" + note " - backend: $backend_name" +else + note 'APIM publication: SKIPPED (APIM name/resource group not resolved)' +fi +if [[ "$skip_dns" == true ]]; then + note 'Private DNS zone: SKIPPED (--skip-dns)' +elif [[ -n "$dns_rg" && -n "$dns_zone" ]]; then + note "Private DNS zone: $dns_zone ($dns_rg)" +else + note 'Private DNS zone: SKIPPED (zone/resource group not resolved)' +fi +if [[ "$skip_rg" == true ]]; then + note 'Infrastructure RG: SKIPPED (--skip-rg)' +elif [[ -n "$mcp_rg" ]]; then + note "Infrastructure RG: $mcp_rg (DELETE)" +else + note 'Infrastructure RG: SKIPPED (HR_MCP_RESOURCE_GROUP not resolved)' +fi +if [[ "$skip_subnet" == true ]]; then + note 'ACA hub subnet: SKIPPED (--skip-subnet)' +elif [[ -n "$hub_rg" && -n "$vnet_name" && -n "$subnet_name" ]]; then + note "ACA hub subnet: $subnet_name in $vnet_name ($hub_rg)" + if [[ -n "$added_vnet_prefix" ]]; then + note "Added VNet address space: $added_vnet_prefix (REMOVE after subnet)" + fi +else + note 'ACA hub subnet: SKIPPED (subnet/VNet not resolved)' +fi +if [[ "$keep_azd_env" == true ]]; then + note 'azd HR_MCP_* values: KEPT (--keep-azd-env)' +else + note 'azd HR_MCP_* values: CLEARED' +fi + +if [[ "$assume_yes" != true ]]; then + printf '\nProceed with teardown? Type "yes" to continue: ' + read -r reply + if [[ "$reply" != "yes" ]]; then + printf '\nTeardown cancelled. No changes were made.\n' + exit 0 + fi +fi + +apim_delete() { + # Best-effort DELETE of an APIM child resource by relative path. A 404 (already + # gone) or any other failure is reported and skipped rather than aborting. + local rel="$1" label="$2" extra_query="${3:-}" + local url="https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${apim_rg}/providers/Microsoft.ApiManagement/service/${apim_name}/${rel}?api-version=2024-06-01-preview${extra_query}" + if az rest --method delete --url "$url" --only-show-errors -o none >/dev/null 2>&1; then + note "Deleted APIM $label" + else + note "APIM $label already absent or not deletable (continuing)" + fi +} + +if [[ "$skip_apim" != true && -n "$apim_name" && -n "$apim_rg" && -n "$subscription_id" ]]; then + if az apim show --name "$apim_name" --resource-group "$apim_rg" >/dev/null 2>&1; then + log "Removing HR MCP publication objects from APIM: $apim_name" + apim_delete "subscriptions/${subscription_name}" "subscription '$subscription_name'" + apim_delete "products/${product_id}" "product '$product_id'" '&deleteSubscriptions=true' + apim_delete "apis/${api_name}" "API '$api_name'" + apim_delete "backends/${backend_name}" "backend '$backend_name'" + else + log "APIM service '$apim_name' not found in '$apim_rg'; skipping APIM cleanup." + fi +fi + +if [[ "$skip_dns" != true && -n "$dns_rg" && -n "$dns_zone" ]]; then + if az network private-dns zone show --resource-group "$dns_rg" --name "$dns_zone" >/dev/null 2>&1; then + log "Removing private DNS zone: $dns_zone" + if az network private-dns link vnet show --resource-group "$dns_rg" --zone-name "$dns_zone" --name "$dns_link_name" >/dev/null 2>&1; then + az network private-dns link vnet delete --resource-group "$dns_rg" --zone-name "$dns_zone" --name "$dns_link_name" --yes --only-show-errors -o none >/dev/null 2>&1 \ + && note "Deleted vnet link '$dns_link_name'" || note "Could not delete vnet link '$dns_link_name' (continuing)" + else + note "Vnet link '$dns_link_name' already absent (continuing)" + fi + if az network private-dns zone delete --resource-group "$dns_rg" --name "$dns_zone" --yes --only-show-errors -o none >/dev/null 2>&1; then + note "Deleted private DNS zone '$dns_zone'" + else + note "Could not delete private DNS zone '$dns_zone' (continuing)" + fi + else + log "Private DNS zone '$dns_zone' already absent (continuing)." + fi +fi + +if [[ "$skip_rg" != true && -n "$mcp_rg" ]]; then + if az group show --name "$mcp_rg" >/dev/null 2>&1; then + if [[ "$no_wait" == true ]]; then + log "Deleting resource group (no wait): $mcp_rg" + az group delete --name "$mcp_rg" --yes --no-wait -o none + note 'Deletion started in the background.' + rg_delete_async=true + else + log "Deleting resource group (this can take several minutes): $mcp_rg" + az group delete --name "$mcp_rg" --yes -o none + note 'Resource group deleted.' + fi + else + log "Resource group '$mcp_rg' already absent (continuing)." + fi +fi + +if [[ "$skip_subnet" != true && -n "$hub_rg" && -n "$vnet_name" && -n "$subnet_name" ]]; then + if az network vnet subnet show --resource-group "$hub_rg" --vnet-name "$vnet_name" --name "$subnet_name" >/dev/null 2>&1; then + if [[ "${rg_delete_async:-false}" == true ]]; then + log "Deferring ACA subnet '$subnet_name' deletion" + note 'The subnet is still delegated to the ACA environment while the resource group deletes in the background.' + note "Rerun: teardown-hr-mcp.sh --skip-apim --skip-dns --skip-rg --keep-azd-env (after '$mcp_rg' is gone)" + else + log "Deleting ACA subnet '$subnet_name' from Citadel hub VNet '$vnet_name'" + if az network vnet subnet delete --resource-group "$hub_rg" --vnet-name "$vnet_name" --name "$subnet_name" --only-show-errors -o none >/dev/null 2>&1; then + note "Deleted subnet '$subnet_name'" + # Subnet is gone; now remove the address prefix deploy added (if any), but only + # when no other subnet still sits inside it, so we never shrink space in use. + if [[ -n "$added_vnet_prefix" ]]; then + vnet_json="$(az network vnet show --resource-group "$hub_rg" --name "$vnet_name" \ + --query '{addressPrefixes:addressSpace.addressPrefixes,subnetPrefixes:subnets[].addressPrefix}' -o json 2>/dev/null || true)" + remove_index="$(python3 - "$added_vnet_prefix" "$vnet_json" <<'PY' +import ipaddress, json, sys +target = ipaddress.ip_network(sys.argv[1], strict=False) +data = json.loads(sys.argv[2] or "{}") +prefixes = [p for p in (data.get("addressPrefixes") or []) if p] +subnets = [p for p in (data.get("subnetPrefixes") or []) if p] +# Refuse to remove if any remaining subnet still lives inside the added prefix. +for s in subnets: + if ipaddress.ip_network(s, strict=False).subnet_of(target): + print("INUSE"); raise SystemExit(0) +for i, p in enumerate(prefixes): + if ipaddress.ip_network(p, strict=False) == target: + print(i); raise SystemExit(0) +print("ABSENT") +PY +)" + if [[ "$remove_index" == "INUSE" ]]; then + note "Address prefix '$added_vnet_prefix' still has subnets; leaving it on the VNet." + elif [[ "$remove_index" == "ABSENT" || -z "$remove_index" ]]; then + note "Address prefix '$added_vnet_prefix' already absent from VNet (continuing)." + elif az network vnet update --resource-group "$hub_rg" --name "$vnet_name" \ + --remove "addressSpace.addressPrefixes" "$remove_index" --only-show-errors -o none >/dev/null 2>&1; then + note "Removed added VNet address space '$added_vnet_prefix'" + else + warn "Could not remove added VNet address space '$added_vnet_prefix' (continuing). Remove it manually if no longer needed." + fi + fi + else + warn "Could not delete subnet '$subnet_name'. It may still be in use by the ACA environment; rerun this script once '$mcp_rg' has finished deleting." + fi + fi + else + log "Subnet '$subnet_name' already absent (continuing)." + fi +fi + +if [[ "$keep_azd_env" != true ]]; then + log 'Clearing HR_MCP_* values from the active azd environment' + dotenv_path="$(azd env list -o json 2>/dev/null | python3 -c ' +import json, sys +def get(d, *keys): + for k in keys: + for kk in d: + if kk.lower() == k.lower(): + return d[kk] + return "" +try: + envs = json.load(sys.stdin) +except Exception: + sys.exit(0) +for e in envs: + if get(e, "IsDefault"): + print(get(e, "DotEnvPath")) + break +' 2>/dev/null || true)" + if [[ -n "$dotenv_path" && -f "$dotenv_path" ]]; then + tmp="${dotenv_path}.teardown.tmp" + if grep -v -E '^HR_MCP_[A-Z0-9_]+=' "$dotenv_path" > "$tmp"; then + mv "$tmp" "$dotenv_path" + note "Removed HR_MCP_* entries from $dotenv_path" + else + rm -f "$tmp" + note "No HR_MCP_* entries found in $dotenv_path" + fi + else + warn 'Could not locate the active azd .env file; HR_MCP_* values were left in place. Select an azd environment or clear them manually.' + fi +fi + +log 'HR MCP teardown complete.' +printf 'Re-run deploy-hr-mcp then publish-hr-mcp-apim to recreate a clean deployment.\n' diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-apim.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-apim.py new file mode 100755 index 000000000..b16f6e15c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-apim.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import time +import urllib.error +import urllib.parse +import urllib.request +from dataclasses import dataclass +from typing import Any + + +EXPECTED_TOOLS = { + "search_employees", + "get_employee_profile", + "recommend_learning_path", + "submit_pto_request", + "update_employee_skills", +} +DEFAULT_SUBSCRIPTION_NAME = "MCP-HR-Tools-DEV-SUB-01" + + +@dataclass +class HttpResponse: + status: int + body: str + headers: dict[str, str] + + +class SmokeFailure(Exception): + pass + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Smoke test the HR MCP APIM /mcp publication and access contract.") + parser.add_argument("--url", help="APIM HR MCP endpoint URL. Accepts either the API base URL or the /mcp URL.") + parser.add_argument("--token", help="Bearer token for APIM /mcp calls. The token is never printed.") + parser.add_argument("--subscription-key", help="APIM subscription key. The key is never printed.") + parser.add_argument("--timeout", type=float, default=20.0, help="HTTP timeout in seconds. Default: 20.") + parser.add_argument("--skip-rate-limit", action="store_true", help="Skip the APIM tools/call rate-limit check.") + parser.add_argument("--skip-log-query", action="store_true", help="Skip the optional Azure Monitor log query check.") + return parser.parse_args() + + +def run_value_command(command: list[str]) -> str | None: + try: + completed = subprocess.run(command, check=False, capture_output=True, text=True) + except FileNotFoundError: + return None + if completed.returncode != 0: + return None + value = completed.stdout.strip() + return value or None + + +def azd_value(name: str) -> str | None: + return run_value_command(["azd", "env", "get-value", name]) + + +def first_value(*values: str | None) -> str | None: + for value in values: + if value and value.strip(): + return value.strip() + return None + + +def resolve_mcp_url(args: argparse.Namespace) -> str: + url = first_value(args.url, os.getenv("HR_MCP_APIM_MCP_URL"), azd_value("HR_MCP_APIM_MCP_URL")) + if not url: + raise SmokeFailure( + "No APIM MCP URL found. Pass --url, set HR_MCP_APIM_MCP_URL, or select an azd environment " + "containing HR_MCP_APIM_MCP_URL." + ) + parsed = urllib.parse.urlparse(url) + if not parsed.scheme or not parsed.netloc: + raise SmokeFailure(f"Invalid APIM MCP URL: {url!r}") + if parsed.path.rstrip("/").endswith("/mcp"): + return url.rstrip("/") + return urllib.parse.urljoin(url.rstrip("/") + "/", "mcp") + + +def redact_url(url: str) -> str: + parsed = urllib.parse.urlparse(url) + return urllib.parse.urlunparse(parsed._replace(query="", fragment="")) + + +def acquire_token(args: argparse.Namespace) -> str: + token = first_value(args.token, os.getenv("HR_MCP_ACCESS_TOKEN")) + if token: + return token + + scope = first_value(os.getenv("HR_MCP_SCOPE"), azd_value("HR_MCP_SCOPE")) + tenant_id = first_value(os.getenv("HR_MCP_TENANT_ID"), azd_value("HR_MCP_TENANT_ID")) + if not scope: + raise SmokeFailure( + "No bearer token or HR_MCP_SCOPE available. Pass --token, set HR_MCP_ACCESS_TOKEN, " + "or make HR_MCP_SCOPE available in env/azd and run az login." + ) + + command = ["az", "account", "get-access-token", "--scope", scope, "--query", "accessToken", "-o", "tsv"] + if tenant_id: + command[3:3] = ["--tenant", tenant_id] + token = run_value_command(command) + if not token: + raise SmokeFailure( + "Azure CLI token acquisition failed. Run az login, verify HR_MCP_SCOPE/HR_MCP_TENANT_ID, " + "or pass --token/set HR_MCP_ACCESS_TOKEN." + ) + return token + + +def resolve_subscription_key(args: argparse.Namespace) -> str: + key = first_value(args.subscription_key, os.getenv("HR_MCP_APIM_SUBSCRIPTION_KEY")) + if key: + return key + + apim_name = first_value(os.getenv("HR_MCP_APIM_NAME"), azd_value("HR_MCP_APIM_NAME")) + apim_rg = first_value( + os.getenv("HR_MCP_APIM_RESOURCE_GROUP"), + azd_value("HR_MCP_APIM_RESOURCE_GROUP"), + os.getenv("AZURE_RESOURCE_GROUP"), + azd_value("AZURE_RESOURCE_GROUP"), + ) + subscription_name = first_value( + os.getenv("HR_MCP_APIM_SUBSCRIPTION_NAME"), + azd_value("HR_MCP_APIM_SUBSCRIPTION_NAME"), + DEFAULT_SUBSCRIPTION_NAME, + ) + if not apim_name or not apim_rg or not subscription_name: + raise SmokeFailure( + "No APIM subscription key available. Pass --subscription-key, set HR_MCP_APIM_SUBSCRIPTION_KEY, " + "or provide HR_MCP_APIM_NAME, HR_MCP_APIM_RESOURCE_GROUP, and HR_MCP_APIM_SUBSCRIPTION_NAME " + "so Azure CLI can retrieve it." + ) + + key = run_value_command( + [ + "az", + "apim", + "subscription", + "show", + "--resource-group", + apim_rg, + "--service-name", + apim_name, + "--sid", + subscription_name, + "--query", + "primaryKey", + "-o", + "tsv", + ] + ) + if key: + return key + + subscription_id = first_value(os.getenv("AZURE_SUBSCRIPTION_ID"), azd_value("AZURE_SUBSCRIPTION_ID"), run_value_command(["az", "account", "show", "--query", "id", "-o", "tsv"])) + if subscription_id: + encoded_subscription_id = urllib.parse.quote(subscription_id, safe="") + encoded_rg = urllib.parse.quote(apim_rg, safe="") + encoded_apim = urllib.parse.quote(apim_name, safe="") + encoded_sid = urllib.parse.quote(subscription_name, safe="") + url = ( + "https://management.azure.com/subscriptions/" + f"{encoded_subscription_id}/resourceGroups/{encoded_rg}" + f"/providers/Microsoft.ApiManagement/service/{encoded_apim}/subscriptions/{encoded_sid}/listSecrets" + "?api-version=2024-06-01-preview" + ) + key = run_value_command(["az", "rest", "--method", "post", "--url", url, "--query", "primaryKey", "-o", "tsv"]) + if key: + return key + + raise SmokeFailure( + "Could not retrieve the APIM subscription key with Azure CLI. Pass --subscription-key or set " + "HR_MCP_APIM_SUBSCRIPTION_KEY. Retrieval command: az apim subscription show --resource-group " + " --service-name --sid --query primaryKey -o tsv" + ) + + +def http_request( + method: str, + url: str, + *, + payload: dict[str, Any] | None = None, + token: str | None = None, + subscription_key: str | None = None, + timeout: float, +) -> HttpResponse: + body = None if payload is None else json.dumps(payload).encode("utf-8") + headers = {"Accept": "application/json"} + if payload is not None: + headers["Content-Type"] = "application/json" + if token: + headers["Authorization"] = f"Bearer {token}" + if subscription_key: + headers["Ocp-Apim-Subscription-Key"] = subscription_key + request = urllib.request.Request(url, data=body, headers=headers, method=method) + try: + with urllib.request.urlopen(request, timeout=timeout) as response: + text = response.read().decode("utf-8", errors="replace") + return HttpResponse(response.status, text, dict(response.headers.items())) + except urllib.error.HTTPError as exc: + text = exc.read().decode("utf-8", errors="replace") + return HttpResponse(exc.code, text, dict(exc.headers.items())) + except urllib.error.URLError as exc: + raise SmokeFailure(f"{method} {redact_url(url)} failed: {exc.reason}") from exc + except TimeoutError as exc: + raise SmokeFailure(f"{method} {redact_url(url)} timed out after {timeout} seconds.") from exc + + +def parse_json(response: HttpResponse, label: str) -> Any: + try: + return json.loads(response.body) + except json.JSONDecodeError as exc: + snippet = response.body[:240].replace("\n", " ") + raise SmokeFailure(f"{label} returned non-JSON response: {snippet}") from exc + + +def expect_http_200(response: HttpResponse, label: str) -> Any: + if response.status != 200: + snippet = response.body[:240].replace("\n", " ") + raise SmokeFailure(f"{label} returned HTTP {response.status}, expected 200. Body: {snippet}") + return parse_json(response, label) + + +def assert_no_error_rpc(payload: Any, label: str) -> dict[str, Any]: + if not isinstance(payload, dict): + raise SmokeFailure(f"{label} returned a non-object JSON-RPC payload.") + if "error" in payload: + raise SmokeFailure(f"{label} returned JSON-RPC error: {payload['error']}") + result = payload.get("result") + if not isinstance(result, dict): + raise SmokeFailure(f"{label} returned no JSON-RPC result object.") + return result + + +def rpc_response( + mcp_url: str, + method: str, + params: dict[str, Any], + request_id: int | str, + token: str | None, + subscription_key: str | None, + timeout: float, +) -> HttpResponse: + return http_request( + "POST", + mcp_url, + payload={"jsonrpc": "2.0", "id": request_id, "method": method, "params": params}, + token=token, + subscription_key=subscription_key, + timeout=timeout, + ) + + +def rpc_call( + mcp_url: str, + method: str, + params: dict[str, Any], + request_id: int | str, + token: str, + subscription_key: str, + timeout: float, +) -> dict[str, Any]: + return expect_http_200(rpc_response(mcp_url, method, params, request_id, token, subscription_key, timeout), method) + + +def decode_tool_text(result: dict[str, Any], label: str) -> dict[str, Any]: + content = result.get("content") + if not isinstance(content, list) or not content: + raise SmokeFailure(f"{label} returned no MCP content.") + first = content[0] + if not isinstance(first, dict) or first.get("type") != "text" or not isinstance(first.get("text"), str): + raise SmokeFailure(f"{label} returned malformed MCP text content.") + try: + payload = json.loads(first["text"]) + except json.JSONDecodeError as exc: + raise SmokeFailure(f"{label} returned text content that is not JSON.") from exc + if result.get("isError") is True or payload.get("ok") is False: + raise SmokeFailure(f"{label} returned tool error payload: {payload}") + return payload + + +def expect_rejected(response: HttpResponse, label: str) -> None: + if response.status not in {401, 403}: + snippet = response.body[:240].replace("\n", " ") + raise SmokeFailure(f"{label} returned HTTP {response.status}; expected 401 or 403. Body: {snippet}") + print(f"OK {label} rejected") + + +def check_negative_credentials(mcp_url: str, token: str, subscription_key: str, timeout: float) -> None: + expect_rejected( + rpc_response(mcp_url, "initialize", {}, "missing-bearer", None, subscription_key, timeout), + "missing bearer token", + ) + expect_rejected( + rpc_response(mcp_url, "initialize", {}, "missing-subscription-key", token, None, timeout), + "missing APIM subscription key", + ) + + +def check_initialize_and_tools(mcp_url: str, token: str, subscription_key: str, timeout: float) -> None: + initialize = assert_no_error_rpc(rpc_call(mcp_url, "initialize", {}, 1, token, subscription_key, timeout), "initialize") + if "protocolVersion" not in initialize or initialize.get("serverInfo", {}).get("name") != "contoso-hr-mcp": + raise SmokeFailure(f"initialize returned unexpected payload: {initialize}") + print("OK initialize") + + listed = assert_no_error_rpc(rpc_call(mcp_url, "tools/list", {}, 2, token, subscription_key, timeout), "tools/list") + tools = listed.get("tools") + if not isinstance(tools, list): + raise SmokeFailure("tools/list did not return a tools array.") + tool_names = {tool.get("name") for tool in tools if isinstance(tool, dict)} + if tool_names != EXPECTED_TOOLS: + raise SmokeFailure(f"tools/list returned {sorted(tool_names)}, expected {sorted(EXPECTED_TOOLS)}.") + print("OK tools/list with exactly the expected tools") + + +def check_read_write_tools(mcp_url: str, token: str, subscription_key: str, timeout: float) -> None: + read_result = assert_no_error_rpc( + rpc_call( + mcp_url, + "tools/call", + {"name": "get_employee_profile", "arguments": {"employee_id": "E1001"}}, + 3, + token, + subscription_key, + timeout, + ), + "read tools/call", + ) + read_payload = decode_tool_text(read_result, "read tools/call") + if read_payload.get("employee_id") != "E1001" or "pto" not in read_payload: + raise SmokeFailure(f"read tools/call returned unexpected employee profile: {read_payload}") + print("OK read tools/call") + + write_result = assert_no_error_rpc( + rpc_call( + mcp_url, + "tools/call", + { + "name": "update_employee_skills", + "arguments": { + "employee_id": "E1001", + "skills": ["apim-smoke-test-validation"], + "evidence_note": "APIM MCP smoke test validation evidence.", + }, + }, + 4, + token, + subscription_key, + timeout, + ), + "write tools/call", + ) + write_payload = decode_tool_text(write_result, "write tools/call") + update = write_payload.get("skill_update", {}) + if update.get("employee_id") != "E1001" or "apim-smoke-test-validation" not in write_payload.get("current_skills", []): + raise SmokeFailure(f"write tools/call returned unexpected skill update: {write_payload}") + print("OK write tools/call") + + +def check_non_tool_methods_not_counted(mcp_url: str, token: str, subscription_key: str, timeout: float) -> None: + for i in range(6): + method = "tools/list" if i % 2 else "initialize" + response = rpc_response(mcp_url, method, {}, f"non-tool-{i}", token, subscription_key, timeout) + if response.status == 429: + raise SmokeFailure( + f"{method} returned HTTP 429 during non-tool accounting check. The APIM rate limit should apply only to tools/call." + ) + payload = expect_http_200(response, f"non-tool {method}") + assert_no_error_rpc(payload, f"non-tool {method}") + print("OK non-tool JSON-RPC methods were not rate-limited") + + +def check_rate_limit(mcp_url: str, token: str, subscription_key: str, timeout: float) -> None: + deadline = time.monotonic() + 30 + saw_429 = False + successful_tool_calls = 0 + for i in range(6): + if time.monotonic() >= deadline: + raise SmokeFailure("Rate-limit check exceeded 30 seconds before sending more than 5 tools/call requests.") + response = rpc_response( + mcp_url, + "tools/call", + {"name": "get_employee_profile", "arguments": {"employee_id": "E1001"}}, + f"rate-{i}", + token, + subscription_key, + timeout, + ) + if response.status == 429: + saw_429 = True + retry_after = response.headers.get("Retry-After") + detail = f" Retry-After={retry_after}." if retry_after else "" + print(f"OK APIM tools/call rate limit returned HTTP 429 on excess call.{detail}") + break + payload = expect_http_200(response, f"rate-limit tools/call {i + 1}") + assert_no_error_rpc(payload, f"rate-limit tools/call {i + 1}") + successful_tool_calls += 1 + + if not saw_429: + raise SmokeFailure( + f"APIM tools/call rate limit did not return HTTP 429 after {successful_tool_calls} rapid tools/call requests. " + "Confirm the HR MCP APIM product policy is published and has rate-limit-by-key calls=\"5\" renewal-period=\"30\"." + ) + + +def resolve_workspace_id() -> str | None: + explicit = first_value( + os.getenv("HR_MCP_LOG_ANALYTICS_WORKSPACE_ID"), + azd_value("HR_MCP_LOG_ANALYTICS_WORKSPACE_ID"), + os.getenv("LOG_ANALYTICS_WORKSPACE_ID"), + ) + if explicit: + return explicit + + workspace_name = first_value(os.getenv("HR_MCP_LOG_ANALYTICS_NAME"), azd_value("HR_MCP_LOG_ANALYTICS_NAME")) + resource_group = first_value( + os.getenv("HR_MCP_RESOURCE_GROUP"), + azd_value("HR_MCP_RESOURCE_GROUP"), + os.getenv("AZURE_RESOURCE_GROUP"), + azd_value("AZURE_RESOURCE_GROUP"), + ) + if not workspace_name or not resource_group: + return None + return run_value_command( + [ + "az", + "monitor", + "log-analytics", + "workspace", + "show", + "--resource-group", + resource_group, + "--workspace-name", + workspace_name, + "--query", + "customerId", + "-o", + "tsv", + ] + ) + + +def check_apim_logs(skip: bool) -> None: + if skip: + print("APIM log query skipped by --skip-log-query.") + return + + workspace_id = resolve_workspace_id() + if not workspace_id: + print( + "APIM log query skipped: set HR_MCP_LOG_ANALYTICS_WORKSPACE_ID, or set " + "HR_MCP_LOG_ANALYTICS_NAME plus HR_MCP_RESOURCE_GROUP, then rerun without --skip-log-query. " + "If APIM diagnostics are configured, query recent gateway traces for source 'hr-mcp-apim'." + ) + return + + query = ( + "AzureDiagnostics | where TimeGenerated > ago(30m) " + "| where Category has 'Gateway' " + "| where requestUrl_s has '/hr-mcp/mcp' or Message has 'hr-mcp-apim' or trace_s has 'hr-mcp-apim' " + "| project TimeGenerated, Category, OperationName, requestUrl_s, responseCode_d, Message " + "| take 5" + ) + result = run_value_command(["az", "monitor", "log-analytics", "query", "--workspace", workspace_id, "--analytics-query", query, "-o", "json"]) + if not result: + print( + "APIM log query did not return results. This is non-fatal; verify APIM diagnostics route gateway logs/traces " + "to the Log Analytics workspace, then query for source 'hr-mcp-apim' or the /hr-mcp/mcp request URL." + ) + return + try: + rows = json.loads(result) + except json.JSONDecodeError: + print("APIM log query ran but returned non-JSON output. Inspect Azure Monitor manually for source 'hr-mcp-apim'.") + return + if rows: + print("OK APIM log query returned recent gateway data") + else: + print("APIM log query returned no recent rows. This is non-fatal; confirm APIM diagnostics are enabled.") + + +def run_smoke(args: argparse.Namespace) -> None: + mcp_url = resolve_mcp_url(args) + token = acquire_token(args) + subscription_key = resolve_subscription_key(args) + + print(f"Testing HR MCP APIM endpoint: {redact_url(mcp_url)}") + check_negative_credentials(mcp_url, token, subscription_key, args.timeout) + check_initialize_and_tools(mcp_url, token, subscription_key, args.timeout) + check_read_write_tools(mcp_url, token, subscription_key, args.timeout) + if args.skip_rate_limit: + print("APIM rate-limit check skipped by --skip-rate-limit.") + else: + check_non_tool_methods_not_counted(mcp_url, token, subscription_key, args.timeout) + check_rate_limit(mcp_url, token, subscription_key, args.timeout) + check_apim_logs(args.skip_log_query) + + +def main() -> int: + args = parse_args() + try: + run_smoke(args) + except SmokeFailure as exc: + print(f"FAIL: {exc}", file=sys.stderr) + return 1 + print("HR MCP APIM smoke test passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-direct.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-direct.py new file mode 100755 index 000000000..1ef55672c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/scripts/test-hr-mcp-direct.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import urllib.error +import urllib.parse +import urllib.request +from dataclasses import dataclass +from typing import Any + + +EXPECTED_TOOLS = { + "search_employees", + "get_employee_profile", + "recommend_learning_path", + "submit_pto_request", + "update_employee_skills", +} +DEFAULT_LOCAL_MCP_URL = "http://localhost:8080/mcp" + + +@dataclass +class HttpResponse: + status: int + body: str + headers: dict[str, str] + + +class SmokeFailure(Exception): + pass + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Smoke test the HR MCP direct /mcp endpoint.") + parser.add_argument("--url", help="HR MCP endpoint URL. Accepts either the base URL or the /mcp URL.") + parser.add_argument("--local", action="store_true", help=f"Use {DEFAULT_LOCAL_MCP_URL} when --url is omitted.") + parser.add_argument("--token", help="Bearer token for authenticated /mcp calls. The token is never printed.") + parser.add_argument( + "--allow-no-auth", + action="store_true", + help="Allow /mcp calls without Authorization and skip the unauthenticated rejection check.", + ) + parser.add_argument( + "--dev-bypass", + action="store_true", + help="Alias for --allow-no-auth for local DEV_BYPASS_AUTH=true testing.", + ) + parser.add_argument("--timeout", type=float, default=20.0, help="HTTP timeout in seconds. Default: 20.") + return parser.parse_args() + + +def run_value_command(command: list[str]) -> str | None: + try: + completed = subprocess.run(command, check=False, capture_output=True, text=True) + except FileNotFoundError: + return None + if completed.returncode != 0: + return None + value = completed.stdout.strip() + return value or None + + +def azd_value(name: str) -> str | None: + return run_value_command(["azd", "env", "get-value", name]) + + +def first_value(*values: str | None) -> str | None: + for value in values: + if value and value.strip(): + return value.strip() + return None + + +def resolve_mcp_url(args: argparse.Namespace) -> str: + url = args.url + if not url and args.local: + url = DEFAULT_LOCAL_MCP_URL + if not url: + url = first_value(os.getenv("HR_MCP_DIRECT_MCP_URL"), azd_value("HR_MCP_DIRECT_MCP_URL")) + if not url: + raise SmokeFailure( + "No MCP URL found. Pass --url, use --local for localhost, set HR_MCP_DIRECT_MCP_URL, " + "or select an azd environment containing HR_MCP_DIRECT_MCP_URL." + ) + parsed = urllib.parse.urlparse(url) + if not parsed.scheme or not parsed.netloc: + raise SmokeFailure(f"Invalid URL: {url!r}") + if parsed.path.rstrip("/").endswith("/mcp"): + return url.rstrip("/") + return urllib.parse.urljoin(url.rstrip("/") + "/", "mcp") + + +def derive_base_url(mcp_url: str) -> str | None: + parsed = urllib.parse.urlparse(mcp_url) + path = parsed.path.rstrip("/") + if not path.endswith("/mcp"): + return None + base_path = path[: -len("/mcp")] or "" + return urllib.parse.urlunparse(parsed._replace(path=base_path or "/", params="", query="", fragment="")).rstrip("/") + + +def http_request( + method: str, + url: str, + *, + payload: dict[str, Any] | None = None, + token: str | None = None, + timeout: float, +) -> HttpResponse: + body = None if payload is None else json.dumps(payload).encode("utf-8") + headers = {"Accept": "application/json"} + if payload is not None: + headers["Content-Type"] = "application/json" + if token: + headers["Authorization"] = f"Bearer {token}" + request = urllib.request.Request(url, data=body, headers=headers, method=method) + try: + with urllib.request.urlopen(request, timeout=timeout) as response: + text = response.read().decode("utf-8", errors="replace") + return HttpResponse(response.status, text, dict(response.headers.items())) + except urllib.error.HTTPError as exc: + text = exc.read().decode("utf-8", errors="replace") + return HttpResponse(exc.code, text, dict(exc.headers.items())) + except urllib.error.URLError as exc: + raise SmokeFailure(f"{method} {url} failed: {exc.reason}") from exc + except TimeoutError as exc: + raise SmokeFailure(f"{method} {url} timed out after {timeout} seconds.") from exc + + +def parse_json(response: HttpResponse, label: str) -> Any: + try: + return json.loads(response.body) + except json.JSONDecodeError as exc: + snippet = response.body[:240].replace("\n", " ") + raise SmokeFailure(f"{label} returned non-JSON response: {snippet}") from exc + + +def expect_http_200(response: HttpResponse, label: str) -> Any: + if response.status != 200: + snippet = response.body[:240].replace("\n", " ") + raise SmokeFailure(f"{label} returned HTTP {response.status}, expected 200. Body: {snippet}") + return parse_json(response, label) + + +def acquire_token(args: argparse.Namespace) -> str | None: + token = first_value(args.token, os.getenv("HR_MCP_ACCESS_TOKEN")) + if token: + return token + + scope = first_value(os.getenv("HR_MCP_SCOPE"), azd_value("HR_MCP_SCOPE")) + tenant_id = first_value(os.getenv("HR_MCP_TENANT_ID"), azd_value("HR_MCP_TENANT_ID")) + if not scope: + return None + + command = ["az", "account", "get-access-token", "--scope", scope, "--query", "accessToken", "-o", "tsv"] + if tenant_id: + command[3:3] = ["--tenant", tenant_id] + return run_value_command(command) + + +def assert_no_error_rpc(payload: Any, label: str) -> dict[str, Any]: + if not isinstance(payload, dict): + raise SmokeFailure(f"{label} returned a non-object JSON-RPC payload.") + if "error" in payload: + raise SmokeFailure(f"{label} returned JSON-RPC error: {payload['error']}") + result = payload.get("result") + if not isinstance(result, dict): + raise SmokeFailure(f"{label} returned no JSON-RPC result object.") + return result + + +def rpc_call(mcp_url: str, method: str, params: dict[str, Any], request_id: int, token: str | None, timeout: float) -> dict[str, Any]: + response = http_request( + "POST", + mcp_url, + payload={"jsonrpc": "2.0", "id": request_id, "method": method, "params": params}, + token=token, + timeout=timeout, + ) + return expect_http_200(response, method) + + +def decode_tool_text(result: dict[str, Any], label: str) -> dict[str, Any]: + content = result.get("content") + if not isinstance(content, list) or not content: + raise SmokeFailure(f"{label} returned no MCP content.") + first = content[0] + if not isinstance(first, dict) or first.get("type") != "text" or not isinstance(first.get("text"), str): + raise SmokeFailure(f"{label} returned malformed MCP text content.") + try: + payload = json.loads(first["text"]) + except json.JSONDecodeError as exc: + raise SmokeFailure(f"{label} returned text content that is not JSON.") from exc + if result.get("isError") is True or payload.get("ok") is False: + raise SmokeFailure(f"{label} returned tool error payload: {payload}") + return payload + + +def check_health(mcp_url: str, timeout: float) -> None: + base_url = derive_base_url(mcp_url) + if not base_url: + print("Skipping /health check because a base URL could not be derived.") + return + response = http_request("GET", urllib.parse.urljoin(base_url + "/", "health"), timeout=timeout) + payload = expect_http_200(response, "/health") + if payload.get("status") != "ok": + raise SmokeFailure(f"/health returned unexpected status payload: {payload}") + if payload.get("tools") != len(EXPECTED_TOOLS): + raise SmokeFailure(f"/health reported tools={payload.get('tools')}, expected {len(EXPECTED_TOOLS)}.") + print("OK /health") + + +def check_unauthenticated_rejected(mcp_url: str, timeout: float) -> None: + response = http_request( + "POST", + mcp_url, + payload={"jsonrpc": "2.0", "id": "unauth", "method": "initialize", "params": {}}, + timeout=timeout, + ) + if response.status not in {401, 403}: + raise SmokeFailure( + f"Unauthenticated /mcp returned HTTP {response.status}; expected 401 or 403 for deployed/auth mode." + ) + print("OK unauthenticated /mcp rejected") + + +def run_smoke(args: argparse.Namespace) -> None: + mcp_url = resolve_mcp_url(args) + no_auth_allowed = args.allow_no_auth or args.dev_bypass + + print(f"Testing HR MCP endpoint: {mcp_url}") + check_health(mcp_url, args.timeout) + + if no_auth_allowed: + token = None + print("Auth check skipped because no-auth/dev-bypass mode is enabled.") + else: + check_unauthenticated_rejected(mcp_url, args.timeout) + token = acquire_token(args) + if not token: + raise SmokeFailure( + "No bearer token available. Pass --token, set HR_MCP_ACCESS_TOKEN, or run az login with " + "HR_MCP_SCOPE and HR_MCP_TENANT_ID available from env/azd." + ) + + initialize = assert_no_error_rpc(rpc_call(mcp_url, "initialize", {}, 1, token, args.timeout), "initialize") + if "protocolVersion" not in initialize or initialize.get("serverInfo", {}).get("name") != "contoso-hr-mcp": + raise SmokeFailure(f"initialize returned unexpected payload: {initialize}") + print("OK initialize") + + listed = assert_no_error_rpc(rpc_call(mcp_url, "tools/list", {}, 2, token, args.timeout), "tools/list") + tools = listed.get("tools") + if not isinstance(tools, list): + raise SmokeFailure("tools/list did not return a tools array.") + tool_names = {tool.get("name") for tool in tools if isinstance(tool, dict)} + if tool_names != EXPECTED_TOOLS: + raise SmokeFailure(f"tools/list returned {sorted(tool_names)}, expected {sorted(EXPECTED_TOOLS)}.") + print("OK tools/list") + + read_result = assert_no_error_rpc( + rpc_call( + mcp_url, + "tools/call", + {"name": "get_employee_profile", "arguments": {"employee_id": "E1001"}}, + 3, + token, + args.timeout, + ), + "read tools/call", + ) + read_payload = decode_tool_text(read_result, "read tools/call") + if read_payload.get("employee_id") != "E1001" or "pto" not in read_payload: + raise SmokeFailure(f"read tools/call returned unexpected employee profile: {read_payload}") + print("OK read tools/call") + + write_result = assert_no_error_rpc( + rpc_call( + mcp_url, + "tools/call", + { + "name": "update_employee_skills", + "arguments": { + "employee_id": "E1001", + "skills": ["smoke-test-validation"], + "evidence_note": "Direct MCP smoke test validation evidence.", + }, + }, + 4, + token, + args.timeout, + ), + "write tools/call", + ) + write_payload = decode_tool_text(write_result, "write tools/call") + update = write_payload.get("skill_update", {}) + if update.get("employee_id") != "E1001" or "smoke-test-validation" not in write_payload.get("current_skills", []): + raise SmokeFailure(f"write tools/call returned unexpected skill update: {write_payload}") + print("OK write tools/call") + + +def main() -> int: + args = parse_args() + try: + run_smoke(args) + except SmokeFailure as exc: + print(f"FAIL: {exc}", file=sys.stderr) + return 1 + print("HR MCP direct smoke test passed.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/.dockerignore b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/.dockerignore new file mode 100644 index 000000000..6f9548d0c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/.dockerignore @@ -0,0 +1,7 @@ +# The build context is this directory; keep only what the image needs. +__pycache__/ +*.pyc +.pytest_cache/ +.ruff_cache/ +.mypy_cache/ +.DS_Store diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/Dockerfile b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/Dockerfile new file mode 100644 index 000000000..95aec80d4 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/Dockerfile @@ -0,0 +1,25 @@ +# The base image bundles both Python 3.13 and uv. It is parameterized so the +# build can pull it from a registry that is not rate-limited (e.g. a copy +# imported into your ACR with `az acr import`). The default uses GHCR, which is +# not subject to Docker Hub's anonymous pull-rate limit. +ARG BASE_IMAGE=ghcr.io/astral-sh/uv:python3.13-bookworm-slim + +FROM ${BASE_IMAGE} + +ENV UV_SYSTEM_PYTHON=1 \ + UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + PORT=8080 + +WORKDIR /app/mcp-hr/server + +# The build context is this directory (mcp-hr/server), so paths are local. This +# keeps the ACR upload small instead of tarring the whole workshop tree. +COPY requirements.txt . +RUN uv pip install --system --no-cache --prerelease=if-necessary-or-explicit -r requirements.txt + +COPY . . + +EXPOSE 8080 + +CMD ["python", "server.py"] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/auth.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/auth.py new file mode 100644 index 000000000..738e55d9a --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/auth.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +import os +from functools import lru_cache +from typing import Any + +import jwt +from fastapi import Header, HTTPException, status +from jwt import PyJWKClient + + +def _truthy(value: str | None) -> bool: + return value is not None and value.lower() in {"1", "true", "yes", "y", "on"} + + +def auth_enabled() -> bool: + if _truthy(os.getenv("DEV_BYPASS_AUTH")): + return False + return _truthy(os.getenv("AUTH_ENABLED")) or bool(os.getenv("ENTRA_AUDIENCE")) + + +def expected_issuer() -> str: + issuer = os.getenv("ENTRA_ISSUER") + if issuer: + return issuer.rstrip("/") + tenant_id = os.getenv("ENTRA_TENANT_ID", "common") + return f"https://login.microsoftonline.com/{tenant_id}/v2.0" + + +def expected_audience() -> list[str]: + audience = os.getenv("ENTRA_AUDIENCE") + if not audience: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="auth_misconfigured") + audiences = [audience] + if audience.startswith("api://"): + audiences.append(audience.removeprefix("api://")) + return audiences + + +def required_scope() -> str | None: + return os.getenv("ENTRA_REQUIRED_SCOPE") or os.getenv("MCP_REQUIRED_SCOPE") + + +def required_role() -> str | None: + """App role accepted for application (managed-identity) callers.""" + return os.getenv("ENTRA_REQUIRED_ROLE") + + +@lru_cache(maxsize=4) +def _jwks_client(jwks_url: str) -> PyJWKClient: + return PyJWKClient(jwks_url) + + +def _jwks_url() -> str: + configured = os.getenv("ENTRA_JWKS_URL") + if configured: + return configured + issuer = expected_issuer() + base = issuer.removesuffix("/v2.0") + return f"{base}/discovery/v2.0/keys" + + +async def validate_bearer_token(authorization: str | None = Header(default=None)) -> dict[str, Any]: + if not auth_enabled(): + return {"auth": "bypassed" if _truthy(os.getenv("DEV_BYPASS_AUTH")) else "disabled"} + if not authorization or not authorization.lower().startswith("bearer "): + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="missing_bearer_token") + token = authorization.split(" ", 1)[1].strip() + try: + signing_key = _jwks_client(_jwks_url()).get_signing_key_from_jwt(token) + claims = jwt.decode( + token, + signing_key.key, + algorithms=["RS256"], + audience=expected_audience(), + issuer=expected_issuer(), + options={"require": ["exp", "iat"]}, + ) + except jwt.PyJWTError as exc: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid_token") from exc + scope = required_scope() + role = required_role() + if scope or role: + scopes = set(str(claims.get("scp", "")).split()) + roles = set(claims.get("roles", []) or []) + allowed = ( + (scope is not None and (scope in scopes or scope in roles)) + or (role is not None and role in roles) + ) + if not allowed: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="insufficient_scope") + return {"auth": "entra", "oid": claims.get("oid"), "tid": claims.get("tid")} diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/hr_data.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/hr_data.py new file mode 100644 index 000000000..af46ec765 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/hr_data.py @@ -0,0 +1,368 @@ +from __future__ import annotations + +import hashlib +import random +from dataclasses import dataclass, field +from datetime import date, datetime, timezone +from typing import Any + + +DEMO_SEED = "contoso-hr-mcp-demo-v1" + + +@dataclass +class Employee: + employee_id: str + name: str + department: str + role: str + location: str + manager_id: str | None + hire_date: date + skills: list[str] + goals: list[str] + pto_accrued_days: int + pto_used_days: int + skill_evidence: list[dict[str, Any]] = field(default_factory=list) + + +class HRStore: + def __init__(self) -> None: + self.employees = self._seed_employees() + self.pto_requests: list[dict[str, Any]] = [] + self.skill_updates: list[dict[str, Any]] = [] + + def _seed_employees(self) -> dict[str, Employee]: + first_names = [ + "Avery", + "Jordan", + "Morgan", + "Riley", + "Taylor", + "Casey", + "Sam", + "Jamie", + "Priya", + "Diego", + "Mina", + "Noah", + ] + last_names = [ + "Chen", + "Patel", + "Garcia", + "Smith", + "Brown", + "Kim", + "Okafor", + "Nakamura", + "Silva", + "Johnson", + "Müller", + "Singh", + ] + departments = { + "People": ["HR Business Partner", "Recruiter", "Learning Specialist"], + "Engineering": ["Software Engineer", "Platform Engineer", "Engineering Manager"], + "Sales": ["Account Executive", "Solution Specialist", "Sales Manager"], + "Finance": ["Financial Analyst", "Controller", "Procurement Lead"], + "Support": ["Support Engineer", "Customer Success Manager", "Escalation Lead"], + } + locations = ["Seattle", "London", "Tokyo", "Berlin", "Toronto", "Bengaluru"] + skill_pool = { + "People": ["coaching", "employee relations", "workforce planning", "facilitation"], + "Engineering": ["python", "azure", "api design", "incident response", "security"], + "Sales": ["negotiation", "crm", "discovery", "forecasting"], + "Finance": ["budgeting", "excel", "risk controls", "vendor management"], + "Support": ["troubleshooting", "customer empathy", "kql", "runbooks"], + } + goals_pool = [ + "manager readiness", + "technical leadership", + "customer storytelling", + "automation", + "cross-functional collaboration", + "operational excellence", + ] + + employees: dict[str, Employee] = {} + index = 1001 + for dept, roles in departments.items(): + for role in roles: + rng = random.Random(f"{DEMO_SEED}:{dept}:{role}") + employee_id = f"E{index}" + name = f"{rng.choice(first_names)} {rng.choice(last_names)}" + hire_year = 2016 + rng.randrange(0, 9) + hire_month = rng.randrange(1, 13) + hire_day = min(28, 1 + rng.randrange(0, 28)) + skills = rng.sample(skill_pool[dept] + ["communication", "analytics", "presentation"], 4) + goals = rng.sample(goals_pool, 2) + accrued = 18 + rng.randrange(0, 9) + used = rng.randrange(0, max(1, accrued - 3)) + employees[employee_id] = Employee( + employee_id=employee_id, + name=name, + department=dept, + role=role, + location=rng.choice(locations), + manager_id=None, + hire_date=date(hire_year, hire_month, hire_day), + skills=sorted(set(skills)), + goals=goals, + pto_accrued_days=accrued, + pto_used_days=used, + ) + index += 1 + + ids = list(employees) + for employee in employees.values(): + if "Manager" not in employee.role and "Lead" not in employee.role and "Controller" not in employee.role: + candidates = [ + e.employee_id + for e in employees.values() + if e.department == employee.department and e.employee_id != employee.employee_id + ] + employee.manager_id = candidates[0] if candidates else None + return employees + + def search_employees(self, query: str, department: str | None = None, location: str | None = None) -> dict[str, Any]: + terms = [t.lower() for t in query.split() if t.strip()] + department_filter = department.lower() if department else None + location_filter = location.lower() if location else None + results = [] + for employee in self.employees.values(): + if department_filter and employee.department.lower() != department_filter: + continue + if location_filter and employee.location.lower() != location_filter: + continue + haystack = " ".join( + [ + employee.employee_id, + employee.name, + employee.department, + employee.role, + employee.location, + " ".join(employee.skills), + " ".join(employee.goals), + ] + ).lower() + score = sum(3 if term in employee.name.lower() else 1 for term in terms if term in haystack) + if not terms: + score = 1 + if score: + results.append((score, employee)) + results.sort(key=lambda item: (-item[0], item[1].name)) + return { + "query": query, + "filters": {"department": department, "location": location}, + "result_count": len(results), + "employees": [self._employee_summary(employee, score) for score, employee in results[:10]], + } + + def get_employee_profile(self, employee_id: str) -> dict[str, Any]: + employee = self.employees.get(employee_id.upper()) + if not employee: + return self._error("NOT_FOUND", f"Employee {employee_id} was not found.", ["Call `search_employees` to find a valid employee_id."]) + today = date.today() + pending_pto = [r for r in self.pto_requests if r["employee_id"] == employee.employee_id and r["status"] == "PENDING_REVIEW"] + return { + **self._employee_summary(employee, score=None), + "hire_date": employee.hire_date.isoformat(), + "tenure_years": round((today - employee.hire_date).days / 365.25, 1), + "manager_id": employee.manager_id, + "goals": employee.goals, + "pto": self._pto_snapshot(employee), + "pending_pto_requests": pending_pto, + "skill_evidence": employee.skill_evidence[-5:], + "generated_at": datetime.now(timezone.utc).isoformat(), + } + + def recommend_learning_path(self, employee_id: str, target_role: str) -> dict[str, Any]: + employee = self.employees.get(employee_id.upper()) + if not employee: + return self._error("NOT_FOUND", f"Employee {employee_id} was not found.", ["Call `search_employees` to find a valid employee_id."]) + target_requirements = self._target_role_skills(target_role) + current = {skill.lower() for skill in employee.skills} + missing = [skill for skill in target_requirements if skill.lower() not in current] + tenure_days = max(1, (date.today() - employee.hire_date).days) + pace = "accelerated" if tenure_days > 365 * 3 and len(missing) <= 3 else "standard" + modules = [ + { + "module": f"{skill.title()} applied lab", + "skill": skill, + "duration_hours": 2 + stable_int(employee.employee_id, skill, modulo=5), + "due_date": add_business_days(date.today(), 10 + i * (7 if pace == "accelerated" else 14)).isoformat(), + } + for i, skill in enumerate(missing[:5]) + ] + confidence = round(0.62 + min(0.28, len(set(target_requirements) & current) * 0.07), 2) + return { + "employee_id": employee.employee_id, + "current_role": employee.role, + "target_role": target_role, + "pace": pace, + "matched_skills": sorted(set(target_requirements) & current), + "skill_gaps": missing, + "recommended_modules": modules, + "mentor_suggestion": self._mentor_for(target_requirements, employee.employee_id), + "confidence": confidence, + "generated_for_date": date.today().isoformat(), + } + + def submit_pto_request(self, employee_id: str, start_date: str, end_date: str, reason: str) -> dict[str, Any]: + employee = self.employees.get(employee_id.upper()) + if not employee: + return self._error("NOT_FOUND", f"Employee {employee_id} was not found.", ["Call `search_employees` to find a valid employee_id."]) + try: + start = date.fromisoformat(start_date) + end = date.fromisoformat(end_date) + except ValueError: + return self._error("INVALID_DATE", "Dates must use ISO format YYYY-MM-DD.", ["Retry `submit_pto_request` with valid start_date and end_date."]) + if end < start: + return self._error("INVALID_DATE_RANGE", "end_date must be on or after start_date.", ["Retry with an end_date on or after start_date."]) + if start < date.today(): + return self._error("PAST_DATE", "PTO cannot start in the past.", ["Retry with a future start_date."]) + business_days = count_business_days(start, end) + if business_days <= 0: + return self._error("NO_BUSINESS_DAYS", "Request does not include any business days.", ["Choose a range with at least one weekday."]) + balance = self._pto_snapshot(employee)["remaining_days"] + overlapping = [ + r["request_id"] + for r in self.pto_requests + if r["employee_id"] == employee.employee_id + and r["status"] != "CANCELLED" + and not (end < date.fromisoformat(r["start_date"]) or start > date.fromisoformat(r["end_date"])) + ] + if overlapping: + return self._error("STATE_CONFLICT", "Requested dates overlap an existing PTO request.", [f"Review existing request(s): {', '.join(overlapping)}."]) + if business_days > balance: + return self._error("INSUFFICIENT_BALANCE", f"Requested {business_days} days but only {balance} are available.", ["Shorten the date range or contact HR for leave options."]) + request_id = f"PTO-{datetime.now(timezone.utc).strftime('%Y%m%d')}-{stable_int(employee.employee_id, start_date, end_date, modulo=9000) + 1000}" + record = { + "request_id": request_id, + "employee_id": employee.employee_id, + "start_date": start.isoformat(), + "end_date": end.isoformat(), + "business_days": business_days, + "reason_category": categorize_reason(reason), + "status": "PENDING_REVIEW" if business_days > 5 else "AUTO_APPROVED", + "submitted_at": datetime.now(timezone.utc).isoformat(), + "manager_id": employee.manager_id, + } + self.pto_requests.append(record) + if record["status"] == "AUTO_APPROVED": + employee.pto_used_days += business_days + return {"ok": True, "pto_request": record, "pto_balance": self._pto_snapshot(employee)} + + def update_employee_skills(self, employee_id: str, skills: list[str], evidence_note: str) -> dict[str, Any]: + employee = self.employees.get(employee_id.upper()) + if not employee: + return self._error("NOT_FOUND", f"Employee {employee_id} was not found.", ["Call `search_employees` to find a valid employee_id."]) + normalized = sorted({skill.strip().lower() for skill in skills if skill and skill.strip()}) + if not normalized: + return self._error("VALIDATION_ERROR", "At least one non-empty skill is required.", ["Retry with skills like ['python', 'facilitation']."]) + if len(evidence_note.strip()) < 12: + return self._error("INSUFFICIENT_EVIDENCE", "evidence_note must describe where the skills were demonstrated.", ["Include a project, course, manager note, or assessment result."]) + existing = {skill.lower() for skill in employee.skills} + added = [skill for skill in normalized if skill not in existing] + already_present = [skill for skill in normalized if skill in existing] + employee.skills = sorted(set(employee.skills) | set(added)) + event = { + "update_id": f"SKILL-{stable_int(employee.employee_id, evidence_note, modulo=900000) + 100000}", + "employee_id": employee.employee_id, + "added_skills": added, + "already_present": already_present, + "evidence_note": evidence_note.strip(), + "updated_at": datetime.now(timezone.utc).isoformat(), + } + employee.skill_evidence.append(event) + self.skill_updates.append(event) + return {"ok": True, "skill_update": event, "current_skills": employee.skills} + + def _employee_summary(self, employee: Employee, score: int | None) -> dict[str, Any]: + payload = { + "employee_id": employee.employee_id, + "name": employee.name, + "department": employee.department, + "role": employee.role, + "location": employee.location, + "skills": employee.skills, + } + if score is not None: + payload["match_score"] = score + return payload + + def _pto_snapshot(self, employee: Employee) -> dict[str, Any]: + pending_days = sum(r["business_days"] for r in self.pto_requests if r["employee_id"] == employee.employee_id and r["status"] == "PENDING_REVIEW") + return { + "accrued_days": employee.pto_accrued_days, + "used_days": employee.pto_used_days, + "pending_days": pending_days, + "remaining_days": max(0, employee.pto_accrued_days - employee.pto_used_days - pending_days), + "as_of": date.today().isoformat(), + } + + def _target_role_skills(self, target_role: str) -> list[str]: + role = target_role.lower() + base = ["communication", "analytics"] + if "manager" in role or "lead" in role: + return base + ["coaching", "planning", "stakeholder management", "decision making"] + if "engineer" in role or "architect" in role: + return base + ["python", "azure", "api design", "security", "incident response"] + if "sales" in role or "account" in role: + return base + ["negotiation", "crm", "customer storytelling", "forecasting"] + if "finance" in role: + return base + ["budgeting", "risk controls", "vendor management"] + return base + ["presentation", "cross-functional collaboration", "automation"] + + def _mentor_for(self, required_skills: list[str], employee_id: str) -> dict[str, Any] | None: + required = {skill.lower() for skill in required_skills} + candidates = [ + (len(required & {skill.lower() for skill in employee.skills}), employee) + for employee in self.employees.values() + if employee.employee_id != employee_id + ] + candidates = [candidate for candidate in candidates if candidate[0] > 0] + if not candidates: + return None + score, employee = sorted(candidates, key=lambda item: (-item[0], item[1].name))[0] + return {"employee_id": employee.employee_id, "name": employee.name, "role": employee.role, "matched_skill_count": score} + + def _error(self, error: str, message: str, next_steps: list[str]) -> dict[str, Any]: + return {"ok": False, "error": error, "message": message, "next_steps": next_steps} + + +def stable_int(*parts: str, modulo: int) -> int: + digest = hashlib.sha256(":".join(parts).encode("utf-8")).hexdigest() + return int(digest[:12], 16) % modulo + + +def count_business_days(start: date, end: date) -> int: + days = 0 + cursor = start + while cursor <= end: + if cursor.weekday() < 5: + days += 1 + cursor = date.fromordinal(cursor.toordinal() + 1) + return days + + +def add_business_days(start: date, days: int) -> date: + cursor = start + remaining = days + while remaining: + cursor = date.fromordinal(cursor.toordinal() + 1) + if cursor.weekday() < 5: + remaining -= 1 + return cursor + + +def categorize_reason(reason: str) -> str: + lowered = reason.lower() + if any(word in lowered for word in ["sick", "medical", "doctor"]): + return "medical" + if any(word in lowered for word in ["family", "care", "child"]): + return "family" + if any(word in lowered for word in ["vacation", "holiday", "travel"]): + return "vacation" + return "personal" diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/pyproject.toml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/pyproject.toml new file mode 100644 index 000000000..a95244a69 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "hr-mcp-server" +version = "0.1.0" +description = "HR-focused MCP server for the Citadel workshop" +requires-python = ">=3.13" +dependencies = [ + "azure-monitor-opentelemetry>=1.6.0", + "fastapi>=0.124.0", + "pyjwt[crypto]>=2.12.1", + "uvicorn[standard]>=0.38.0", +] + diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/requirements.txt new file mode 100644 index 000000000..c7bdc4d5b --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/requirements.txt @@ -0,0 +1,213 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile pyproject.toml -o requirements.txt +annotated-doc==0.0.4 + # via fastapi +annotated-types==0.7.0 + # via pydantic +anyio==4.13.0 + # via + # starlette + # watchfiles +asgiref==3.11.1 + # via opentelemetry-instrumentation-asgi +azure-core==1.41.0 + # via + # azure-core-tracing-opentelemetry + # azure-identity + # azure-monitor-opentelemetry + # azure-monitor-opentelemetry-exporter + # msrest +azure-core-tracing-opentelemetry==1.0.0b13 + # via azure-monitor-opentelemetry +azure-identity==1.25.3 + # via azure-monitor-opentelemetry-exporter +azure-monitor-opentelemetry==1.8.8 + # via hr-mcp-server (pyproject.toml) +azure-monitor-opentelemetry-exporter==1.0.0b53 + # via azure-monitor-opentelemetry +certifi==2026.5.20 + # via + # msrest + # requests +cffi==2.0.0 + # via cryptography +charset-normalizer==3.4.7 + # via requests +click==8.4.1 + # via uvicorn +cryptography==48.0.1 + # via + # azure-identity + # msal + # pyjwt +fastapi==0.136.3 + # via hr-mcp-server (pyproject.toml) +h11==0.16.0 + # via uvicorn +httptools==0.8.0 + # via uvicorn +idna==3.18 + # via + # anyio + # requests +importlib-metadata==8.7.1 + # via opentelemetry-api +isodate==0.7.2 + # via msrest +msal==1.37.0 + # via + # azure-identity + # msal-extensions +msal-extensions==1.3.1 + # via azure-identity +msrest==0.7.1 + # via azure-monitor-opentelemetry-exporter +oauthlib==3.3.1 + # via requests-oauthlib +opentelemetry-api==1.40.0 + # via + # azure-core-tracing-opentelemetry + # azure-monitor-opentelemetry-exporter + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-fastapi + # opentelemetry-instrumentation-flask + # opentelemetry-instrumentation-logging + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-urllib + # opentelemetry-instrumentation-urllib3 + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-instrumentation==0.61b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-fastapi + # opentelemetry-instrumentation-flask + # opentelemetry-instrumentation-logging + # opentelemetry-instrumentation-psycopg2 + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-urllib + # opentelemetry-instrumentation-urllib3 + # opentelemetry-instrumentation-wsgi +opentelemetry-instrumentation-asgi==0.61b0 + # via opentelemetry-instrumentation-fastapi +opentelemetry-instrumentation-dbapi==0.61b0 + # via opentelemetry-instrumentation-psycopg2 +opentelemetry-instrumentation-django==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-fastapi==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-flask==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-logging==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-psycopg2==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-requests==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-urllib==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-urllib3==0.61b0 + # via azure-monitor-opentelemetry +opentelemetry-instrumentation-wsgi==0.61b0 + # via + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-flask +opentelemetry-resource-detector-azure==0.1.5 + # via azure-monitor-opentelemetry +opentelemetry-sdk==1.40.0 + # via + # azure-monitor-opentelemetry + # azure-monitor-opentelemetry-exporter + # opentelemetry-resource-detector-azure +opentelemetry-semantic-conventions==0.61b0 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-fastapi + # opentelemetry-instrumentation-flask + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-urllib + # opentelemetry-instrumentation-urllib3 + # opentelemetry-instrumentation-wsgi + # opentelemetry-sdk +opentelemetry-util-http==0.61b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-django + # opentelemetry-instrumentation-fastapi + # opentelemetry-instrumentation-flask + # opentelemetry-instrumentation-requests + # opentelemetry-instrumentation-urllib + # opentelemetry-instrumentation-urllib3 + # opentelemetry-instrumentation-wsgi +packaging==26.2 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-flask +psutil==7.2.2 + # via azure-monitor-opentelemetry-exporter +pycparser==3.0 + # via cffi +pydantic==2.13.4 + # via fastapi +pydantic-core==2.46.4 + # via pydantic +pyjwt==2.13.0 + # via + # hr-mcp-server (pyproject.toml) + # msal +python-dotenv==1.2.2 + # via uvicorn +pyyaml==6.0.3 + # via uvicorn +requests==2.34.2 + # via + # azure-core + # msal + # msrest + # requests-oauthlib +requests-oauthlib==2.0.0 + # via msrest +starlette==1.3.1 + # via fastapi +typing-extensions==4.15.0 + # via + # azure-core + # azure-identity + # fastapi + # opentelemetry-api + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.2 + # via + # fastapi + # pydantic +urllib3==2.7.0 + # via requests +uvicorn==0.49.0 + # via hr-mcp-server (pyproject.toml) +uvloop==0.22.1 + # via uvicorn +watchfiles==1.2.0 + # via uvicorn +websockets==16.0 + # via uvicorn +wrapt==1.17.3 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-dbapi + # opentelemetry-instrumentation-urllib3 +zipp==4.1.0 + # via importlib-metadata diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/server.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/server.py new file mode 100644 index 000000000..e864afa6c --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/server.py @@ -0,0 +1,194 @@ +from __future__ import annotations + +import json +import os +from typing import Any, Callable + +from fastapi import Depends, FastAPI, Request +from fastapi.responses import JSONResponse +from pydantic import BaseModel, Field + +from auth import validate_bearer_token +from hr_data import HRStore +from telemetry import configure_telemetry, logger, tool_span + + +SERVER_NAME = "contoso-hr-mcp" +PROTOCOL_VERSION = "2025-06-18" + +app = FastAPI(title="Contoso HR MCP Server", version="0.1.0") +store = HRStore() +configure_telemetry() + + +class JSONRPCRequest(BaseModel): + jsonrpc: str = "2.0" + id: str | int | None = None + method: str + params: dict[str, Any] = Field(default_factory=dict) + + +def jsonrpc_result(request_id: str | int | None, result: Any) -> dict[str, Any]: + if request_id is None: + return {} + return {"jsonrpc": "2.0", "id": request_id, "result": result} + + +def jsonrpc_error(request_id: str | int | None, code: int, message: str, data: Any | None = None) -> dict[str, Any]: + payload: dict[str, Any] = {"jsonrpc": "2.0", "id": request_id, "error": {"code": code, "message": message}} + if data is not None: + payload["error"]["data"] = data + return payload + + +def tool_result(payload: dict[str, Any]) -> dict[str, Any]: + return {"content": [{"type": "text", "text": json.dumps(payload, indent=2, sort_keys=True)}], "isError": not payload.get("ok", True)} + + +def tool_schema(name: str, description: str, properties: dict[str, Any], required: list[str], read_only: bool) -> dict[str, Any]: + return { + "name": name, + "description": description, + "inputSchema": {"type": "object", "properties": properties, "required": required}, + "annotations": {"readOnlyHint": read_only}, + } + + +TOOLS: dict[str, Callable[..., dict[str, Any]]] = { + "search_employees": store.search_employees, + "get_employee_profile": store.get_employee_profile, + "recommend_learning_path": store.recommend_learning_path, + "submit_pto_request": store.submit_pto_request, + "update_employee_skills": store.update_employee_skills, +} + + +TOOL_DEFINITIONS = [ + tool_schema( + "search_employees", + "Search employees by name, role, skills, department, location, or goals.", + { + "query": {"type": "string", "description": "Search terms such as name, role, skill, or goal."}, + "department": {"type": "string", "description": "Optional exact department filter."}, + "location": {"type": "string", "description": "Optional exact location filter."}, + }, + ["query"], + True, + ), + tool_schema( + "get_employee_profile", + "Get a detailed employee profile including current PTO and skill evidence.", + {"employee_id": {"type": "string", "description": "Employee id, for example E1001."}}, + ["employee_id"], + True, + ), + tool_schema( + "recommend_learning_path", + "Recommend a dynamic learning path for an employee and target role.", + { + "employee_id": {"type": "string", "description": "Employee id, for example E1001."}, + "target_role": {"type": "string", "description": "Target role such as Engineering Manager."}, + }, + ["employee_id", "target_role"], + True, + ), + tool_schema( + "submit_pto_request", + "Submit a PTO request and update in-memory state when auto-approved.", + { + "employee_id": {"type": "string", "description": "Employee id, for example E1001."}, + "start_date": {"type": "string", "description": "ISO date YYYY-MM-DD."}, + "end_date": {"type": "string", "description": "ISO date YYYY-MM-DD."}, + "reason": {"type": "string", "description": "Reason for PTO."}, + }, + ["employee_id", "start_date", "end_date", "reason"], + False, + ), + tool_schema( + "update_employee_skills", + "Add employee skills with evidence and persist the update in memory.", + { + "employee_id": {"type": "string", "description": "Employee id, for example E1001."}, + "skills": {"type": "array", "items": {"type": "string"}, "description": "Skills to add."}, + "evidence_note": {"type": "string", "description": "Evidence for the skill update."}, + }, + ["employee_id", "skills", "evidence_note"], + False, + ), +] + + +@app.get("/health") +async def health() -> dict[str, Any]: + return {"status": "ok", "service": SERVER_NAME, "tools": len(TOOL_DEFINITIONS)} + + +@app.get("/") +async def root() -> dict[str, Any]: + return await health() + + +@app.post("/mcp") +async def mcp_endpoint(request: Request, _claims: dict[str, Any] = Depends(validate_bearer_token)) -> JSONResponse: + try: + body = await request.json() + except json.JSONDecodeError: + return JSONResponse(jsonrpc_error(None, -32700, "Parse error")) + + if isinstance(body, list): + responses = [await handle_message(item) for item in body] + return JSONResponse([response for response in responses if response]) + return JSONResponse(await handle_message(body)) + + +async def handle_message(raw: dict[str, Any]) -> dict[str, Any]: + try: + message = JSONRPCRequest.model_validate(raw) + except Exception as exc: + return jsonrpc_error(raw.get("id") if isinstance(raw, dict) else None, -32600, "Invalid Request", str(exc)) + + if message.method == "initialize": + return jsonrpc_result( + message.id, + { + "protocolVersion": PROTOCOL_VERSION, + "capabilities": {"tools": {"listChanged": False}, "prompts": {}, "resources": {}}, + "serverInfo": {"name": SERVER_NAME, "version": "0.1.0"}, + }, + ) + if message.method == "notifications/initialized": + return jsonrpc_result(message.id, {}) + if message.method == "tools/list": + return jsonrpc_result(message.id, {"tools": TOOL_DEFINITIONS}) + if message.method == "prompts/list": + return jsonrpc_result(message.id, {"prompts": []}) + if message.method == "resources/list": + return jsonrpc_result(message.id, {"resources": []}) + if message.method == "logging/setLevel": + level = str(message.params.get("level", "info")).upper() + logger.setLevel(level if level in {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"} else "INFO") + return jsonrpc_result(message.id, {}) + if message.method == "tools/call": + return jsonrpc_result(message.id, tool_result(call_tool(message.params))) + return jsonrpc_error(message.id, -32601, "Method not found") + + +def call_tool(params: dict[str, Any]) -> dict[str, Any]: + name = params.get("name") + arguments = params.get("arguments") or {} + if name not in TOOLS: + return {"ok": False, "error": "UNKNOWN_TOOL", "message": f"Tool {name!r} is not available.", "next_steps": ["Call `tools/list` to discover available tools."]} + with tool_span(str(name)): + try: + return TOOLS[str(name)](**arguments) + except TypeError as exc: + return {"ok": False, "error": "VALIDATION_ERROR", "message": str(exc), "next_steps": [f"Retry `{name}` with arguments matching its inputSchema."]} + except Exception as exc: + logger.exception("Tool call failed for %s", name) + return {"ok": False, "error": "INTERNAL_ERROR", "message": type(exc).__name__, "next_steps": ["Retry later or contact the MCP server operator."]} + + +if __name__ == "__main__": + import uvicorn + + uvicorn.run("server:app", host="0.0.0.0", port=int(os.getenv("PORT", "8080"))) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/telemetry.py b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/telemetry.py new file mode 100644 index 000000000..579c8ea7f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/telemetry.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +import logging +import os +from contextlib import contextmanager +from typing import Iterator + +from opentelemetry import trace + + +logger = logging.getLogger("mcp_hr_server") +tracer = trace.get_tracer("workshop.mcp_hr_server") + + +def configure_telemetry() -> None: + logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO")) + logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(logging.WARNING) + logging.getLogger("azure.monitor.opentelemetry").setLevel(logging.WARNING) + if not os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING"): + logger.info("Application Insights not configured; using local OpenTelemetry no-op exporter.") + return + try: + from azure.monitor.opentelemetry import configure_azure_monitor + + configure_azure_monitor() + logger.info("Application Insights telemetry configured.") + except Exception as exc: + logger.warning("Application Insights telemetry configuration failed: %s", exc) + + +@contextmanager +def tool_span(tool_name: str) -> Iterator[None]: + with tracer.start_as_current_span("mcp.tool_call") as span: + span.set_attribute("mcp.tool.name", tool_name) + try: + yield + span.set_attribute("mcp.tool.success", True) + except Exception as exc: + span.set_attribute("mcp.tool.success", False) + span.set_attribute("mcp.tool.error_type", type(exc).__name__) + raise diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/uv.lock b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/uv.lock new file mode 100644 index 000000000..491d792f9 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/mcp-hr/server/uv.lock @@ -0,0 +1,1160 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, +] + +[[package]] +name = "azure-core" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/f3/b416179e408990df5db0d516283022dde0f5d0111d98c1a848e41853e81c/azure_core-1.41.0.tar.gz", hash = "sha256:f46ff5dfcd230f25cf1c19e8a34b8dc08a337b2503e268bb600a16c00db8ad5a", size = 381042, upload-time = "2026-05-07T23:30:54.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/db/325c6d7312d2200251c52323878281045aaffcb5586612296484e4280eaa/azure_core-1.41.0-py3-none-any.whl", hash = "sha256:522b4011e8180b1a3dcd2024396a4e7fe9ac37fb8597db47163d230b5efe892d", size = 220920, upload-time = "2026-05-07T23:30:56.357Z" }, +] + +[[package]] +name = "azure-core-tracing-opentelemetry" +version = "1.0.0b13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/ab/a937e4af8afec9d437d55252f2a3a4419fc3fc7d5e5d54022622bd11b2b6/azure_core_tracing_opentelemetry-1.0.0b13.tar.gz", hash = "sha256:6cb2f8dfd5dee6c11843db0205fc92e2434e1a272c169c953afe92483aafc7eb", size = 25832, upload-time = "2026-05-01T00:59:57.941Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/01/8898c2506cae6a57c1b76d930d2af94764a65354bc863feb2684235851ce/azure_core_tracing_opentelemetry-1.0.0b13-py3-none-any.whl", hash = "sha256:4dacd3a9f117f11f98e89305e161c951b8df85b984f3b56130614de9cd9887f9", size = 12112, upload-time = "2026-05-01T00:59:59.149Z" }, +] + +[[package]] +name = "azure-identity" +version = "1.25.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "msal" }, + { name = "msal-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/0e/3a63efb48aa4a5ae2cfca61ee152fbcb668092134d3eb8bfda472dd5c617/azure_identity-1.25.3.tar.gz", hash = "sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6", size = 286304, upload-time = "2026-03-13T01:12:20.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/9a/417b3a533e01953a7c618884df2cb05a71e7b68bdbce4fbdb62349d2a2e8/azure_identity-1.25.3-py3-none-any.whl", hash = "sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c", size = 192138, upload-time = "2026-03-13T01:12:22.951Z" }, +] + +[[package]] +name = "azure-monitor-opentelemetry" +version = "1.8.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "azure-core-tracing-opentelemetry" }, + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "opentelemetry-instrumentation-django" }, + { name = "opentelemetry-instrumentation-fastapi" }, + { name = "opentelemetry-instrumentation-flask" }, + { name = "opentelemetry-instrumentation-logging" }, + { name = "opentelemetry-instrumentation-psycopg2" }, + { name = "opentelemetry-instrumentation-requests" }, + { name = "opentelemetry-instrumentation-urllib" }, + { name = "opentelemetry-instrumentation-urllib3" }, + { name = "opentelemetry-resource-detector-azure" }, + { name = "opentelemetry-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/9e/3e63aa6cf8a46d06090b6f0046da6a59c470ffaf9968430867fa4a3c2eac/azure_monitor_opentelemetry-1.8.8.tar.gz", hash = "sha256:c6478cac82939230e9af1004b0a147e39b9046a564f3811d65241797f2f9d41d", size = 77532, upload-time = "2026-05-14T16:21:44.796Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/eb0cbb6771b222fddc520c30a7a8005e8537470131910c089ece37c82655/azure_monitor_opentelemetry-1.8.8-py3-none-any.whl", hash = "sha256:8c0d3095785ca8297d727181bef8dc0341f318fae750ad63af47722bbc56096f", size = 41371, upload-time = "2026-05-14T16:21:45.949Z" }, +] + +[[package]] +name = "azure-monitor-opentelemetry-exporter" +version = "1.0.0b53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "azure-identity" }, + { name = "msrest" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "psutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/64/875f13849fe2e3832ceda6a218fa5422a25e72c1b86623a8514f541a8c60/azure_monitor_opentelemetry_exporter-1.0.0b53.tar.gz", hash = "sha256:1274e9008909414a25c6287185a6c5a884209705b6e651a1ffddfbdab3b76e52", size = 335614, upload-time = "2026-06-08T15:54:22.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/dc/6dbff881ac739999bc31f166504ed31a2e0fef45cc59a3c1b2386a4b4abe/azure_monitor_opentelemetry_exporter-1.0.0b53-py2.py3-none-any.whl", hash = "sha256:e2faadaf203369f25cb96df2a75017bd7f11655b033200c5867151f8fb2a6ba7", size = 249226, upload-time = "2026-06-08T15:54:24.413Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/bc/ee4137cbbe105652c0ee4252792b78fc8e7afa4b8e61d9d5dc05a7f45731/cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1", size = 8008324, upload-time = "2026-06-09T22:31:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/d5/85/6379d42181bfc713094f081360fc5784d6c816b599d45e7f082502d173ce/cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225", size = 4696243, upload-time = "2026-06-09T22:32:33.446Z" }, + { url = "https://files.pythonhosted.org/packages/9c/87/c85d147b53323c7eb4d850920c8901377323c2a0ff8d79c262d4fee89aa2/cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691", size = 4713235, upload-time = "2026-06-09T22:31:40.141Z" }, + { url = "https://files.pythonhosted.org/packages/79/58/67cbf8cf1ee7c54b439ca07bbecf8362c07afc11a3724fea70f745784add/cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242", size = 4702323, upload-time = "2026-06-09T22:31:42.191Z" }, + { url = "https://files.pythonhosted.org/packages/89/c6/24266ac10c47f6cd2a865f4446062b466da1d1f10b27189eac00e61bf0c9/cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08", size = 5300085, upload-time = "2026-06-09T22:31:58.703Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bb/cc4b78784f97efc8c5874c2a9743708d172be6663024b34a0467885ae0c8/cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6", size = 4746137, upload-time = "2026-06-09T22:31:31.01Z" }, + { url = "https://files.pythonhosted.org/packages/1f/52/0c44de3f5267f8fbe8e835138017522a333436166e406f0db9b9e6e3033f/cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8", size = 4333867, upload-time = "2026-06-09T22:32:28.096Z" }, + { url = "https://files.pythonhosted.org/packages/9a/2e/772d7adbfa931537bc401640b7cac9976bff689bda187833e5d63b428e49/cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429", size = 4701805, upload-time = "2026-06-09T22:31:38.284Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a3/b06844f303873493c963caf581c04df31c7035e0c1b0f02c4814d319ec80/cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f", size = 5258461, upload-time = "2026-06-09T22:31:04.187Z" }, + { url = "https://files.pythonhosted.org/packages/9f/13/8b765e2e12b07c74941caadb9d1c8fdc006c4dfbf2b8f2d610519758954d/cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f", size = 4745488, upload-time = "2026-06-09T22:32:30.07Z" }, + { url = "https://files.pythonhosted.org/packages/2e/aa/48972bce55049b32a94f4907eda4d75fa385aad8a39506cc2fc72196ecf0/cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41", size = 4830256, upload-time = "2026-06-09T22:31:43.868Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/e5079a032fb85cf6005046ca92bbd78b0c82dad2b5751ab8c311659da06f/cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6", size = 4979117, upload-time = "2026-06-09T22:31:05.845Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a0/8f50cae9c74e718ed769d63ed5c74bd0ea830c9550a74629cebd1b9c7bc7/cryptography-48.0.1-cp311-abi3-win32.whl", hash = "sha256:b9a32b876490d66c8bcc9963ef220199569748434ab01a9d6aaeabf88e7f5158", size = 3304154, upload-time = "2026-06-09T22:32:16.845Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/0572c77dbace6fef72f33755bd52ea399c71367250d366237f8691826b9e/cryptography-48.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:39489bfca54c7a1f6b297efcd8bc608ab92d16c4ca631b0cad4da46724588b24", size = 3817138, upload-time = "2026-06-09T22:32:00.388Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/3e768b4c3bc78201583fa35a0e18f640dd782ff41afba88f8545481a8874/cryptography-48.0.1-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:f817adc181390bd54f2f700107a7419040fb7c1bdf2fc26f36551a06a68c3345", size = 7989830, upload-time = "2026-06-09T22:31:07.8Z" }, + { url = "https://files.pythonhosted.org/packages/8a/13/6476736484b94041110c8340a3eb63962fea4975baea8cb4a512adb44d4d/cryptography-48.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d5d30989c6917b478b5817902e85fddaea2261efa8648383d965381ccb9e1ac4", size = 4689201, upload-time = "2026-06-09T22:31:09.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/62/65a87f34d2a431546e2509b85d55e8c90df86d668f6731da64d538512ac2/cryptography-48.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df637c05205ea7c1d7fbcbe54bbfea648a52951155f997af13d895d0ecc96991", size = 4702822, upload-time = "2026-06-09T22:32:24.409Z" }, + { url = "https://files.pythonhosted.org/packages/7f/59/810b5204b0a9b10f4b6bc06bd551a8b609803cd931806bc3b71884b225e5/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:869c3b8a53bfe27147832df48b32adadf558249d50e76cb3769d40e986b13265", size = 4694875, upload-time = "2026-06-09T22:32:08.737Z" }, + { url = "https://files.pythonhosted.org/packages/24/dc/d8ca05ffea724eec6d232ea6f18e74c269eb6bdfdcc9bfba689790d1325f/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:e361afba8918070d376df76f408a4f67fec0ee9cff81a99e48fe9a233ef59e17", size = 5290385, upload-time = "2026-06-09T22:31:15.212Z" }, + { url = "https://files.pythonhosted.org/packages/03/8c/3be6cb4da181f5bb6c19cf560c2359d60644a6b5fc5b57854e528f47b296/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d069066deead00ac7f090be101be875a06855908f7ec004c27b8fefb4acfb411", size = 4737082, upload-time = "2026-06-09T22:32:22.66Z" }, + { url = "https://files.pythonhosted.org/packages/aa/f6/d5f60a5a1434dbfd949e227fd0065d194c7e6b6ac526b17f5c06152b8231/cryptography-48.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:09f73a725d582cef64b91281a322cd798d14a33b2b6f2b7ad9531dc336d84c02", size = 4325328, upload-time = "2026-06-09T22:32:10.777Z" }, + { url = "https://files.pythonhosted.org/packages/17/b7/ba75dd947a14b6ad907b01ae8f6b5b348cdd1b48142f0063dee9e20c1d9d/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:15254441469dd6bf027039453288e2072124f8b6603563f5d759e1c9b69273fa", size = 4694530, upload-time = "2026-06-09T22:31:53.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/50d6b9e8aff12d8b67afaeb3569335e32dc83a5723e3bbded24fdac9f809/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:8ace4507d1e6533c125f4fac754f8bb8b6a74c08e92179dabd7e16571a3efbf3", size = 5245046, upload-time = "2026-06-09T22:31:25.774Z" }, + { url = "https://files.pythonhosted.org/packages/9f/04/618f4115cfc0add0838c82507aa18a346089428da8653ad38b3ff36f5cb3/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b4e391975f038e66432328639620a4aff2d307513b004f1ca06d6225bced815c", size = 4736660, upload-time = "2026-06-09T22:32:12.676Z" }, + { url = "https://files.pythonhosted.org/packages/24/9c/06e062462a0de28a3b3911322eded4c16deb9f441b1b7575d3dc59488ab5/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42fcd8e26fe555d9b3577a135f5091fefa0aa4e99129c23fb56787a1bd4ada72", size = 4822229, upload-time = "2026-06-09T22:31:17.062Z" }, + { url = "https://files.pythonhosted.org/packages/f4/be/0561971eaaee4b8a0e7d5113c536921063ab91aaf23278ac374eaf881e11/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1400da5e32a43253392277eac7490a60e497d810a63dd5608d71bbd7af507c9", size = 4966364, upload-time = "2026-06-09T22:31:32.842Z" }, + { url = "https://files.pythonhosted.org/packages/a4/27/728c77876f12b000820b69ae490f3c4083775e79e07827e9e60be07ad209/cryptography-48.0.1-cp314-cp314t-win32.whl", hash = "sha256:0df56b056bc17c1b7d6821dfa65216e62bd232d8ab05eb3db44e71d235651471", size = 3278498, upload-time = "2026-06-09T22:31:29.154Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/79a612c6d7b1e6ee0edd43633d53035bec2cfb78c82b76f7864f39e36f34/cryptography-48.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:9de21387aa95e2a895823d0745b430bed4f33503ba9ab5e0b5311f33e37d66d2", size = 3798790, upload-time = "2026-06-09T22:31:56.697Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6c/00fa2a95997164c8b2072ce327c23d4ab20809ccc323ea5fab91e53a4bba/cryptography-48.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:4fdc69f8e4316bcf0c8c8ec1f26f285d12e8142d88d96c876a59a03be3f6ae67", size = 7987408, upload-time = "2026-06-09T22:32:20.777Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d9/45f309a7e4e5f3f8f121d6d3be9e94024a7726ec598d6e08ae04edb2f04d/cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8", size = 4690196, upload-time = "2026-06-09T22:31:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9f/a1bc8bcc798811b8527eb374bbccf30a3f3e806829d967118222bf1125eb/cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a", size = 4696782, upload-time = "2026-06-09T22:31:45.615Z" }, + { url = "https://files.pythonhosted.org/packages/66/c2/81a4fb4e4373c500bb526bc337ac5719dd31dd15b970b84a238168c6aa08/cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577", size = 4696618, upload-time = "2026-06-09T22:31:11.564Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0b/aa68b221dde92d09cb29a024ede17550ee21e77a404e59fc093c82bb51e1/cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1", size = 5289970, upload-time = "2026-06-09T22:31:20.368Z" }, + { url = "https://files.pythonhosted.org/packages/78/13/fba657f958d2af66ea959a4ba01212632089249d34af1ae48054136344d7/cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d", size = 4731873, upload-time = "2026-06-09T22:31:22.253Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4c/9a964756d24a26b3e34dfcb16f961b89838786e6700b635b0d1e3adff4b6/cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6", size = 4330804, upload-time = "2026-06-09T22:31:36.56Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0f/a10f3a6eb12950a10e3a874070283aa2dd5875b2bfd15fad8a3e17b3f13e/cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46", size = 4696217, upload-time = "2026-06-09T22:31:13.351Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6f/5cd12f951165ea73ef85266775d97e4c763b2474ccfd816dd69d3a18d6f8/cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401", size = 5245252, upload-time = "2026-06-09T22:32:02.193Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/8aaa12e4516ec4464033ab79b6f3b592bd5a92102467c4ace8a0d970203f/cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b", size = 4731388, upload-time = "2026-06-09T22:32:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/1b/24/50027ea4dca85ec1f40688f3c24fb32ccacd520583c9592c3cc95628e6fb/cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1", size = 4824186, upload-time = "2026-06-09T22:32:18.707Z" }, + { url = "https://files.pythonhosted.org/packages/52/41/04cb5eb17085ade6f50cc611fb657df6a0f5885350de8764ece89c050197/cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475", size = 4964539, upload-time = "2026-06-09T22:31:18.793Z" }, + { url = "https://files.pythonhosted.org/packages/36/bf/ed70785c496e89d7e73b7cda2d21f2447fd6d4e821714b8d04ff217fed92/cryptography-48.0.1-cp39-abi3-win32.whl", hash = "sha256:6b2c0c3e6ccf3ade7750f836ef3ee36eea250cc467d45c256895573ac08cc6f1", size = 3282307, upload-time = "2026-06-09T22:30:53.162Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ff/371ea7d252656ee1eb6d83eeeef3d1d0c6baf1d6497687d081ea03814670/cryptography-48.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:9a49ca6c81417f6a5edb50375a60cccdd70fa0a91a5211829dbea74eba94d2ac", size = 3793408, upload-time = "2026-06-09T22:32:15.191Z" }, +] + +[[package]] +name = "fastapi" +version = "0.136.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hr-mcp-server" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "azure-monitor-opentelemetry" }, + { name = "fastapi" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.metadata] +requires-dist = [ + { name = "azure-monitor-opentelemetry", specifier = ">=1.6.0" }, + { name = "fastapi", specifier = ">=0.124.0" }, + { name = "pyjwt", extras = ["crypto"], specifier = ">=2.12.1" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.38.0" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + +[[package]] +name = "isodate" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, +] + +[[package]] +name = "msal" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/99/d840198ecf6e8057bbc937f129ae940404485d736cda73253bbff9537f01/msal-1.37.0.tar.gz", hash = "sha256:1b1672a33ee467c1d70b341bb16cafd51bb3c817147a95b93263794b03971bec", size = 182444, upload-time = "2026-05-29T19:49:05.561Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/b0/d807279f4b55d16d1f120d5ac4344c6e39b56732e2a224d40bded7fd67ad/msal-1.37.0-py3-none-any.whl", hash = "sha256:dd17e95a7c71bce75e8108113438ba7c4a086b3bcad4f57a8c09b7af3d753c2d", size = 123725, upload-time = "2026-05-29T19:49:04.335Z" }, +] + +[[package]] +name = "msal-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315, upload-time = "2025-03-14T23:51:03.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583, upload-time = "2025-03-14T23:51:03.016Z" }, +] + +[[package]] +name = "msrest" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "certifi" }, + { name = "isodate" }, + { name = "requests" }, + { name = "requests-oauthlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332, upload-time = "2022-06-13T22:41:25.111Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384, upload-time = "2022-06-13T22:41:22.42Z" }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "packaging" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/37/6bf8e66bfcee5d3c6515b79cb2ee9ad05fe573c20f7ceb288d0e7eeec28c/opentelemetry_instrumentation-0.61b0.tar.gz", hash = "sha256:cb21b48db738c9de196eba6b805b4ff9de3b7f187e4bbf9a466fa170514f1fc7", size = 32606, upload-time = "2026-03-04T14:20:16.825Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/3e/f6f10f178b6316de67f0dfdbbb699a24fbe8917cf1743c1595fb9dcdd461/opentelemetry_instrumentation-0.61b0-py3-none-any.whl", hash = "sha256:92a93a280e69788e8f88391247cc530fd81f16f2b011979d4d6398f805cfbc63", size = 33448, upload-time = "2026-03-04T14:19:02.447Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-asgi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/3e/143cf5c034e58037307e6a24f06e0dd64b2c49ae60a965fc580027581931/opentelemetry_instrumentation_asgi-0.61b0.tar.gz", hash = "sha256:9d08e127244361dc33976d39dd4ca8f128b5aa5a7ae425208400a80a095019b5", size = 26691, upload-time = "2026-03-04T14:20:21.038Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/78/154470cf9d741a7487fbb5067357b87386475bbb77948a6707cae982e158/opentelemetry_instrumentation_asgi-0.61b0-py3-none-any.whl", hash = "sha256:e4b3ce6b66074e525e717efff20745434e5efd5d9df6557710856fba356da7a4", size = 16980, upload-time = "2026-03-04T14:19:10.894Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-dbapi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/ed/ba91c9e4a3ec65781e9c59982109f0a36de9fa574f622596b33d1985dab5/opentelemetry_instrumentation_dbapi-0.61b0.tar.gz", hash = "sha256:02fa800682c1de87dcad0e59f2092b3b6fb8b8ea0636518f989e1166b418dcb9", size = 16761, upload-time = "2026-03-04T14:20:29.782Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/a5/d26c68f3fd33eb7410985cef7700bb426e2c4a26de9207902cbbffb19a3f/opentelemetry_instrumentation_dbapi-0.61b0-py3-none-any.whl", hash = "sha256:8f762c39c8edd20c6aef3282550a2cfbfec76c3f431bf5c36327dcf9ece2e5a0", size = 14134, upload-time = "2026-03-04T14:19:24.718Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-django" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/ef/6bc1a6560630f26b1c010af86b28f42bfbe6a601bd1647d1436e0d3436aa/opentelemetry_instrumentation_django-0.61b0.tar.gz", hash = "sha256:9885154dc128578de0e6b5ce49e965c786f8ab071175bec005dcd454510be951", size = 25996, upload-time = "2026-03-04T14:20:30.453Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/3b/74dad6d98fdee1d137f1c2748548d4159578508f21e3aef581c110e64041/opentelemetry_instrumentation_django-0.61b0-py3-none-any.whl", hash = "sha256:26c1b0b325a9783d4a2f4df660ba05cf929c3eda2ae9b07916b649bb44e1c5b6", size = 20773, upload-time = "2026-03-04T14:19:25.675Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-fastapi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-asgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/35/aa727bb6e6ef930dcdc96a617b83748fece57b43c47d83ba8d83fbeca657/opentelemetry_instrumentation_fastapi-0.61b0.tar.gz", hash = "sha256:3a24f35b07c557ae1bbc483bf8412221f25d79a405f8b047de8b670722e2fa9f", size = 24800, upload-time = "2026-03-04T14:20:32.759Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/05/acfeb2cccd434242a0a7d0ea29afaf077e04b42b35b485d89aee4e0d9340/opentelemetry_instrumentation_fastapi-0.61b0-py3-none-any.whl", hash = "sha256:a1a844d846540d687d377516b2ff698b51d87c781b59f47c214359c4a241047c", size = 13485, upload-time = "2026-03-04T14:19:30.351Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-flask" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/33/d6852d8f2c3eef86f2f8c858d6f5315983c7063e07e595519e96d4c31c06/opentelemetry_instrumentation_flask-0.61b0.tar.gz", hash = "sha256:e9faf58dfd9860a1868442d180142645abdafc1a652dd73d469a5efd106a7d49", size = 24071, upload-time = "2026-03-04T14:20:33.437Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/41/619f3530324a58491f2d20f216a10dd7393629b29db4610dda642a27f4ed/opentelemetry_instrumentation_flask-0.61b0-py3-none-any.whl", hash = "sha256:e8ce474d7ce543bfbbb3e93f8a6f8263348af9d7b45502f387420cf3afa71253", size = 15996, upload-time = "2026-03-04T14:19:31.304Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-logging" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/e0/69473f925acfe2d4edf5c23bcced36906ac3627aa7c5722a8e3f60825f3b/opentelemetry_instrumentation_logging-0.61b0.tar.gz", hash = "sha256:feaa30b700acd2a37cc81db5f562ab0c3a5b6cc2453595e98b72c01dcf649584", size = 17906, upload-time = "2026-03-04T14:20:37.398Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/0e/2137db5239cc5e564495549a4d11488a7af9b48fc76520a0eea20e69ddae/opentelemetry_instrumentation_logging-0.61b0-py3-none-any.whl", hash = "sha256:6d87e5ded6a0128d775d41511f8380910a1b610671081d16efb05ac3711c0074", size = 17076, upload-time = "2026-03-04T14:19:36.765Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-psycopg2" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-dbapi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/28/f28d52b1088e7a09761566f8700507b54d3d83a6f9c93c0ce02f53619e83/opentelemetry_instrumentation_psycopg2-0.61b0.tar.gz", hash = "sha256:863ccf9687b71e73dd489c7bb117278768bdf26aa0dafe7dc974a2425e05b5d7", size = 11676, upload-time = "2026-03-04T14:20:41.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/f1/4341d0584c288765c73e28c30ba58e7aedb50c01108f17f947b872657f79/opentelemetry_instrumentation_psycopg2-0.61b0-py3-none-any.whl", hash = "sha256:36b96983beda05c927179bb66b6c72f07a8d9a591f76ce9da88b1dd1587cb083", size = 11491, upload-time = "2026-03-04T14:19:42.018Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-requests" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/c7/7a47cb85c7aa93a9c820552e414889185bcf91245271d12e5d443e5f834d/opentelemetry_instrumentation_requests-0.61b0.tar.gz", hash = "sha256:15f879ce8fb206bd7e6fdc61663ea63481040a845218c0cf42902ce70bd7e9d9", size = 18379, upload-time = "2026-03-04T14:20:46.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/a1/a7a133b273d1f53950f16a370fc94367eff472c9c2576e8e9e28c62dcc9f/opentelemetry_instrumentation_requests-0.61b0-py3-none-any.whl", hash = "sha256:cce19b379949fe637eb73ba39b02c57d2d0805447ca6d86534aa33fcb141f683", size = 14207, upload-time = "2026-03-04T14:19:51.765Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/37/77cd326b083390e74280c08bbd585153809619dad068e2d1b253fec1164d/opentelemetry_instrumentation_urllib-0.61b0.tar.gz", hash = "sha256:6a15ff862fc1603e0ea5ea75558f76f36436b02e0ae48daecedcb5e574cce160", size = 16894, upload-time = "2026-03-04T14:20:52.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/fc/a88fbfd8b9eb16ba1c21f0514c12696441be7fc42c7e319f3ee793bf9e96/opentelemetry_instrumentation_urllib-0.61b0-py3-none-any.whl", hash = "sha256:d7e409876580fb41102e3522ce81a756e53a74073c036a267a1c280cc0fa09b0", size = 13970, upload-time = "2026-03-04T14:20:01.24Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib3" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/80/7ad8da30f479c6117768e72d6f2f3f0bd3495338707d6f61de042149578a/opentelemetry_instrumentation_urllib3-0.61b0.tar.gz", hash = "sha256:f00037bc8ff813153c4b79306f55a14618c40469a69c6c03a3add29dc7e8b928", size = 19325, upload-time = "2026-03-04T14:20:53.386Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/0c/01359e55b9f2fb2b1d4d9e85e77773a96697207895118533f3be718a3326/opentelemetry_instrumentation_urllib3-0.61b0-py3-none-any.whl", hash = "sha256:9644f8c07870266e52f129e6226859ff3a35192555abe46fa0ef9bbbf5b6b46d", size = 14339, upload-time = "2026-03-04T14:20:02.681Z" }, +] + +[[package]] +name = "opentelemetry-instrumentation-wsgi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/e5/189f2845362cfe78e356ba127eab21456309def411c6874aa4800c3de816/opentelemetry_instrumentation_wsgi-0.61b0.tar.gz", hash = "sha256:380f2ae61714e5303275a80b2e14c58571573cd1fddf496d8c39fb9551c5e532", size = 19898, upload-time = "2026-03-04T14:20:54.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628, upload-time = "2026-03-04T14:20:03.934Z" }, +] + +[[package]] +name = "opentelemetry-resource-detector-azure" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503, upload-time = "2024-05-16T21:54:58.994Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252, upload-time = "2024-05-16T21:54:57.208Z" }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252, upload-time = "2026-03-04T14:17:31.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951, upload-time = "2026-03-04T14:17:17.961Z" }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755, upload-time = "2026-03-04T14:17:32.664Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" }, +] + +[[package]] +name = "opentelemetry-util-http" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/3c/f0196223efc5c4ca19f8fad3d5462b171ac6333013335ce540c01af419e9/opentelemetry_util_http-0.61b0.tar.gz", hash = "sha256:1039cb891334ad2731affdf034d8fb8b48c239af9b6dd295e5fabd07f1c95572", size = 11361, upload-time = "2026-03-04T14:20:57.01Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/e5/c08aaaf2f64288d2b6ef65741d2de5454e64af3e050f34285fb1907492fe/opentelemetry_util_http-0.61b0-py3-none-any.whl", hash = "sha256:8e715e848233e9527ea47e275659ea60a57a75edf5206a3b937e236a6da5fc33", size = 9281, upload-time = "2026-03-04T14:20:08.364Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, +] + +[[package]] +name = "starlette" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.49.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/1f/fa18009dea8469069cca78a4e877a008ab78f08b064bfc9ab891579077ff/uvicorn-0.49.0.tar.gz", hash = "sha256:ebf4271aa580d9de97f93192d4595176df6e91f9aae919ca73e4fc07df1e66a3", size = 91284, upload-time = "2026-06-03T22:01:30.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/fa/e1388bbcf24ef3274f45c0c1c7b501fd14971037c1b6ee23610553307497/uvicorn-0.49.0-py3-none-any.whl", hash = "sha256:ba3d14c3ee7e41c6c654c46c9eb489d33213cdd30aa1696eab1374337c13f68f", size = 71376, upload-time = "2026-06-03T22:01:29.037Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, +] + +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + +[[package]] +name = "wrapt" +version = "1.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, +] + +[[package]] +name = "zipp" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/d8/eab98a517c14134c0b2eb4e2387bc5f457334293ec5d2dd3857ec2966802/zipp-4.1.0.tar.gz", hash = "sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602", size = 26214, upload-time = "2026-05-18T20:08:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/13/547360d81e6d88d58492968ffda9f9542854f11310ee556fef14260cc886/zipp-4.1.0-py3-none-any.whl", hash = "sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f", size = 10238, upload-time = "2026-05-18T20:08:57.045Z" }, +] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/pyproject.toml b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/pyproject.toml new file mode 100644 index 000000000..9b067e427 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "workshop" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "agent-framework-core>=1.2.0", + "agent-framework-openai>=1.2.0", + "agent-framework-foundry-hosting>=1.0.0a260507", + "azure-ai-projects>=2.1.0", + "azure-cosmos>=4.9.0", + "azure-identity>=1.25.3", + "azure-keyvault>=4.2.0", + "azure-mgmt-apimanagement>=5.0.0", + "azure-monitor-opentelemetry>=1.6.0", + "ipykernel>=7.2.0", + "langchain>=1.2.15", + "langchain-openai>=1.2.1", + "matplotlib>=3.10.9", + "openai>=2.32.0", + "opentelemetry-semantic-conventions-ai>=0.5.1", + "pandas>=3.0.2", +] diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/readme.md b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/readme.md new file mode 100644 index 000000000..36495206b --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/readme.md @@ -0,0 +1,994 @@ +# Citadel AI Governance Hub - Hands-On Workshop Lab Guide 🏛️ + +**Duration:** ~3 hours +**Level:** Intermediate +**Last updated:** June 2026 + +This hands-on lab deploys a governed AI hub-and-spoke architecture into your Azure subscription: + +- **Citadel Hub** — the shared governance plane centered on API Management, observability, policy enforcement, and core AI platform resources. +- **Citadel Spoke** — a sample Azure AI Foundry workload that connects to the hub as a governed use case. +- **Lab environment** — your local machine or the included Devcontainer, where deployment commands, notebooks, and validation traffic originate. + +You deploy the hub with `azd up`, then deploy the sample spoke with `workshop/scripts/deploy-spoke-foundry.*`. + +--- + +## Table of Contents + +1. [Workshop Overview](#1-workshop-overview) +2. [Pre-Requisites](#2-pre-requisites) +3. [Lab 1 — Deploy Citadel to Your Azure Subscription](#lab-1) +4. [Lab 2 — Review Deployed Services and Configuration](#lab-2) +5. [Lab 3 — Run Validation Notebooks](#lab-3) +6. [Lab 4 — Observability: Metrics, Logs, and Telemetry](#lab-4) +7. [Clean Up](#7-clean-up) +8. [Troubleshooting](#8-troubleshooting) + +--- + +## 1. Workshop Overview + +In this hands-on workshop you will: + +1. **Deploy** the Citadel Hub to your own Azure subscription using Azure Developer CLI (`azd`) +2. **Deploy** a sample Citadel Spoke by running the `workshop/scripts/deploy-spoke-foundry.*` script after `azd up` completes +3. **Review** the deployed Azure services and their configuration — focusing on API Management as the unified AI gateway between clients and AI backends +4. **Run** validation notebooks from your lab environment to exercise Citadel capabilities (backend onboarding, access contracts, PII processing, etc.) +5. **Observe** telemetry generated by your notebook runs — explore APIM metrics, Application Insights, Log Analytics, and Cosmos DB usage records + +### What is Citadel? + +Citadel Governance Hub is an enterprise-grade AI landing zone that provides a centralized, governable, and observable control plane for AI consumption. It uses Azure API Management as a unified AI gateway with policy-driven governance, usage tracking, PII detection, content safety, and multi-model routing. + +### Workshop Timeline (Approximate) + +| Time | Activity | +|------|----------| +| 0:00 – 0:15 | Verify pre-requisites, environment setup | +| 0:15 – 1:00 | **Lab 1** — Deploy Citadel Hub (`azd up`) + Spoke (script) | +| 1:00 – 1:30 | **Lab 2** — Review deployed services in Azure Portal | +| 1:30 – 2:30 | **Lab 3** — Run validation notebooks | +| 2:30 – 3:00 | **Lab 4** — Explore observability and telemetry | + +> ℹ️ **Note:** Deployment takes 30-45 minutes. Use that time to read through Lab 2 and Lab 3 so you are ready when deployment completes. + +--- + +## 2. Pre-Requisites + +Complete these steps **before** the workshop day to ensure a smooth experience. + +> ✅ **Assumed knowledge:** This workshop assumes participants have a working understanding of the Azure services used by Citadel (API Management, AI Foundry, Cosmos DB, Key Vault, Event Hub, etc.) and are comfortable using the tools listed below. You do not need to be a subject-matter expert, but Azure fundamentals are outside the scope of this workshop. + +### 2.1 Azure Requirements + +| Requirement | Details | +|-------------|---------| +| **Azure Subscription** | You need an Azure subscription where you can deploy resources at subscription scope | +| **Deployment Permissions** | Use **Owner** permissions, or **Contributor** plus **User Access Administrator**, because `azd up` creates managed identities and assigns RBAC roles | +| **Sufficient Quota** | Quota for Azure OpenAI / AI Foundry model deployments (GPT-4.1, DeepSeek-R1, etc.) in the target region | +| **Resource Providers** | Several resource providers must be registered (see [Section 2.4](#24-register-azure-resource-providers)) | + +
+Why these deployment permissions are required + +The deployment creates user-assigned managed identities for API Management and usage-processing workloads (`id-apim-*` and `id-logicapp-*` by default). It also enables system-assigned identities on services such as AI Foundry projects and the Logic App. + +During `azd up`, Bicep assigns roles to these identities, including: + +- **Cognitive Services User** and **Cognitive Services OpenAI User** for the APIM managed identity. +- **Azure Event Hubs Data Sender** for the APIM managed identity. +- **Azure Event Hubs Data Owner** and **Monitoring Reader** for the usage-processing Logic App identity. +- **Cosmos DB Built-in Data Contributor** on the Cosmos DB SQL account for usage ingestion. +- **Key Vault Secrets User** for APIM and AI Foundry identities. +- **Key Vault Certificates Officer** for AI Foundry identities. +- **Azure AI Project Manager** for the deployer on AI Foundry resources. + +Creating these role assignments requires `Microsoft.Authorization/roleAssignments/write`, which is included in **Owner** or **User Access Administrator**. + +
+ +### 2.2 Lab Environment Requirements + +You can run the workshop from your local machine or from the included Devcontainer. + +
+Option A — Local machine + +| Tool | Purpose | Install Link | +|------|---------|-------------| +| **Azure CLI** (`az`) | Authenticate and manage Azure resources | [Install Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) | +| **Azure Developer CLI** (`azd`) | Deploy Citadel infrastructure | [Install azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) | +| **Python 3.13+** | Run validation notebooks | [python.org](https://www.python.org/downloads/) | +| **VS Code** | Code editor and notebook runner | [code.visualstudio.com](https://code.visualstudio.com/) | +| **Git** | Clone the repository | [git-scm.com](https://git-scm.com/downloads) | + +**VS Code Extensions (recommended):** +- Python +- Jupyter +- Bicep + +
+ +
+Option B — Devcontainer + +Use the included [Devcontainer](../.devcontainer/devcontainer.json) for a preconfigured VS Code environment with Azure CLI, Azure Developer CLI, Python 3.13, Jupyter, Bicep, Node.js, Git, and workshop dependencies. + +1. Install [VS Code](https://code.visualstudio.com/), [Docker Desktop](https://www.docker.com/products/docker-desktop/), and the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers). +2. Open the repository root in VS Code. +3. Start Docker Desktop and wait until Docker is running. +4. Choose **Reopen in Container** when prompted, or run **Dev Containers: Reopen in Container** from the command palette. +5. After the container starts, run `az login` and `azd auth login` before provisioning or running notebooks. + +
+ +### 2.3 Network Requirements + +- Ability to connect to the Internet and Azure services +- You can use Wi-Fi provided by the workshop organizer or your own connectivity (e.g. if you are running this lab from home) + +### 2.4 Register Azure Resource Providers + +Some Azure resource providers need to be registered before deployment. Run these commands in your terminal: + +```bash +# Login to Azure +az login + +# Select your subscription +az account set --subscription "" + +# Register required resource providers +az provider register --namespace Microsoft.AlertsManagement +az provider register --namespace Microsoft.ApiManagement +az provider register --namespace Microsoft.CognitiveServices +az provider register --namespace Microsoft.DocumentDB +az provider register --namespace Microsoft.EventHub +az provider register --namespace Microsoft.Insights +az provider register --namespace Microsoft.KeyVault +az provider register --namespace Microsoft.MachineLearningServices +az provider register --namespace Microsoft.ManagedIdentity +az provider register --namespace Microsoft.Network +az provider register --namespace Microsoft.OperationalInsights +az provider register --namespace Microsoft.Storage +az provider register --namespace Microsoft.Web +az provider register --namespace Microsoft.Logic +az provider register --namespace Microsoft.Cache + +# Verify registration (may take a few minutes to complete) +az provider list --query "[?registrationState=='Registered'].namespace" -o table +``` + +### 2.5 Verify Tool Installation + +Run these commands to confirm all tools are installed: + +```bash +az --version +azd version +python --version +git --version +code --version +``` + +--- + + + +## 3. Lab 1 — Deploy Citadel to Your Azure Subscription + +**Goal:** Deploy the Citadel Hub first, then deploy a sample Citadel Spoke that connects to it. +**Estimated time:** ~45 minutes + +### 3.1 Clone the Repository + +```bash +git clone https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator.git +cd ai-hub-gateway-solution-accelerator +git checkout workshop +``` + +
+Alternative — initialize with azd init + +```bash +mkdir citadel-workshop && cd citadel-workshop +azd init --template https://github.com/mohamedsaif/ai-hub-gateway-solution-accelerator.git -e citadel-workshop --branch workshop +``` + +
+ +### 3.2 Authenticate to Azure + +```bash +# Login to Azure CLI +az login + +# Login to Azure Developer CLI (uses the same browser auth) +azd auth login + +# Confirm you are on the correct subscription +az account show --output table +``` + +If you need to target a specific tenant: +```bash +az login --tenant-id +azd auth login --tenant-id +``` + +### 3.3 Create a New Environment + +```bash +azd env new citadel-workshop +``` + +### 3.4 Configure Environment (Optional) + +> **This section is optional** and can be skipped for the lab. It is provided in case you want to experiment with specific settings. + +
+Optional — configure environment settings + +The defaults work well for the workshop. However, you may want to set the Azure region: + +```bash +azd env set AZURE_LOCATION swedencentral +``` + +> 💡 **Tip:** Choose a region that has Azure OpenAI quota available for your subscription. Common choices: `swedencentral`, `eastus`, `eastus2`. + +To reduce costs during the workshop, you can optionally disable components you won't test: + +```bash +# Optional — disable components to speed up deployment and reduce costs +azd env set ENABLE_API_CENTER false +azd env set ENABLE_MANAGED_REDIS false +azd env set ENABLE_AZURE_AI_SEARCH false +``` + +
+ +### 3.5 Deploy + +> **Recommended region:** For this lab, **Sweden Central** (`swedencentral`) is the recommended region. + +```bash +azd up +``` + +The CLI will prompt you to: +1. Select your Azure subscription +2. Confirm the Azure region + +Then the deployment begins. **This takes approximately 30-45 minutes.** + +> ⏳ **While waiting:** Read ahead through Lab 2 and Lab 3 to familiarize yourself with the deployed services and validation notebooks. + +> 🔁 **Transient errors:** Deployment of some resources, such as model deployments, may occasionally fail with a transient error. If `azd up` fails, re-run the command — it will pick up where it left off. + +If your deployment uses the **Developer** APIM SKU (APIM_SKU="Developer"), run the APIM cleanup script immediately after `azd up` completes. The script checks the live APIM SKU and only removes the default APIM products and subscriptions when the SKU is `Developer`. + +
+🪟 PowerShell + +```powershell +.\workshop\scripts\cleanup-apim-defaults.ps1 +``` + +
+ +
+💻 Bash + +```bash +./workshop/scripts/cleanup-apim-defaults.sh +``` + +
+ +### 3.6 Verify Deployment + +Once `azd up` completes: + +- The **Citadel Hub** is deployed. +- The sample **Citadel Spoke** is **not** deployed yet. + +```bash +# View deployment outputs (resource names, endpoints, etc.) +azd env get-values +``` + +### 3.7 Deploy the Sample Spoke + +After `azd up` finishes, run the spoke deployment script from the repository root. Pass a unique spoke suffix so the script creates a distinct spoke resource group and unique resource names for the Foundry account, Key Vault, Log Analytics workspace, Application Insights, and Azure Container Registry. + +
+🪟 PowerShell + +```powershell +.\workshop\scripts\deploy-spoke-foundry.ps1 -SpokeSuffix 1 +``` + +
+ +
+💻 Bash + +```bash +./workshop/scripts/deploy-spoke-foundry.sh --spoke-suffix 1 +``` + +
+ +This step deploys a sample Citadel Spoke: a standalone Azure AI Foundry account and project that represent an onboarded workload (use case). For example, if the hub resource group is `rg-citadel-demo-1`, `-SpokeSuffix 1` creates a spoke resource group named `rg-citadel-demo-1-spoke-1`. To create another spoke without editing the scripts, rerun the same command with a different suffix: + +
+🪟 PowerShell + +```powershell +.\workshop\scripts\deploy-spoke-foundry.ps1 -SpokeSuffix 2 +.\workshop\scripts\deploy-spoke-foundry.ps1 -SpokeSuffix 10 +``` + +
+ +
+💻 Bash + +```bash +./workshop/scripts/deploy-spoke-foundry.sh --spoke-suffix 2 +./workshop/scripts/deploy-spoke-foundry.sh --spoke-suffix 10 +``` + +
+ +
+Advanced — override generated spoke names + +The suffix only affects generated defaults. Advanced users can still override individual names with environment variables such as `SPOKE_RESOURCE_GROUP_NAME`, `FOUNDRY_ACCOUNT_NAME`, `KEY_VAULT_NAME`, `SPOKE_LOG_ANALYTICS_NAME`, `SPOKE_APP_INSIGHTS_NAME`, and `SPOKE_ACR_NAME`. + +
+ +### 3.8 Verify Deployment + +Once the script completes, there is no need to note down any values — the notebooks read directly from `azd env` variables at runtime. + +You can verify in the Azure Portal: +1. Go to [portal.azure.com](https://portal.azure.com) +2. Navigate to the hub resource group and confirm the shared Citadel resources are deployed (APIM, Event Hub, Cosmos DB, Key Vault, Logic App, AI Foundry, etc.) +3. Navigate to the spoke resource group created by the script and confirm the sample Azure AI Foundry resources are deployed there + +--- + + + +## 4. Lab 2 — Review Deployed Services and Configuration + +**Goal:** Understand the services Citadel deploys and how they are configured. +**Estimated time:** ~30 minutes + +### 4.1 Resource Group Overview + +Navigate to your resource group in the Azure Portal. You should see resources including: + +| Resource Type | Name Pattern | Purpose | +|---------------|-------------|---------| +| API Management | `apim-*` | Unified AI gateway — the heart of Citadel Hub | +| AI Foundry | `aif-*` | Hosts AI model deployments (GPT-4.1, DeepSeek-R1, etc.) | +| Application Insights | `appi-apim-*` | APIM performance monitoring and telemetry | +| Cosmos DB | `cosmos-*` | Usage record storage for analytics | +| Event Hub | `evhns-*` | Real-time usage data streaming | +| Key Vault | `kv-*` | Secrets and certificates management | +| Log Analytics | `log-*` | Centralized log collection | +| Logic App | `logic-*` | Usage ingestion workflow processing | +| Virtual Network | `vnet-*` | Network isolation and private endpoints | +| Managed Identity | `id-*` | Service-to-service authentication (zero credentials) | + +### 4.2 Explore AI Foundry + +> ℹ️ **Note:** After `azd up`, the AI Foundry resources in Citadel Hub have public network access disabled by design. Related Foundry projects are not accessible through the Foundry portal, and users will see **"Private network access required"**. This is expected. + +
+Optional — explore deployed models in the Foundry portal + +If you would like to explore the deployed models at the end of the lab: + +1. Go to the **Foundry resource**. +2. Open **Resource Management** → **Networking**. +3. Change access to **All networks**. +4. Open the related project in the Foundry portal. +5. Go to **Build** → **Models** to view deployed models. + +We recommend doing this at the end of the workshop to minimize context switching. + +
+ +### 4.3 Explore API Management + +API Management is the core of Citadel. Explore these areas: + +#### APIs +1. Navigate to your **API Management** resource → **APIs** +2. You should see: + - **Azure OpenAI API** — Standard Azure OpenAI compatible endpoint + - **Universal LLM API** — Citadel's universal inference endpoint + - **Unified AI API** — Multi-provider wildcard API (if enabled) +3. Go to **Universal LLM API** → **Settings**. Scroll down to **Diagnostics Logs** and select **Azure Monitor**. Observe that **"Log LLM messages"** is enabled. This is also the case with other APIs — Azure OpenAI API and Unified AI API. + +> 🔎 **Optional:** Select an API, such as **Universal LLM API**, open the **Test** tab, and search for the "chat" operation ("Gets chat completions for the provided chat messages."). In the request body, change the `model` field from `gpt-4o` to `gpt-4.1`. Click **Send** and observe the reply. + +#### Products +1. Go to **Products** — each product represents an "access contract" for a team/application + +> ℹ️ **Note:** Additional products will be created when you run the validation notebooks. + +#### Policies +1. Select an API (e.g. Universal LLM API) → Select **All operations** → **Inbound processing** → **Policy code editor** +2. Review the XML policies — pay attention to `` policy blocks. Note down the policy block with `fragment-id="set-llm-usage"`. In the next step, go to **APIs** → **Policy Fragments**, select the **set-llm-usage** policy fragment, then go to **Settings** → **Policy editor** to review its policy code. + +#### Backends +1. Go to **Backends** — see registered AI Foundry / OpenAI backends +2. Note the circuit breaker configuration on LLM backends (not all backends have circuit breakers — only the LLM-related ones) +3. Go to **Load balancer** — see how backends are grouped into pools for load distribution + +### 4.4 Explore Supporting Services + +**Cosmos DB:** +1. Go to **Settings** → **Networking** and enable **Selected networks**. Under the Firewall option, add your current IP address and/or accept connections from within public Azure datacenters. +2. Note that this is only for the purposes of this lab. In a real implementation, networking should be configured to fit your specific networking setup. + +**Key Vault:** +1. Review the Key Vault resource in the **spoke resource group** — note that there are no secrets stored immediately after `azd up` +2. As part of the notebooks execution, secrets (such as access contract subscription keys) will be created here + +--- + + + +## 5. Lab 3 — Run Validation Notebooks + +**Goal:** Exercise Citadel's capabilities by running validation notebooks. +**Estimated time:** ~60 minutes + +### 5.1 Set Up Python Environment + +If you are using the Devcontainer, the post-create setup installs the workshop dependencies automatically. Confirm the selected Python interpreter points to `workshop/.venv`, then continue to [Section 5.2](#52-open-notebooks-in-vs-code). + +Navigate to the `workshop` folder in your terminal: + +```bash +cd workshop +``` + +
+Option A — Using uv (recommended) + +```bash +# Install uv if you don't have it +# Windows: +# powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +# macOS/Linux: +# curl -LsSf https://astral.sh/uv/install.sh | sh + +# Create environment and install dependencies +uv sync +``` + +
+ +
+Option B — Using pip + +> ⚠️ **Important:** This workshop requires **Python 3.13 or later**. Install it from [python.org](https://www.python.org/downloads/) if you don't have it. + +```bash +# Create a virtual environment with Python 3.13 +# Windows: +py -3.13 -m venv .venv +# macOS/Linux: +# python3.13 -m venv .venv + +# Activate it +# Windows: +.venv\Scripts\activate +# macOS/Linux: +# source .venv/bin/activate + +# Install dependencies +pip install -r requirements.txt +``` + +
+ +### 5.2 Open Notebooks in VS Code + +1. Open the `workshop` folder in VS Code +2. Open a notebook file (`.ipynb`) - see **5.4 Recommended Notebook Execution Order** +3. Select the Python kernel from your virtual environment (`.venv` or `uv` managed). In the Devcontainer, use the preconfigured `workshop/.venv` interpreter. + +### 5.3 Configure Notebook Variables + +All required variables (resource group name, Azure region, APIM endpoint, etc.) are **automatically configured** in the notebooks. The initialization cell (Step 0) in each notebook retrieves these values from your `azd` environment and Azure resource metadata at runtime — you do not need to manually set anything. + +Take a moment to review how this works in the code: the notebooks use `azd env get-values` and Azure CLI commands to discover resource names and endpoints, then inject them as Python variables used throughout the notebook. + +### 5.4 Recommended Notebook Execution Order + +The workshop includes a subset of validation notebooks. Execute them in this order: + +| Order | Notebook | File | Details | +|-------|----------|------|---------| +| 1 | LLM Backend Onboarding Runner | [`1. llm-backend-onboarding-runner.ipynb`](1.%20llm-backend-onboarding-runner.ipynb) | [Validation guide](../validation/README.md#1-llm-backend-onboarding-runner) | +| 2 | Universal LLM API — All-Models Tests | [`2. citadel-universal-llm-api-all-models-tests.ipynb`](2.%20citadel-universal-llm-api-all-models-tests.ipynb) | [Validation guide](../validation/README.md#2-universal-llm-api--all-models-tests) | +| 3 | Citadel Access Contracts Tests | [`3. citadel-access-contracts-tests.ipynb`](3.%20citadel-access-contracts-tests.ipynb) | [Validation guide](../validation/README.md#3-citadel-access-contracts-tests) | +| 4 | Citadel Agent Frameworks Tests | [`4. citadel-agent-frameworks-tests.ipynb`](4.%20citadel-agent-frameworks-tests.ipynb) | [Validation guide](../validation/README.md#4-citadel-agent-frameworks-tests) | +| 5 | Citadel PII Processing Tests | [`5. citadel-pii-processing-tests.ipynb`](5.%20citadel-pii-processing-tests.ipynb) | [Validation guide](../validation/README.md#5-citadel-pii-processing-tests) | +| 6 | Citadel Unified AI API Tests | [`6. citadel-unified-ai-api-tests.ipynb`](6.%20citadel-unified-ai-api-tests.ipynb) | [Validation guide](../validation/README.md#6-unified-ai-api) | + +> **Lab context:** For the purposes of this lab, the same backends that were already provisioned by `azd up` will be re-provisioned through the notebook. In a real-world scenario, you would first provision new backends (e.g. a new AI Foundry account) and then use this notebook to register them with APIM. Note that all backends must be included in a single provisioning run — if only one new backend is added via the notebook, previously registered backends will be removed. For example, if `backend1` and `backend2` are already registered and you want to add `backend3`, all three must be specified in the notebook. + +#### Optional Notebooks (7–9) + +The following three notebooks are **optional** and can be run after notebooks 1–6. They are net-new labs and do not have equivalent notebooks in the `validation/` folder. Unless noted, they share the same pre-requisites as notebooks 1–6 (deployed hub + spoke, Azure CLI login, workshop Python environment). + +
+7. Citadel Hosted Agent with AGT — build, deploy, and govern a Foundry hosted agent + +| | | +|---|---| +| **Notebook** | [`7. citadel-hosted-agent-with-agt.ipynb`](7.%20citadel-hosted-agent-with-agt.ipynb) | +| **Extra pre-reqs** | None beyond the standard notebooks 1–6 pre-requisites | + +Builds a **Foundry Hosted Agent** for a Contoso HR scenario, governed end-to-end with the **Microsoft Agent Governance Toolkit (AGT)**. The notebook generates the agent source, builds and pushes the container image with `az acr build` (no local Docker), deploys it as a hosted agent version to the spoke Foundry project, assigns the required RBAC, and tests it end-to-end — including governance enforcement (PII/SSN blocking, third-party salary lookups, destructive-tool denial), multi-turn conversations, and streaming responses. AGT policy and audit decisions are exported to Application Insights. + +
+ +
+8. Publish and Use an A2A Endpoint — expose a Foundry agent as a governed Agent-to-Agent endpoint + +| | | +|---|---| +| **Notebook** | [`8. publish-and-use-a2a-endpoint.ipynb`](8.%20publish-and-use-a2a-endpoint.ipynb) | +| **Extra pre-reqs** | None beyond the standard notebooks 1–6 pre-requisites | + +Walks through the full lifecycle of running an HR **prompt agent** on Microsoft Foundry, enabling its incoming **Agent-to-Agent (A2A)** endpoint, verifying the agent card, and publishing it through the Citadel Governance Hub (APIM) as a governed **access contract** (product + subscription). It then calls the agent through APIM with only a subscription key — proving APIM reaches the agent over its private endpoint even with public network access disabled. Everything is resolved from your current `azd` environment, so no resource names are hardcoded. + +
+ +
+9. Publish and Use the HR MCP via APIM — publish and consume an HR MCP server through APIM (requires setup scripts) + +| | | +|---|---| +| **Notebook** | [`9. publish-and-use-hr-mcp-via-apim.ipynb`](9.%20publish-and-use-hr-mcp-via-apim.ipynb) | +| **Extra pre-reqs** | The HR MCP server must be deployed and published first (see steps below) | + +Demonstrates the HR **Model Context Protocol (MCP)** server through **Azure API Management only**. It validates the Citadel-style APIM access contract, exercises MCP protocol/tool calls, demonstrates the APIM `5 tools/call per 30 seconds` rate-limit policy, inspects APIM logs, and deploys a Foundry hosted agent whose tools come from the APIM-published MCP endpoint. Everything required to run this notebook lives in the `mcp-hr/` folder. + +**Pre-requisite setup** — before running the notebook, run the scripts in [`mcp-hr/scripts/`](mcp-hr/scripts/) in this order (use the `.sh` scripts on macOS/Linux, `.ps1` on Windows). Run them from the `workshop/` folder with Azure CLI logged in: + +1. **Deploy the MCP server** — provisions the Container Apps host, ACR, telemetry, and Entra auth settings: + ```bash + # macOS/Linux + bash ./mcp-hr/scripts/deploy-hr-mcp.sh + # Windows + pwsh -File ./mcp-hr/scripts/deploy-hr-mcp.ps1 + ``` +2. **Publish to APIM** — creates the APIM MCP API, backend, Citadel product, subscription, and policies: + ```bash + # macOS/Linux + bash ./mcp-hr/scripts/publish-hr-mcp-apim.sh + # Windows + pwsh -File ./mcp-hr/scripts/publish-hr-mcp-apim.ps1 + ``` +3. **Validate** — run the two test scripts to confirm direct and APIM-mediated access work: + ```bash + uv run python ./mcp-hr/scripts/test-hr-mcp-direct.py + uv run python ./mcp-hr/scripts/test-hr-mcp-apim.py + ``` + +Once these scripts complete successfully, run the notebook. + +
+ +### 5.5 Running Notebooks — Tips + +- ✅ **Run All is fine** — you can use **Run All** to execute the entire notebook, but review the output of each cell afterwards to understand what happened +- 🔐 **Azure CLI auth** — ensure you are logged in with `az login` before running notebooks; this is required for notebooks to discover resources and authenticate +- 🧹 **Cleanup** — do not clean up resources created by notebooks individually; at the end of the workshop, run `azd down --purge --force` +- 🛠️ **Errors?** — Check the [Troubleshooting](#8-troubleshooting) section + +--- + + + +## 6. Lab 4 — Observability: Metrics, Logs, and Telemetry + +**Goal:** After running notebooks, explore the telemetry and usage data generated by your API calls. +**Estimated time:** ~30 minutes + +### 6.1 API Management — Built-in Analytics + +1. Navigate to your **API Management** resource + +2. **Analytics** (under Monitoring): + - Explore the **Timeline** tab to see request volume over time + - Switch to the **Subscriptions** tab to view usage broken down by APIM subscription + - Open the **Language Models** tab to observe per-model metrics + +3. **Metrics**: + - Go to **Metrics** + - Add metric: **Requests** → Split by **ApiId** — observe how traffic distributed across your notebook runs. Use Bar Chart for visualization. + - Create new chart, then add a new metric: **Backend Duration** → Split by **ApiId**, to see backend response times + +4. **Logs**: + - Go to **Logs** (under Monitoring) + - Run sample queries to explore APIM gateway activity: + + **Request summary by response code:** + ```kusto + ApiManagementGatewayLogs + | where TimeGenerated > ago(6h) + | summarize count() by ResponseCode, ApiId + | order by count_ desc + ``` + + **Request volume by API:** + ```kusto + ApiManagementGatewayLogs + | where TimeGenerated > ago(6h) + | summarize RequestCount = count() by ApiId, bin(TimeGenerated, 5m) + | render columnchart + ``` + + **Latency percentiles:** + ```kusto + ApiManagementGatewayLogs + | where TimeGenerated > ago(2h) + | summarize + P50 = percentile(TotalTime, 50), + P95 = percentile(TotalTime, 95), + P99 = percentile(TotalTime, 99) + by ApiId + ``` + + **Throttled requests (429s):** + ```kusto + ApiManagementGatewayLogs + | where TimeGenerated > ago(6h) + | where ResponseCode == 429 + | summarize ThrottledCount = count() by ProductId, bin(TimeGenerated, 5m) + | render columnchart + ``` + +### 6.2 Application Insights — APIM Telemetry + +From your APIM resource, click the **Application Insights** option in the sidebar to navigate directly to the associated Application Insights resource. + +1. **Application Map** — Visualize how components are connected: + - Go to **Application Map** + - See APIM, AI Foundry backends, and dependency calls + +2. **Transaction Search** — Drill into individual requests: + - Go to **Transaction search** + - Filter by time range covering your notebook runs + - Click on a request to see full end-to-end trace (request → APIM policy execution → backend call → response) + +### 6.3 Cosmos DB — Usage Records + +**Populate data first:** Before querying Cosmos DB, you need to run the Logic App workflow that ingests usage data from Application Insights into Cosmos DB: + +1. In the Azure Portal, open the **Logic Apps** service (the resource name starts with `logic-`) +2. Navigate to **Workflows** → **Workflows** +3. Select the **`llm-usage-ingestion`** workflow +4. Click **Run** to trigger the workflow manually +5. Check **Run History** — wait until you see a successful run +6. Explore the run results to observe how data flows from App Insights to Cosmos DB + +Once you have a successful run, proceed with importing model pricing data. + +**Import model pricing data:** The `model-pricing` container stores per-model cost information used for usage analytics. This data is imported from a local JSON file (`model-pricing.json`) included in the workshop/scripts folder. The import script authenticates via Entra ID and requires the **Cosmos DB Built-in Data Contributor** role on your Cosmos DB account. + +1. First, retrieve your Cosmos DB account name and assign the required role to your current user: + +
+ 💻 Bash + + ```bash + # Get the Cosmos DB account name from your deployment + COSMOS_ACCOUNT=$(az cosmosdb list --resource-group $(azd env get-value AZURE_RESOURCE_GROUP) --query "[0].name" -o tsv) + COSMOS_RESOURCE_GROUP=$(azd env get-value AZURE_RESOURCE_GROUP) + CURRENT_USER_ID=$(az ad signed-in-user show --query id -o tsv) + + # Assign "Cosmos DB Built-in Data Contributor" role (role definition id: 00000000-0000-0000-0000-000000000002) + az cosmosdb sql role assignment create \ + --account-name $COSMOS_ACCOUNT \ + --resource-group $COSMOS_RESOURCE_GROUP \ + --role-definition-id 00000000-0000-0000-0000-000000000002 \ + --principal-id $CURRENT_USER_ID \ + --scope "/" + ``` + +
+ +
+ 🪟 PowerShell + + ```powershell + $COSMOS_ACCOUNT = az cosmosdb list --resource-group $(azd env get-value AZURE_RESOURCE_GROUP) --query "[0].name" -o tsv + $COSMOS_RESOURCE_GROUP = azd env get-value AZURE_RESOURCE_GROUP + $CURRENT_USER_ID = az ad signed-in-user show --query id -o tsv + + az cosmosdb sql role assignment create ` + --account-name $COSMOS_ACCOUNT ` + --resource-group $COSMOS_RESOURCE_GROUP ` + --role-definition-id 00000000-0000-0000-0000-000000000002 ` + --principal-id $CURRENT_USER_ID ` + --scope "/" + ``` + +
+ +2. Run the import script to load model pricing data into Cosmos DB: + +
+ 💻 Bash + + ```bash + # Get the Cosmos DB endpoint + COSMOS_ENDPOINT=$(az cosmosdb show --name $COSMOS_ACCOUNT --resource-group $COSMOS_RESOURCE_GROUP --query "documentEndpoint" -o tsv) + + # Import model pricing data + uv run python scripts/import-model-pricing.py --endpoint $COSMOS_ENDPOINT + ``` + +
+ +
+ 🪟 PowerShell + + ```powershell + $COSMOS_ENDPOINT = az cosmosdb show --name $COSMOS_ACCOUNT --resource-group $COSMOS_RESOURCE_GROUP --query "documentEndpoint" -o tsv + uv run python scripts\import-model-pricing.py --endpoint $COSMOS_ENDPOINT + ``` + +
+ + You should see output confirming each model pricing record was upserted. + +**Data Explorer:** Now navigate to Cosmos DB to explore both usage records and model pricing: + +1. Navigate to your **Cosmos DB** resource +2. Open **Data Explorer** +3. Look for the `ai-usage-db` database +4. Review the two important containers: + - `llm-usage-container` — stores usage records generated by notebook calls + - `model-pricing` — stores model pricing data imported by the script +5. Select `ai-usage-db` database → `llm-usage-container` +6. Run a query to see usage records generated by your notebook calls: + + ```sql + SELECT * FROM c + WHERE c._ts > (GetCurrentTimestamp()/1000 - 7200) + ORDER BY c._ts DESC + ``` + +7. Examine a usage record — note fields like: + - Model name and deployment + - Token counts (prompt, completion, total) + - Product that made the call + - Timestamp + + > 💡 **Tip:** These usage records are generated by the `set-llm-usage` APIM policy fragment you reviewed in Lab 2 (Section 4.3). That policy fragment captures token counts and model metadata from each LLM call and emits them to Event Hub, which the Logic App workflow then ingests into Cosmos DB. + +8. Also explore the `model-pricing` container to see how model pricing data is stored + +> ℹ️ **Note:** If you get a warning that the request is blocked by your Cosmos DB account firewall settings, go to your Cosmos DB resource → **Settings** → **Networking**, choose **Selected networks**, and add your current IP address. + +
+Optional — visualize usage data in Power BI + +You can now visualize the data in Cosmos DB using the provided Power BI template. See the [Power BI Dashboard guide](../guides/power-bi-dashboard.md) for instructions. + +
+ +--- + +## 7. Clean Up + +After the workshop, remove all deployed resources to avoid ongoing costs. Perform these steps **in order** — tear down the optional MCP lab resources first, then the Citadel hub, then any spokes. + +### 7.1 Tear Down MCP Lab Resources (only if you ran optional notebook 9) + +If you ran optional notebook 9 (HR MCP via APIM), tear down the HR MCP resources **before** running `azd down` for the Citadel hub. The MCP deployment creates resources inside the shared hub VNet (a dedicated ACA subnet, private DNS zone link, etc.), so removing them first avoids leaving orphaned dependencies that can block hub deletion. Run the teardown script from the `workshop/` folder: + +
+💻 Bash + +```bash +bash ./mcp-hr/scripts/teardown-hr-mcp.sh +``` + +
+ +
+🪟 PowerShell + +```powershell +pwsh -File ./mcp-hr/scripts/teardown-hr-mcp.ps1 +``` + +
+ +The teardown is best-effort and idempotent — resources that are already gone are skipped. + +> ⏭️ **Skip this step** if you did not run notebook 9. + +### 7.2 Tear Down the Citadel Hub (`azd down`) + +Remove the hub resources that were deployed via `azd up`: + +```bash +# This will delete ALL resources in the resource group and purge soft-deleted resources +azd down --purge --force +``` + +> 🛠️ **If `azd down` fails:** Go to the Azure Portal, navigate to your hub resource group, and delete the deployment manually. + +> ⚠️ **Important:** The `--purge` flag ensures soft-deleted resources (Key Vault, Cognitive Services) are permanently removed. The `--force` flag skips confirmation prompts. + +### 7.3 Tear Down the Spoke(s) (via Azure Portal) + +`azd down` only removes the hub resources that were deployed via `azd up`. You must also manually delete any **spoke resource groups** in the Azure Portal because they were deployed separately by script. + +1. Go to [portal.azure.com](https://portal.azure.com) +2. Locate each spoke resource group (e.g. `rg-citadel-demo-1-spoke-1`) +3. Delete the resource group + +### 7.4 Verify Cleanup + +Verify in the Azure Portal that the hub resource group and any spoke resource groups have been deleted. + +--- + +## 8. Troubleshooting + +### Deployment Issues + +
+azd up fails with provider not registered + +```bash +# Register the missing provider +az provider register --namespace +# Wait a minute, then retry +azd up +``` + +
+ +
+Quota errors for AI model deployments + +- Check your subscription's Azure OpenAI quota in the target region +- Try a different region: `azd env set AZURE_LOCATION eastus2` +- Reduce model capacity in the parameter file + +
+ +
+APIM deployment takes very long + +- StandardV2 APIM typically deploys in 15-25 minutes +- Classic tier (Developer/Premium) can take 30-45 minutes +- This is normal — wait for completion + +
+ +### Notebook Issues + +
+az CLI not authenticated in notebook + +```bash +# Run in your terminal +az login +# Restart the Jupyter kernel in VS Code +``` + +
+ +
+Module not found errors + +```bash +# Ensure you activated the correct virtual environment +# For uv: +uv sync +# For pip: +pip install -r requirements.txt +``` + +
+ +
+APIM returns 401/403 errors + +- Verify your subscription key is correct (re-run the "Retrieve API key" cell) +- Check that the APIM product is published and the subscription is active +- For JWT tests: ensure Entra ID setup has been completed + +
+ +
+Notebook cells fail with timeout + +- Some operations (Bicep deployments) can take 2-5 minutes +- Re-run the cell if it timed out +- Check Azure Portal → Resource Group → Deployments for deployment status + +
+ +### Observability Issues + +
+No data in Application Insights + +- There is typically a 2-5 minute ingestion delay +- Ensure you are looking at the correct time range +- Verify the Application Insights resource is connected to your APIM instance + +
+ +
+No usage records in Cosmos DB + +- Usage records are processed by the Logic App workflow +- Check Logic App → Run history for any failed runs +- Allow a few minutes for events to flow through Event Hub → Logic App → Cosmos DB + +
+ +--- + +## Appendix + +### Key Azure Resources Deployed + +| Service | SKU (Default) | Purpose | +|---------|--------------|---------| +| API Management | StandardV2 | Unified AI gateway | +| AI Foundry (x2) | Standard | AI model hosting (2 regions) | +| Cosmos DB | 400 RU/s | Usage analytics storage | +| Event Hub | 1 capacity unit | Usage event streaming | +| Key Vault | Standard | Secrets management | +| Language Service | S | PII detection | +| Content Safety | S0 | Content safety scanning | +| Log Analytics | Per-GB | Centralized logging | +| Application Insights | — | APIM monitoring | +| Logic App (Standard) | WS1 | Usage ingestion workflows | +| Virtual Network | — | Network isolation | +| Storage Account | Standard LRS | Logic App backing storage | + +### Useful `azd` Commands + +```bash +azd env get-values # View all deployment outputs +azd env list # List all environments +azd monitor --overview # Open Application Insights in browser +azd monitor --live # Open Live Metrics in browser +azd monitor --logs # Open Log Analytics in browser +azd down --purge --force # Delete all resources +``` + +### Useful Links + +- [Citadel Governance Hub Repository](https://github.com/Azure-Samples/ai-hub-gateway-solution-accelerator) +- [Quick Deployment Guide](../guides/quick-deployment-guide.md) +- [Full Deployment Guide](../guides/full-deployment-guide.md) +- [Parameters Usage Guide](../guides/parameters-usage-guide.md) +- [Azure API Management Documentation](https://learn.microsoft.com/azure/api-management/) +- [Azure Developer CLI Documentation](https://learn.microsoft.com/azure/developer/azure-developer-cli/) diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/requirements.txt b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/requirements.txt new file mode 100644 index 000000000..db61aabf3 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/requirements.txt @@ -0,0 +1,331 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile pyproject.toml -o requirements.txt +agent-framework-core==1.2.0 + # via + # workshop (pyproject.toml) + # agent-framework-openai +agent-framework-openai==1.2.0 + # via workshop (pyproject.toml) +annotated-types==0.7.0 + # via pydantic +anyio==4.13.0 + # via + # httpx + # openai +appnope==0.1.4 + # via ipykernel +asttokens==3.0.1 + # via stack-data +azure-ai-projects==2.1.0 + # via workshop (pyproject.toml) +azure-common==1.1.28 + # via azure-mgmt-apimanagement +azure-core==1.39.0 + # via + # azure-ai-projects + # azure-cosmos + # azure-identity + # azure-keyvault-certificates + # azure-keyvault-keys + # azure-keyvault-secrets + # azure-mgmt-core + # azure-storage-blob +azure-cosmos==4.15.0 + # via workshop (pyproject.toml) +azure-identity==1.25.3 + # via + # workshop (pyproject.toml) + # azure-ai-projects +azure-keyvault==4.2.0 + # via workshop (pyproject.toml) +azure-keyvault-certificates==4.11.0 + # via azure-keyvault +azure-keyvault-keys==4.11.0 + # via azure-keyvault +azure-keyvault-secrets==4.11.0 + # via azure-keyvault +azure-mgmt-apimanagement==5.0.0 + # via workshop (pyproject.toml) +azure-mgmt-core==1.6.0 + # via azure-mgmt-apimanagement +azure-storage-blob==12.28.0 + # via azure-ai-projects +certifi==2026.4.22 + # via + # httpcore + # httpx + # requests +cffi==2.0.0 + # via cryptography +charset-normalizer==3.4.7 + # via requests +comm==0.2.3 + # via ipykernel +contourpy==1.3.3 + # via matplotlib +cryptography==47.0.0 + # via + # azure-identity + # azure-keyvault-keys + # azure-storage-blob + # msal + # pyjwt +cycler==0.12.1 + # via matplotlib +debugpy==1.8.20 + # via ipykernel +decorator==5.2.1 + # via ipython +distro==1.9.0 + # via openai +executing==2.2.1 + # via stack-data +fonttools==4.62.1 + # via matplotlib +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via + # langgraph-sdk + # langsmith + # openai +idna==3.13 + # via + # anyio + # httpx + # requests +importlib-metadata==8.7.1 + # via opentelemetry-api +ipykernel==7.2.0 + # via workshop (pyproject.toml) +ipython==9.13.0 + # via ipykernel +ipython-pygments-lexers==1.1.1 + # via ipython +isodate==0.7.2 + # via + # azure-ai-projects + # azure-keyvault-certificates + # azure-keyvault-keys + # azure-keyvault-secrets + # azure-mgmt-apimanagement + # azure-storage-blob +jedi==0.19.2 + # via ipython +jiter==0.14.0 + # via openai +jsonpatch==1.33 + # via langchain-core +jsonpointer==3.1.1 + # via jsonpatch +jupyter-client==8.8.0 + # via ipykernel +jupyter-core==5.9.1 + # via + # ipykernel + # jupyter-client +kiwisolver==1.5.0 + # via matplotlib +langchain==1.2.15 + # via workshop (pyproject.toml) +langchain-core==1.3.2 + # via + # langchain + # langchain-openai + # langgraph + # langgraph-checkpoint + # langgraph-prebuilt +langchain-openai==1.2.1 + # via workshop (pyproject.toml) +langchain-protocol==0.0.12 + # via langchain-core +langgraph==1.1.9 + # via langchain +langgraph-checkpoint==4.0.2 + # via + # langgraph + # langgraph-prebuilt +langgraph-prebuilt==1.0.11 + # via langgraph +langgraph-sdk==0.3.13 + # via langgraph +langsmith==0.7.37 + # via langchain-core +matplotlib==3.10.9 + # via workshop (pyproject.toml) +matplotlib-inline==0.2.1 + # via + # ipykernel + # ipython +msal==1.36.0 + # via + # azure-identity + # msal-extensions +msal-extensions==1.3.1 + # via azure-identity +nest-asyncio==1.6.0 + # via ipykernel +numpy==2.4.4 + # via + # contourpy + # matplotlib + # pandas +openai==2.32.0 + # via + # workshop (pyproject.toml) + # agent-framework-openai + # azure-ai-projects + # langchain-openai +opentelemetry-api==1.41.1 + # via + # agent-framework-core + # opentelemetry-sdk + # opentelemetry-semantic-conventions +opentelemetry-sdk==1.41.1 + # via opentelemetry-semantic-conventions-ai +opentelemetry-semantic-conventions==0.62b1 + # via + # opentelemetry-sdk + # opentelemetry-semantic-conventions-ai +opentelemetry-semantic-conventions-ai==0.5.1 + # via workshop (pyproject.toml) +orjson==3.11.8 + # via + # langgraph-sdk + # langsmith +ormsgpack==1.12.2 + # via langgraph-checkpoint +packaging==26.2 + # via + # ipykernel + # langchain-core + # langsmith + # matplotlib +pandas==3.0.2 + # via workshop (pyproject.toml) +parso==0.8.6 + # via jedi +pexpect==4.9.0 + # via ipython +pillow==12.2.0 + # via matplotlib +platformdirs==4.9.6 + # via jupyter-core +prompt-toolkit==3.0.52 + # via ipython +psutil==7.2.2 + # via + # ipykernel + # ipython +ptyprocess==0.7.0 + # via pexpect +pure-eval==0.2.3 + # via stack-data +pycparser==3.0 + # via cffi +pydantic==2.13.3 + # via + # agent-framework-core + # langchain + # langchain-core + # langgraph + # langsmith + # openai +pydantic-core==2.46.3 + # via pydantic +pygments==2.20.0 + # via + # ipython + # ipython-pygments-lexers +pyjwt==2.12.1 + # via msal +pyparsing==3.3.2 + # via matplotlib +python-dateutil==2.9.0.post0 + # via + # jupyter-client + # matplotlib + # pandas +python-dotenv==1.2.2 + # via agent-framework-core +pyyaml==6.0.3 + # via langchain-core +pyzmq==27.1.0 + # via + # ipykernel + # jupyter-client +regex==2026.4.4 + # via tiktoken +requests==2.33.1 + # via + # azure-core + # langsmith + # msal + # requests-toolbelt + # tiktoken +requests-toolbelt==1.0.0 + # via langsmith +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via openai +stack-data==0.6.3 + # via ipython +tenacity==9.1.4 + # via langchain-core +tiktoken==0.12.0 + # via langchain-openai +tornado==6.5.5 + # via + # ipykernel + # jupyter-client +tqdm==4.67.3 + # via openai +traitlets==5.14.3 + # via + # ipykernel + # ipython + # jupyter-client + # jupyter-core + # matplotlib-inline +typing-extensions==4.15.0 + # via + # agent-framework-core + # azure-ai-projects + # azure-core + # azure-cosmos + # azure-identity + # azure-keyvault-certificates + # azure-keyvault-keys + # azure-keyvault-secrets + # azure-mgmt-apimanagement + # azure-storage-blob + # langchain-core + # langchain-protocol + # openai + # opentelemetry-api + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.2 + # via pydantic +urllib3==2.6.3 + # via requests +uuid-utils==0.14.1 + # via + # langchain-core + # langsmith +wcwidth==0.6.0 + # via prompt-toolkit +xxhash==3.7.0 + # via + # langgraph + # langsmith +zipp==3.23.1 + # via importlib-metadata +zstandard==0.25.0 + # via langsmith diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.ps1 new file mode 100644 index 000000000..67ac5ba2f --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.ps1 @@ -0,0 +1,165 @@ +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +if ($PSVersionTable.PSVersion.Major -ge 7) { + $PSNativeCommandUseErrorActionPreference = $false +} + +$apimApiVersion = '2024-05-01' + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" +} + +function Assert-Command { + param([string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command not found: $Name" + } +} + +function Get-AzdValue { + param( + [string]$Name, + [switch]$Required + ) + + $value = (& azd env get-value $Name 2>$null) + if ($LASTEXITCODE -ne 0) { + $value = $null + } + + $value = ConvertTo-TrimmedCliOutput $value + if ($Required -and [string]::IsNullOrWhiteSpace($value)) { + throw "Required azd environment value is missing: $Name. Run azd up first, then rerun this script." + } + + return $value +} + +function ConvertTo-TrimmedCliOutput { + param([AllowNull()]$Value) + + if ($null -eq $Value) { + return $null + } + + $text = [string]($Value | Select-Object -First 1) + if ([string]::IsNullOrWhiteSpace($text)) { + return $null + } + + return $text.Trim() +} + +function Remove-DefaultProduct { + param([string]$ProductId) + + & az apim product show ` + --resource-group $script:ResourceGroup ` + --service-name $script:ApimName ` + --product-id $ProductId ` + --only-show-errors ` + -o none *> $null + + if ($LASTEXITCODE -eq 0) { + Write-Step "Deleting APIM default product: $ProductId" + & az apim product delete ` + --resource-group $script:ResourceGroup ` + --service-name $script:ApimName ` + --product-id $ProductId ` + --delete-subscriptions true ` + --yes ` + --only-show-errors ` + -o none + } + else { + Write-Host "Default product already absent: $ProductId" + } +} + +function Remove-DefaultSubscription { + param([string]$SubscriptionName) + + $subscriptionUri = "/subscriptions/$script:SubscriptionId/resourceGroups/$script:ResourceGroup/providers/Microsoft.ApiManagement/service/$script:ApimName/subscriptions/${SubscriptionName}?api-version=$apimApiVersion" + + & az rest ` + --method delete ` + --uri $subscriptionUri ` + --headers 'If-Match=*' ` + --only-show-errors ` + -o none *> $null + + if ($LASTEXITCODE -eq 0) { + Write-Host "Deleted default subscription: $SubscriptionName" + } + else { + Write-Host "Default subscription already absent or could not be deleted: $SubscriptionName" + } +} + +Assert-Command az +Assert-Command azd + +& az account show *> $null +if ($LASTEXITCODE -ne 0) { + throw 'Azure CLI is not logged in. Run az login, then rerun this script.' +} + +$script:ResourceGroup = Get-AzdValue -Name 'AZURE_RESOURCE_GROUP' -Required +$script:SubscriptionId = Get-AzdValue -Name 'AZURE_SUBSCRIPTION_ID' +$script:ApimName = Get-AzdValue -Name 'APIM_NAME' + +if (-not [string]::IsNullOrWhiteSpace($script:SubscriptionId)) { + & az account set --subscription $script:SubscriptionId +} +else { + $script:SubscriptionId = ConvertTo-TrimmedCliOutput (& az account show --query id -o tsv) +} + +if ([string]::IsNullOrWhiteSpace($script:ApimName)) { + $script:ApimName = ConvertTo-TrimmedCliOutput (& az apim list --resource-group $script:ResourceGroup --query '[0].name' -o tsv 2>$null) +} + +if ([string]::IsNullOrWhiteSpace($script:ApimName)) { + throw 'Could not resolve the APIM service name from azd or the resource group.' +} + +$skuName = ConvertTo-TrimmedCliOutput (& az apim show --resource-group $script:ResourceGroup --name $script:ApimName --query 'sku.name' -o tsv) + +if ($skuName -ne 'Developer') { + Write-Host "APIM SKU is $skuName. Cleanup only runs for Developer SKU, so no action was taken." + exit 0 +} + +Write-Step "Developer SKU detected for APIM service $script:ApimName" + +Remove-DefaultProduct -ProductId 'starter' +Remove-DefaultProduct -ProductId 'unlimited' + +Write-Step 'Checking for remaining default APIM subscriptions' +$subscriptionListUri = "/subscriptions/$script:SubscriptionId/resourceGroups/$script:ResourceGroup/providers/Microsoft.ApiManagement/service/$script:ApimName/subscriptions?api-version=$apimApiVersion" +$remainingSubscriptions = (& az rest ` + --method get ` + --uri $subscriptionListUri ` + --query "value[?name != 'master'].name" ` + -o tsv 2>$null) + +if ($LASTEXITCODE -ne 0) { + $remainingSubscriptions = $null +} + +$remainingSubscriptionNames = @($remainingSubscriptions | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object { $_.ToString().Trim() }) +if ($remainingSubscriptionNames.Count -eq 0) { + Write-Host 'No remaining subscriptions were found after preserving master.' + exit 0 +} + +foreach ($subscriptionName in $remainingSubscriptionNames) { + Remove-DefaultSubscription -SubscriptionName $subscriptionName +} + +Write-Host "`nAPIM default cleanup completed." \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.sh new file mode 100755 index 000000000..6669b1f94 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/cleanup-apim-defaults.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +set -euo pipefail + +APIM_API_VERSION="2024-05-01" + +log() { + printf '\n==> %s\n' "$1" +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + printf 'Required command not found: %s\n' "$1" >&2 + exit 1 + fi +} + +azd_get_required() { + local key="$1" + local value + value="$(azd env get-value "$key" 2>/dev/null || true)" + if [[ -z "$value" ]]; then + printf 'Required azd environment value is missing: %s\nRun azd up first, then rerun this script.\n' "$key" >&2 + exit 1 + fi + printf '%s' "$value" +} + +azd_get_optional() { + azd env get-value "$1" 2>/dev/null || true +} + +delete_default_product() { + local product_id="$1" + + if az apim product show \ + --resource-group "$resource_group" \ + --service-name "$apim_name" \ + --product-id "$product_id" \ + --only-show-errors \ + -o none >/dev/null 2>&1; then + log "Deleting APIM default product: $product_id" + az apim product delete \ + --resource-group "$resource_group" \ + --service-name "$apim_name" \ + --product-id "$product_id" \ + --delete-subscriptions true \ + --yes \ + --only-show-errors \ + -o none + else + printf 'Default product already absent: %s\n' "$product_id" + fi +} + +delete_default_subscription() { + local subscription_name="$1" + local subscription_uri="/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.ApiManagement/service/${apim_name}/subscriptions/${subscription_name}?api-version=${APIM_API_VERSION}" + + if az rest \ + --method delete \ + --uri "$subscription_uri" \ + --headers 'If-Match=*' \ + --only-show-errors \ + -o none >/dev/null 2>&1; then + printf 'Deleted default subscription: %s\n' "$subscription_name" + else + printf 'Default subscription already absent or could not be deleted: %s\n' "$subscription_name" + fi +} + +require_command az +require_command azd + +if ! az account show >/dev/null 2>&1; then + printf 'Azure CLI is not logged in. Run az login, then rerun this script.\n' >&2 + exit 1 +fi + +resource_group="$(azd_get_required AZURE_RESOURCE_GROUP)" +subscription_id="$(azd_get_optional AZURE_SUBSCRIPTION_ID)" +apim_name="$(azd_get_optional APIM_NAME)" + +if [[ -n "$subscription_id" ]]; then + az account set --subscription "$subscription_id" +else + subscription_id="$(az account show --query id -o tsv)" +fi + +if [[ -z "$apim_name" ]]; then + apim_name="$(az apim list --resource-group "$resource_group" --query '[0].name' -o tsv 2>/dev/null || true)" +fi + +if [[ -z "$apim_name" ]]; then + printf 'Could not resolve the APIM service name from azd or the resource group.\n' >&2 + exit 1 +fi + +sku_name="$(az apim show --resource-group "$resource_group" --name "$apim_name" --query 'sku.name' -o tsv)" + +if [[ "$sku_name" != "Developer" ]]; then + printf 'APIM SKU is %s. Cleanup only runs for Developer SKU, so no action was taken.\n' "$sku_name" + exit 0 +fi + +log "Developer SKU detected for APIM service $apim_name" + +delete_default_product starter +delete_default_product unlimited + +log "Checking for remaining default APIM subscriptions" +remaining_subscriptions="$( + az rest \ + --method get \ + --uri "/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.ApiManagement/service/${apim_name}/subscriptions?api-version=${APIM_API_VERSION}" \ + --query "value[?name != 'master'].name" \ + -o tsv + 2>/dev/null || true +)" + +if [[ -z "$remaining_subscriptions" ]]; then + printf 'No remaining subscriptions were found after preserving master.\n' + exit 0 +fi + +while IFS= read -r subscription_name; do + [[ -z "$subscription_name" ]] && continue + delete_default_subscription "$subscription_name" +done <<< "$remaining_subscriptions" + +printf '\nAPIM default cleanup completed.\n' \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.ps1 new file mode 100644 index 000000000..eb82a1e46 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.ps1 @@ -0,0 +1,494 @@ +[CmdletBinding()] +param( + [string]$SpokeResourceGroupName = $env:SPOKE_RESOURCE_GROUP_NAME, + [string]$SpokeSuffix = $env:SPOKE_SUFFIX, + [string]$FoundryAccountName = $env:FOUNDRY_ACCOUNT_NAME, + [string]$FoundryProjectName = $env:FOUNDRY_PROJECT_NAME, + [string]$KeyVaultName = $env:KEY_VAULT_NAME, + [string]$KeyVaultEnablePurgeProtection = $(if ($env:KEY_VAULT_ENABLE_PURGE_PROTECTION) { $env:KEY_VAULT_ENABLE_PURGE_PROTECTION } else { 'false' }) +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +if ($PSVersionTable.PSVersion.Major -ge 7) { + $PSNativeCommandUseErrorActionPreference = $false +} + +$aiProjectManagerRoleId = 'eadc314b-1a2d-4efa-be10-5d325db5065e' +$keyVaultSecretsUserRoleId = '4633458b-17de-408a-b874-0445c86b69e6' +$acrPullRoleId = '7f951dda-4ed3-4680-a7ca-43fe172d538d' +$foundryAppInsightsConnectionApiVersion = '2025-06-01' +$securityControlTag = 'SecurityControl=Ignore' + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" +} + +function Assert-Command { + param([string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command not found: $Name" + } +} + +function Get-AzdValue { + param( + [string]$Name, + [switch]$Required + ) + + $value = (& azd env get-value $Name 2>$null) + if ($LASTEXITCODE -ne 0) { + $value = $null + } + + if ($Required -and [string]::IsNullOrWhiteSpace($value)) { + throw "Required azd environment value is missing: $Name. Run azd up/provision first, then rerun this script." + } + + return ($value | Select-Object -First 1) +} + +function ConvertTo-SafeNamePart { + param([string]$Value) + $name = $Value.ToLowerInvariant() -replace '[^a-z0-9-]', '-' -replace '-+', '-' + $name = $name.Trim('-') + if ([string]::IsNullOrWhiteSpace($name)) { return 'citadel' } + return $name +} + +function ConvertTo-CompactNamePart { + param([string]$Value) + $name = $Value.ToLowerInvariant() -replace '[^a-z0-9]', '' + if ([string]::IsNullOrWhiteSpace($name)) { return 'citadel' } + return $name +} + +function ConvertTo-TrimmedCliOutput { + param([AllowNull()]$Value) + + if ($null -eq $Value) { + return $null + } + + $text = [string]($Value | Select-Object -First 1) + if ([string]::IsNullOrWhiteSpace($text)) { + return $null + } + + return $text.Trim() +} + +function Invoke-NativeCommandQuietly { + param([scriptblock]$Command) + + $previousErrorActionPreference = $ErrorActionPreference + try { + $ErrorActionPreference = 'Continue' + & $Command *> $null + return $LASTEXITCODE + } + finally { + $ErrorActionPreference = $previousErrorActionPreference + } +} + +function Add-RoleAssignmentIfMissing { + param( + [string]$PrincipalId, + [string]$PrincipalType, + [string]$RoleId, + [string]$RoleName, + [string]$Scope + ) + + $assignmentId = $null + try { + $assignmentId = (& az role assignment list ` + --assignee-object-id $PrincipalId ` + --role $RoleId ` + --scope $Scope ` + --all ` + --fill-principal-name false ` + --fill-role-definition-name false ` + --query '[0].id' ` + -o tsv 2>$null) + } catch { $assignmentId = $null } + + if (-not [string]::IsNullOrWhiteSpace($assignmentId)) { + Write-Host "Role assignment already exists: $RoleName" + return + } + + & az role assignment create ` + --assignee-object-id $PrincipalId ` + --assignee-principal-type $PrincipalType ` + --role $RoleId ` + --scope $Scope ` + --only-show-errors ` + -o none + Write-Host "Assigned role: $RoleName" +} + +function Set-SecurityControlTag { + param([string]$ResourceId) + + if ([string]::IsNullOrWhiteSpace($ResourceId)) { + return + } + + & az tag update ` + --resource-id $ResourceId ` + --operation Merge ` + --tags $securityControlTag ` + --only-show-errors ` + -o none +} + +Assert-Command az +Assert-Command azd + +if ((Invoke-NativeCommandQuietly { & az account show }) -ne 0) { + throw 'Azure CLI is not logged in. Run az login, then rerun this script.' +} + +if ((Invoke-NativeCommandQuietly { & az cognitiveservices account project -h }) -ne 0) { + throw 'This Azure CLI does not include az cognitiveservices account project. Update Azure CLI and rerun this script.' +} + +$governanceResourceGroup = Get-AzdValue -Name 'AZURE_RESOURCE_GROUP' -Required +$location = Get-AzdValue -Name 'AZURE_LOCATION' -Required +$azdEnvName = Get-AzdValue -Name 'AZURE_ENV_NAME' +$subscriptionId = Get-AzdValue -Name 'AZURE_SUBSCRIPTION_ID' + +if (-not [string]::IsNullOrWhiteSpace($subscriptionId)) { + & az account set --subscription $subscriptionId +} else { + $subscriptionId = ConvertTo-TrimmedCliOutput (& az account show --query id -o tsv) +} + +$seedSource = if ([string]::IsNullOrWhiteSpace($azdEnvName)) { $governanceResourceGroup } else { $azdEnvName } +$tagEnvName = if ([string]::IsNullOrWhiteSpace($azdEnvName)) { 'unknown' } else { $azdEnvName } +$baseNameSeed = ConvertTo-SafeNamePart $seedSource +$baseCompactSeed = ConvertTo-CompactNamePart $seedSource +$hasSpokeSuffix = -not [string]::IsNullOrWhiteSpace($SpokeSuffix) + +if ($hasSpokeSuffix) { + $spokeSuffixName = ConvertTo-SafeNamePart $SpokeSuffix + $spokeSuffixCompact = ConvertTo-CompactNamePart $SpokeSuffix + $nameSeed = "$baseNameSeed-spoke-$spokeSuffixName" + $compactSeed = "${baseCompactSeed}spoke$spokeSuffixCompact" + $defaultSpokeResourceGroupName = "$governanceResourceGroup-spoke-$spokeSuffixName" +} else { + $nameSeed = $baseNameSeed + $compactSeed = $baseCompactSeed + $defaultSpokeResourceGroupName = "$governanceResourceGroup-spoke" +} + +$subscriptionSuffix = ($subscriptionId -replace '-', '').Substring(0, 8) + +if ([string]::IsNullOrWhiteSpace($SpokeResourceGroupName)) { + $SpokeResourceGroupName = $defaultSpokeResourceGroupName +} +if ([string]::IsNullOrWhiteSpace($FoundryProjectName)) { + $FoundryProjectName = 'citadel-agents-project' +} + +if ([string]::IsNullOrWhiteSpace($FoundryAccountName)) { + $FoundryAccountName = "aif-$nameSeed-$subscriptionSuffix" + if ($FoundryAccountName.Length -gt 64) { $FoundryAccountName = $FoundryAccountName.Substring(0, 64).TrimEnd('-') } +} + +if ([string]::IsNullOrWhiteSpace($KeyVaultName)) { + $KeyVaultName = "kv$compactSeed$subscriptionSuffix" + if ($KeyVaultName.Length -gt 24) { $KeyVaultName = $KeyVaultName.Substring(0, 24) } +} + +$logAnalyticsWorkspaceName = if ($env:SPOKE_LOG_ANALYTICS_NAME) { $env:SPOKE_LOG_ANALYTICS_NAME } else { "law-$nameSeed-$subscriptionSuffix" } +if ($logAnalyticsWorkspaceName.Length -gt 63) { $logAnalyticsWorkspaceName = $logAnalyticsWorkspaceName.Substring(0, 63).TrimEnd('-') } + +$appInsightsName = if ($env:SPOKE_APP_INSIGHTS_NAME) { $env:SPOKE_APP_INSIGHTS_NAME } else { "appi-$nameSeed-$subscriptionSuffix" } +if ($appInsightsName.Length -gt 255) { $appInsightsName = $appInsightsName.Substring(0, 255).TrimEnd('-') } + +$foundryAppInsightsConnectionName = if ($env:SPOKE_AI_FOUNDRY_APPINSIGHTS_CONNECTION_NAME) { $env:SPOKE_AI_FOUNDRY_APPINSIGHTS_CONNECTION_NAME } else { 'appinsights-connection' } + +$currentUserObjectId = (& az ad signed-in-user show --query id -o tsv 2>$null) +if ([string]::IsNullOrWhiteSpace($currentUserObjectId)) { + throw 'Could not resolve the signed-in user object ID. This script expects an interactive user login.' +} +$currentUserObjectId = ConvertTo-TrimmedCliOutput $currentUserObjectId + +Write-Step 'Creating spoke resource group' +& az group create ` + --name $SpokeResourceGroupName ` + --location $location ` + --tags "azd-env-name=$tagEnvName" 'workload=citadel-spoke' ` + -o none + +Write-Step 'Creating Azure AI Foundry account' +if ((Invoke-NativeCommandQuietly { & az cognitiveservices account show --name $FoundryAccountName --resource-group $SpokeResourceGroupName }) -ne 0) { + & az cognitiveservices account create ` + --name $FoundryAccountName ` + --resource-group $SpokeResourceGroupName ` + --location $location ` + --kind AIServices ` + --sku S0 ` + --assign-identity ` + --custom-domain $FoundryAccountName ` + --allow-project-management true ` + --yes ` + --tags "azd-env-name=$tagEnvName" 'workload=citadel-spoke' $securityControlTag ` + -o none +} else { + Write-Host "Foundry account already exists: $FoundryAccountName" +} + +$foundryAccountId = ConvertTo-TrimmedCliOutput (& az cognitiveservices account show ` + --name $FoundryAccountName ` + --resource-group $SpokeResourceGroupName ` + --query id ` + -o tsv) +Set-SecurityControlTag -ResourceId $foundryAccountId + +$foundryAccountPatchBody = @{ + properties = @{ + allowProjectManagement = $true + disableLocalAuth = $true + publicNetworkAccess = 'Enabled' + networkAcls = @{ + defaultAction = 'Allow' + ipRules = @() + virtualNetworkRules = @() + } + } +} | ConvertTo-Json -Depth 5 -Compress + +$foundryAccountPatchPath = Join-Path ([System.IO.Path]::GetTempPath()) ("foundry-account-patch-{0}.json" -f [System.Guid]::NewGuid().ToString('N')) +[System.IO.File]::WriteAllText($foundryAccountPatchPath, $foundryAccountPatchBody, [System.Text.UTF8Encoding]::new($false)) + +try { + & az resource patch ` + --ids $foundryAccountId ` + --api-version 2025-06-01 ` + --properties "@$foundryAccountPatchPath" ` + --only-show-errors ` + -o none +} +finally { + Remove-Item -Path $foundryAccountPatchPath -Force -ErrorAction SilentlyContinue +} + +Write-Step 'Creating Azure AI Foundry project' +if ((Invoke-NativeCommandQuietly { & az cognitiveservices account project show --name $FoundryAccountName --resource-group $SpokeResourceGroupName --project-name $FoundryProjectName }) -ne 0) { + & az cognitiveservices account project create ` + --name $FoundryAccountName ` + --resource-group $SpokeResourceGroupName ` + --project-name $FoundryProjectName ` + --location $location ` + --assign-identity ` + --display-name $FoundryProjectName ` + --description 'Citadel workshop project for Foundry Agents.' ` + -o none +} else { + Write-Host "Foundry project already exists: $FoundryProjectName" +} + +$projectResourceId = "/subscriptions/$subscriptionId/resourceGroups/$SpokeResourceGroupName/providers/Microsoft.CognitiveServices/accounts/$FoundryAccountName/projects/$FoundryProjectName" +Set-SecurityControlTag -ResourceId $projectResourceId + +Write-Step 'Ensuring Application Insights CLI extension' +& az extension add --name application-insights --upgrade --only-show-errors *> $null + +Write-Step 'Creating Log Analytics workspace' +if ((Invoke-NativeCommandQuietly { & az monitor log-analytics workspace show --resource-group $SpokeResourceGroupName --workspace-name $logAnalyticsWorkspaceName }) -ne 0) { + & az monitor log-analytics workspace create ` + --resource-group $SpokeResourceGroupName ` + --workspace-name $logAnalyticsWorkspaceName ` + --location $location ` + --sku PerGB2018 ` + --tags "azd-env-name=$tagEnvName" 'workload=citadel-spoke' ` + -o none +} else { + Write-Host "Log Analytics workspace already exists: $logAnalyticsWorkspaceName" +} + +$logAnalyticsWorkspaceId = ConvertTo-TrimmedCliOutput (& az monitor log-analytics workspace show ` + --resource-group $SpokeResourceGroupName ` + --workspace-name $logAnalyticsWorkspaceName ` + --query id ` + -o tsv) + +Write-Step 'Creating Application Insights' +if ((Invoke-NativeCommandQuietly { & az monitor app-insights component show --app $appInsightsName --resource-group $SpokeResourceGroupName }) -ne 0) { + & az monitor app-insights component create ` + --app $appInsightsName ` + --location $location ` + --resource-group $SpokeResourceGroupName ` + --workspace $logAnalyticsWorkspaceId ` + --kind web ` + --application-type web ` + --tags "azd-env-name=$tagEnvName" 'workload=citadel-spoke' ` + -o none +} else { + Write-Host "Application Insights already exists: $appInsightsName" +} + +$appInsightsId = ConvertTo-TrimmedCliOutput (& az monitor app-insights component show ` + --app $appInsightsName ` + --resource-group $SpokeResourceGroupName ` + --query id ` + -o tsv) + +$appInsightsConnectionString = ConvertTo-TrimmedCliOutput (& az monitor app-insights component show ` + --app $appInsightsName ` + --resource-group $SpokeResourceGroupName ` + --query connectionString ` + -o tsv) + +$foundryConnectionBody = @{ + properties = @{ + category = 'AppInsights' + target = $appInsightsId + authType = 'ApiKey' + credentials = @{ + key = $appInsightsConnectionString + } + metadata = @{ + ApiType = 'Azure' + ResourceId = $appInsightsId + } + isSharedToAll = $true + } +} | ConvertTo-Json -Depth 6 -Compress + +$foundryConnectionBodyPath = Join-Path ([System.IO.Path]::GetTempPath()) ("foundry-project-connection-{0}.json" -f [System.Guid]::NewGuid().ToString('N')) +[System.IO.File]::WriteAllText($foundryConnectionBodyPath, $foundryConnectionBody, [System.Text.UTF8Encoding]::new($false)) + +Write-Step 'Connecting Application Insights to Azure AI Foundry project' +# The cognitiveservices project connection wrapper currently rejects a valid +# App Insights payload, so use the ARM resource endpoint directly. +try { + & az rest ` + --method PUT ` + --url "https://management.azure.com$projectResourceId/connections/${foundryAppInsightsConnectionName}?api-version=$foundryAppInsightsConnectionApiVersion" ` + --body "@$foundryConnectionBodyPath" ` + -o none +} +finally { + Remove-Item -Path $foundryConnectionBodyPath -Force -ErrorAction SilentlyContinue +} + +Write-Step 'Creating Key Vault' +if ((Invoke-NativeCommandQuietly { & az keyvault show --name $KeyVaultName --resource-group $SpokeResourceGroupName }) -ne 0) { + $keyVaultCreateArgs = @( + 'keyvault', 'create', + '--name', $KeyVaultName, + '--resource-group', $SpokeResourceGroupName, + '--location', $location, + '--sku', 'standard', + '--enable-rbac-authorization', 'true', + '--public-network-access', 'Enabled', + '--default-action', 'Allow', + '--bypass', 'AzureServices', + '--tags', "azd-env-name=$tagEnvName", 'workload=citadel-spoke', $securityControlTag, + '-o', 'none' + ) + if ([string]::Equals($KeyVaultEnablePurgeProtection, 'true', [System.StringComparison]::OrdinalIgnoreCase)) { + $keyVaultCreateArgs += @('--enable-purge-protection', 'true') + } + & az @keyVaultCreateArgs +} else { + Write-Host "Key Vault already exists: $KeyVaultName" +} + +$existingKeyVaultPurgeProtection = ConvertTo-TrimmedCliOutput (& az keyvault show ` + --name $KeyVaultName ` + --resource-group $SpokeResourceGroupName ` + --query 'properties.enablePurgeProtection' ` + -o tsv) + +if ($existingKeyVaultPurgeProtection -eq 'true') { + Write-Warning 'Key Vault purge protection is enabled and cannot be disabled. Immediate purge will not be available for this vault.' +} + +$keyVaultId = ConvertTo-TrimmedCliOutput (& az keyvault show ` + --name $KeyVaultName ` + --resource-group $SpokeResourceGroupName ` + --query id ` + -o tsv) +Set-SecurityControlTag -ResourceId $keyVaultId + +Write-Step 'Assigning RBAC roles to the signed-in user' +Add-RoleAssignmentIfMissing -PrincipalId $currentUserObjectId -PrincipalType User -RoleId $aiProjectManagerRoleId -RoleName 'Azure AI Project Manager' -Scope $foundryAccountId +Add-RoleAssignmentIfMissing -PrincipalId $currentUserObjectId -PrincipalType User -RoleId $keyVaultSecretsUserRoleId -RoleName 'Key Vault Secrets User' -Scope $keyVaultId + +# --- Azure Container Registry --- + +$acrName = if ($env:SPOKE_ACR_NAME) { $env:SPOKE_ACR_NAME } else { "acr$compactSeed$subscriptionSuffix" } +$acrName = $acrName -replace '[^a-zA-Z0-9]', '' +if ($acrName.Length -gt 50) { $acrName = $acrName.Substring(0, 50) } + +Write-Step 'Creating Azure Container Registry' +if ((Invoke-NativeCommandQuietly { & az acr show --name $acrName --resource-group $SpokeResourceGroupName }) -ne 0) { + & az acr create ` + --name $acrName ` + --resource-group $SpokeResourceGroupName ` + --location $location ` + --sku Basic ` + --admin-enabled false ` + --tags "azd-env-name=$tagEnvName" 'workload=citadel-spoke' ` + -o none +} else { + Write-Host "Container Registry already exists: $acrName" +} + +$acrId = ConvertTo-TrimmedCliOutput (& az acr show ` + --name $acrName ` + --resource-group $SpokeResourceGroupName ` + --query id ` + -o tsv) + +$acrLoginServer = ConvertTo-TrimmedCliOutput (& az acr show ` + --name $acrName ` + --resource-group $SpokeResourceGroupName ` + --query loginServer ` + -o tsv) + +Write-Step 'Assigning AcrPull to Foundry project managed identity' +$foundryProjectMiObjectId = ConvertTo-TrimmedCliOutput (& az cognitiveservices account project show ` + --name $FoundryAccountName ` + --resource-group $SpokeResourceGroupName ` + --project-name $FoundryProjectName ` + --query 'identity.principalId' ` + -o tsv) + +if (-not [string]::IsNullOrWhiteSpace($foundryProjectMiObjectId)) { + Add-RoleAssignmentIfMissing -PrincipalId $foundryProjectMiObjectId -PrincipalType ServicePrincipal -RoleId $acrPullRoleId -RoleName 'AcrPull (Foundry project MI)' -Scope $acrId +} else { + Write-Warning 'Could not resolve Foundry project managed identity. AcrPull role assignment skipped.' +} + +Write-Step 'Saving spoke resource values to azd environment' +& azd env set SPOKE_RESOURCE_GROUP $SpokeResourceGroupName *> $null +& azd env set SPOKE_AI_FOUNDRY_ACCOUNT_NAME $FoundryAccountName *> $null +& azd env set SPOKE_AI_FOUNDRY_PROJECT_NAME $FoundryProjectName *> $null +& azd env set SPOKE_LOG_ANALYTICS_NAME $logAnalyticsWorkspaceName *> $null +& azd env set SPOKE_APP_INSIGHTS_NAME $appInsightsName *> $null +& azd env set SPOKE_APP_INSIGHTS_ID $appInsightsId *> $null +& azd env set SPOKE_AI_FOUNDRY_APPINSIGHTS_CONNECTION_NAME $foundryAppInsightsConnectionName *> $null +& azd env set SPOKE_KEY_VAULT_NAME $KeyVaultName *> $null +& azd env set SPOKE_ACR_NAME $acrName *> $null +& azd env set SPOKE_ACR_LOGIN_SERVER $acrLoginServer *> $null + +Write-Host "`nSpoke resources are ready." +Write-Host "Resource group: $SpokeResourceGroupName" +Write-Host "Foundry account: $FoundryAccountName" +Write-Host "Foundry project: $FoundryProjectName" +Write-Host "Log Analytics workspace: $logAnalyticsWorkspaceName" +Write-Host "Application Insights: $appInsightsName" +Write-Host "Foundry App Insights connection: $foundryAppInsightsConnectionName" +Write-Host "Key Vault: $KeyVaultName" +Write-Host "`nAfter deleting these resources, purge soft-deleted names with:" +Write-Host "az cognitiveservices account purge --name `"$FoundryAccountName`" --resource-group `"$SpokeResourceGroupName`" --location `"$location`"" +Write-Host "az keyvault purge --name `"$KeyVaultName`" --location `"$location`"" diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.sh new file mode 100755 index 000000000..b35341bd2 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/deploy-spoke-foundry.sh @@ -0,0 +1,455 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Silence a known Azure CLI Python SyntaxWarning from an upstream dependency. +export PYTHONWARNINGS="${PYTHONWARNINGS:+$PYTHONWARNINGS,}ignore::SyntaxWarning" + +AI_PROJECT_MANAGER_ROLE_ID="eadc314b-1a2d-4efa-be10-5d325db5065e" +KEY_VAULT_SECRETS_USER_ROLE_ID="4633458b-17de-408a-b874-0445c86b69e6" +ACR_PULL_ROLE_ID="7f951dda-4ed3-4680-a7ca-43fe172d538d" +FOUNDRY_APPINSIGHTS_CONNECTION_API_VERSION="2025-06-01" +SECURITY_CONTROL_TAG="SecurityControl=Ignore" +spoke_suffix="${SPOKE_SUFFIX:-}" + +while [[ $# -gt 0 ]]; do + case "$1" in + -s|--spoke-suffix) + if [[ $# -lt 2 ]]; then + printf 'Missing value for %s\n' "$1" >&2 + exit 1 + fi + spoke_suffix="$2" + shift 2 + ;; + --spoke-suffix=*) + spoke_suffix="${1#*=}" + shift + ;; + *) + if [[ -z "$spoke_suffix" ]]; then + spoke_suffix="$1" + shift + else + printf 'Unknown argument: %s\n' "$1" >&2 + exit 1 + fi + ;; + esac +done + +log() { + printf '\n==> %s\n' "$1" +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + printf 'Required command not found: %s\n' "$1" >&2 + exit 1 + fi +} + +azd_get_required() { + local key="$1" + local value + value="$(azd env get-value "$key" 2>/dev/null || true)" + if [[ -z "$value" ]]; then + printf 'Required azd environment value is missing: %s\nRun azd up/provision first, then rerun this script.\n' "$key" >&2 + exit 1 + fi + printf '%s' "$value" +} + +azd_get_optional() { + azd env get-value "$1" 2>/dev/null || true +} + +safe_name_part() { + local value="$1" + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g; s/--*/-/g; s/^-*//; s/-*$//')" + if [[ -z "$value" ]]; then + value="citadel" + fi + printf '%s' "$value" +} + +compact_name_part() { + local value="$1" + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]//g')" + if [[ -z "$value" ]]; then + value="citadel" + fi + printf '%s' "$value" +} + +trim_trailing_hyphen() { + sed 's/-*$//' +} + +assign_role_if_missing() { + local principal_id="$1" + local principal_type="$2" + local role_id="$3" + local role_name="$4" + local scope="$5" + + local assignment_id + assignment_id="$(az role assignment list \ + --assignee-object-id "$principal_id" \ + --role "$role_id" \ + --scope "$scope" \ + --all \ + --fill-principal-name false \ + --fill-role-definition-name false \ + --query '[0].id' \ + -o tsv 2>/dev/null || true)" + + if [[ -n "$assignment_id" ]]; then + printf 'Role assignment already exists: %s\n' "$role_name" + return + fi + + az role assignment create \ + --assignee-object-id "$principal_id" \ + --assignee-principal-type "$principal_type" \ + --role "$role_id" \ + --scope "$scope" \ + --only-show-errors \ + -o none + printf 'Assigned role: %s\n' "$role_name" +} + +tag_resource_ignore() { + local resource_id="$1" + + if [[ -z "$resource_id" ]]; then + return + fi + + az tag update \ + --resource-id "$resource_id" \ + --operation Merge \ + --tags "$SECURITY_CONTROL_TAG" \ + --only-show-errors \ + -o none +} + +require_command az +require_command azd + +if ! az account show >/dev/null 2>&1; then + printf 'Azure CLI is not logged in. Run az login, then rerun this script.\n' >&2 + exit 1 +fi + +if ! az cognitiveservices account project -h >/dev/null 2>&1; then + printf 'This Azure CLI does not include az cognitiveservices account project. Update Azure CLI and rerun this script.\n' >&2 + exit 1 +fi + +governance_rg="$(azd_get_required AZURE_RESOURCE_GROUP)" +location="$(azd_get_required AZURE_LOCATION)" +azd_env_name="$(azd_get_optional AZURE_ENV_NAME)" +subscription_id="$(azd_get_optional AZURE_SUBSCRIPTION_ID)" + +if [[ -n "$subscription_id" ]]; then + az account set --subscription "$subscription_id" +else + subscription_id="$(az account show --query id -o tsv)" +fi + +base_name_seed="$(safe_name_part "${azd_env_name:-$governance_rg}")" +base_compact_seed="$(compact_name_part "${azd_env_name:-$governance_rg}")" + +if [[ -n "$spoke_suffix" ]]; then + spoke_suffix_name="$(safe_name_part "$spoke_suffix")" + spoke_suffix_compact="$(compact_name_part "$spoke_suffix")" + name_seed="${base_name_seed}-spoke-${spoke_suffix_name}" + compact_seed="${base_compact_seed}spoke${spoke_suffix_compact}" + default_spoke_resource_group_name="${governance_rg}-spoke-${spoke_suffix_name}" +else + name_seed="$base_name_seed" + compact_seed="$base_compact_seed" + default_spoke_resource_group_name="${governance_rg}-spoke" +fi + +spoke_resource_group_name="${SPOKE_RESOURCE_GROUP_NAME:-$default_spoke_resource_group_name}" +subscription_suffix="$(printf '%s' "$subscription_id" | tr -d '-' | cut -c1-8)" + +default_foundry_name="$(printf 'aif-%s-%s' "$name_seed" "$subscription_suffix" | cut -c1-64 | trim_trailing_hyphen)" +default_log_analytics_name="$(printf 'law-%s-%s' "$name_seed" "$subscription_suffix" | cut -c1-63 | trim_trailing_hyphen)" +default_app_insights_name="$(printf 'appi-%s-%s' "$name_seed" "$subscription_suffix" | cut -c1-255 | trim_trailing_hyphen)" +default_key_vault_name="$(printf 'kv%s%s' "$compact_seed" "$subscription_suffix" | cut -c1-24)" + +foundry_account_name="${FOUNDRY_ACCOUNT_NAME:-$default_foundry_name}" +foundry_project_name="${FOUNDRY_PROJECT_NAME:-citadel-agents-project}" +log_analytics_workspace_name="${SPOKE_LOG_ANALYTICS_NAME:-$default_log_analytics_name}" +app_insights_name="${SPOKE_APP_INSIGHTS_NAME:-$default_app_insights_name}" +foundry_appinsights_connection_name="${SPOKE_AI_FOUNDRY_APPINSIGHTS_CONNECTION_NAME:-appinsights-connection}" +key_vault_name="${KEY_VAULT_NAME:-$default_key_vault_name}" +key_vault_enable_purge_protection="${KEY_VAULT_ENABLE_PURGE_PROTECTION:-false}" +current_user_object_id="$(az ad signed-in-user show --query id -o tsv 2>/dev/null || true)" + +if [[ -z "$current_user_object_id" ]]; then + printf 'Could not resolve the signed-in user object ID. This script expects an interactive user login.\n' >&2 + exit 1 +fi + +log "Creating spoke resource group" +az group create \ + --name "$spoke_resource_group_name" \ + --location "$location" \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" \ + -o none + +log "Creating Azure AI Foundry account" +if ! az cognitiveservices account show --name "$foundry_account_name" --resource-group "$spoke_resource_group_name" >/dev/null 2>&1; then + az cognitiveservices account create \ + --name "$foundry_account_name" \ + --resource-group "$spoke_resource_group_name" \ + --location "$location" \ + --kind AIServices \ + --sku S0 \ + --assign-identity \ + --custom-domain "$foundry_account_name" \ + --allow-project-management true \ + --yes \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" "$SECURITY_CONTROL_TAG" \ + -o none +else + printf 'Foundry account already exists: %s\n' "$foundry_account_name" +fi + +foundry_account_id="$(az cognitiveservices account show \ + --name "$foundry_account_name" \ + --resource-group "$spoke_resource_group_name" \ + --query id \ + -o tsv)" +tag_resource_ignore "$foundry_account_id" + +az rest \ + --method patch \ + --uri "https://management.azure.com${foundry_account_id}?api-version=2025-06-01" \ + --headers 'Content-Type=application/json' \ + --body '{"properties":{"allowProjectManagement":true,"disableLocalAuth":true,"publicNetworkAccess":"Enabled","networkAcls":{"defaultAction":"Allow","ipRules":[],"virtualNetworkRules":[]}}}' \ + -o none + +log "Creating Azure AI Foundry project" +if ! az cognitiveservices account project show \ + --name "$foundry_account_name" \ + --resource-group "$spoke_resource_group_name" \ + --project-name "$foundry_project_name" >/dev/null 2>&1; then + az cognitiveservices account project create \ + --name "$foundry_account_name" \ + --resource-group "$spoke_resource_group_name" \ + --project-name "$foundry_project_name" \ + --location "$location" \ + --assign-identity \ + --display-name "$foundry_project_name" \ + --description 'Citadel workshop project for Foundry Agents.' \ + -o none +else + printf 'Foundry project already exists: %s\n' "$foundry_project_name" +fi + +project_resource_id="/subscriptions/${subscription_id}/resourceGroups/${spoke_resource_group_name}/providers/Microsoft.CognitiveServices/accounts/${foundry_account_name}/projects/${foundry_project_name}" +tag_resource_ignore "$project_resource_id" + +log "Ensuring Application Insights CLI extension" +az extension add --name application-insights --upgrade --only-show-errors >/dev/null + +log "Creating Log Analytics workspace" +if ! az monitor log-analytics workspace show \ + --resource-group "$spoke_resource_group_name" \ + --workspace-name "$log_analytics_workspace_name" >/dev/null 2>&1; then + az monitor log-analytics workspace create \ + --resource-group "$spoke_resource_group_name" \ + --workspace-name "$log_analytics_workspace_name" \ + --location "$location" \ + --sku PerGB2018 \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" \ + -o none +else + printf 'Log Analytics workspace already exists: %s\n' "$log_analytics_workspace_name" +fi + +log_analytics_workspace_id="$(az monitor log-analytics workspace show \ + --resource-group "$spoke_resource_group_name" \ + --workspace-name "$log_analytics_workspace_name" \ + --query id \ + -o tsv)" + +log "Creating Application Insights" +if ! az monitor app-insights component show \ + --app "$app_insights_name" \ + --resource-group "$spoke_resource_group_name" >/dev/null 2>&1; then + az monitor app-insights component create \ + --app "$app_insights_name" \ + --location "$location" \ + --resource-group "$spoke_resource_group_name" \ + --workspace "$log_analytics_workspace_id" \ + --kind web \ + --application-type web \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" \ + -o none +else + printf 'Application Insights already exists: %s\n' "$app_insights_name" +fi + +app_insights_id="$(az monitor app-insights component show \ + --app "$app_insights_name" \ + --resource-group "$spoke_resource_group_name" \ + --query id \ + -o tsv)" + +app_insights_connection_string="$(az monitor app-insights component show \ + --app "$app_insights_name" \ + --resource-group "$spoke_resource_group_name" \ + --query connectionString \ + -o tsv)" + +connection_body_file="$(mktemp)" +trap 'rm -f "$connection_body_file"' EXIT +cat >"$connection_body_file" </dev/null 2>&1; then + key_vault_create_args=( + --name "$key_vault_name" \ + --resource-group "$spoke_resource_group_name" \ + --location "$location" \ + --sku standard \ + --enable-rbac-authorization true \ + --public-network-access Enabled \ + --default-action Allow \ + --bypass AzureServices \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" \ + -o none + ) + case "$key_vault_enable_purge_protection" in + true|TRUE|True) + key_vault_create_args+=(--enable-purge-protection true) + ;; + esac + az keyvault create "${key_vault_create_args[@]}" +else + printf 'Key Vault already exists: %s\n' "$key_vault_name" +fi + +existing_key_vault_purge_protection="$(az keyvault show \ + --name "$key_vault_name" \ + --resource-group "$spoke_resource_group_name" \ + --query 'properties.enablePurgeProtection' \ + -o tsv)" + +if [[ "$existing_key_vault_purge_protection" == "true" ]]; then + printf 'Warning: Key Vault purge protection is enabled and cannot be disabled. Immediate purge will not be available for this vault.\n' >&2 +fi + +key_vault_id="$(az keyvault show \ + --name "$key_vault_name" \ + --resource-group "$spoke_resource_group_name" \ + --query id \ + -o tsv)" +tag_resource_ignore "$key_vault_id" + +log "Assigning RBAC roles to the signed-in user" +assign_role_if_missing "$current_user_object_id" User "$AI_PROJECT_MANAGER_ROLE_ID" "Azure AI Project Manager" "$foundry_account_id" +assign_role_if_missing "$current_user_object_id" User "$KEY_VAULT_SECRETS_USER_ROLE_ID" "Key Vault Secrets User" "$key_vault_id" + +# --- Azure Container Registry --- + +acr_name="${SPOKE_ACR_NAME:-$(printf 'acr%s%s' "$compact_seed" "$subscription_suffix" | sed 's/[^a-zA-Z0-9]//g' | cut -c1-50)}" + +log "Creating Azure Container Registry" +if ! az acr show --name "$acr_name" --resource-group "$spoke_resource_group_name" >/dev/null 2>&1; then + az acr create \ + --name "$acr_name" \ + --resource-group "$spoke_resource_group_name" \ + --location "$location" \ + --sku Basic \ + --admin-enabled false \ + --tags "azd-env-name=${azd_env_name:-unknown}" "workload=citadel-spoke" "$SECURITY_CONTROL_TAG" \ + -o none +else + printf 'Container Registry already exists: %s\n' "$acr_name" +fi + +acr_id="$(az acr show \ + --name "$acr_name" \ + --resource-group "$spoke_resource_group_name" \ + --query id \ + -o tsv)" + +acr_login_server="$(az acr show \ + --name "$acr_name" \ + --resource-group "$spoke_resource_group_name" \ + --query loginServer \ + -o tsv)" + +log "Assigning AcrPull to Foundry project managed identity" +foundry_project_mi_object_id="$(az cognitiveservices account project show \ + --name "$foundry_account_name" \ + --resource-group "$spoke_resource_group_name" \ + --project-name "$foundry_project_name" \ + --query 'identity.principalId' \ + -o tsv 2>/dev/null || true)" + +if [[ -n "$foundry_project_mi_object_id" ]]; then + assign_role_if_missing "$foundry_project_mi_object_id" ServicePrincipal "$ACR_PULL_ROLE_ID" "AcrPull (Foundry project MI)" "$acr_id" +else + printf 'Warning: Could not resolve Foundry project managed identity. AcrPull role assignment skipped.\n' >&2 +fi + +log "Saving spoke resource values to azd environment" +azd env set SPOKE_RESOURCE_GROUP "$spoke_resource_group_name" >/dev/null +azd env set SPOKE_AI_FOUNDRY_ACCOUNT_NAME "$foundry_account_name" >/dev/null +azd env set SPOKE_AI_FOUNDRY_PROJECT_NAME "$foundry_project_name" >/dev/null +azd env set SPOKE_LOG_ANALYTICS_NAME "$log_analytics_workspace_name" >/dev/null +azd env set SPOKE_APP_INSIGHTS_NAME "$app_insights_name" >/dev/null +azd env set SPOKE_APP_INSIGHTS_ID "$app_insights_id" >/dev/null +azd env set SPOKE_AI_FOUNDRY_APPINSIGHTS_CONNECTION_NAME "$foundry_appinsights_connection_name" >/dev/null +azd env set SPOKE_KEY_VAULT_NAME "$key_vault_name" >/dev/null +azd env set SPOKE_ACR_NAME "$acr_name" >/dev/null +azd env set SPOKE_ACR_LOGIN_SERVER "$acr_login_server" >/dev/null + +cat <.documents.azure.com:443/ + +Authentication uses DefaultAzureCredential (Entra OAuth). +""" + +import argparse +import json +from pathlib import Path + +from azure.cosmos import CosmosClient +from azure.identity import DefaultAzureCredential + +DATABASE_NAME = "ai-usage-db" +CONTAINER_NAME = "model-pricing" +DEFAULT_INPUT_FILE = Path(__file__).parent / "model-pricing.json" + + +def main(): + parser = argparse.ArgumentParser(description="Import model pricing data into Cosmos DB") + parser.add_argument("--endpoint", required=True, help="Cosmos DB account endpoint URL") + parser.add_argument("--input", default=str(DEFAULT_INPUT_FILE), help="Path to model-pricing.json") + args = parser.parse_args() + + credential = DefaultAzureCredential() + client = CosmosClient(url=args.endpoint, credential=credential, enable_endpoint_discovery=False) + + database = client.get_database_client(DATABASE_NAME) + container = database.get_container_client(CONTAINER_NAME) + + with open(args.input, encoding="utf-8") as f: + items = json.load(f) + + for item in items: + container.upsert_item(item) + print(f"Upserted: {item['model']} (id={item['id']})") + + print(f"\nDone. {len(items)} items imported to {DATABASE_NAME}/{CONTAINER_NAME}.") + + +if __name__ == "__main__": + main() diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/model-pricing.json b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/model-pricing.json new file mode 100644 index 000000000..57a8cf460 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/model-pricing.json @@ -0,0 +1,93 @@ +[ + { + "id": "1", + "model": "text-embedding-3-large", + "deploymentName": "text-embedding-3-large", + "isActive": true, + "CostPerInputUnit": 0.000143, + "CostPerOutputUnit": 0, + "CostUnit": 1000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "2", + "model": "gpt-4.1", + "deploymentName": "gpt-4.1", + "isActive": true, + "CostPerInputUnit": 2, + "CostPerOutputUnit": 8, + "CostUnit": 1000000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "3", + "model": "gpt-5.4-mini", + "deploymentName": "gpt-5.4-mini", + "isActive": true, + "CostPerInputUnit": 0.75, + "CostPerOutputUnit": 4.50, + "CostUnit": 1000000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "4", + "model": "DeepSeek-R1", + "deploymentName": "DeepSeek-R1", + "isActive": true, + "CostPerInputUnit": 0.00135, + "CostPerOutputUnit": 0.0054, + "CostUnit": 1000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "5", + "model": "Mistral-Large-3", + "deploymentName": "Mistral-Large-3", + "isActive": true, + "CostPerInputUnit": 0.5, + "CostPerOutputUnit": 1.5, + "CostUnit": 1000000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "6", + "model": "gpt-5.2", + "deploymentName": "gpt-5.2", + "isActive": true, + "CostPerInputUnit": 1.75, + "CostPerOutputUnit": 14, + "CostUnit": 1000000, + "BaseCost": 0, + "Currency": "USD", + "CalculationMethod": "tokens", + "region": "ALL" + }, + { + "id": "7", + "model": "ai-search", + "deploymentName": "ai-search-marketing", + "isActive": true, + "CostPerInputUnit": 1, + "CostPerOutputUnit": 0, + "CostUnit": 1, + "BaseCost": 1000, + "Currency": "USD", + "CalculationMethod": "percentage", + "region": "ALL" + } +] \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.ps1 b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.ps1 new file mode 100644 index 000000000..5abb842b1 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.ps1 @@ -0,0 +1,79 @@ +[CmdletBinding()] +param( + [string]$KernelName = $(if ($env:KERNEL_NAME) { $env:KERNEL_NAME } else { 'workshop' }), + [int]$TimeoutSeconds = $(if ($env:TIMEOUT_SECONDS) { [int]$env:TIMEOUT_SECONDS } else { 1200 }), + [bool]$AllowErrors = $(if ($null -ne $env:ALLOW_ERRORS) { [bool]::Parse($env:ALLOW_ERRORS) } else { $true }), + [string]$PythonBin = $(if ($env:PYTHON_BIN) { $env:PYTHON_BIN } else { 'python' }) +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' +if ($PSVersionTable.PSVersion.Major -ge 7) { + $PSNativeCommandUseErrorActionPreference = $false +} + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" +} + +function Assert-Command { + param([string]$Name) + if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { + throw "Required command not found: $Name" + } +} + +$scriptDir = Split-Path -Parent $PSCommandPath +$workshopDir = Split-Path -Parent $scriptDir +$venvPython = Join-Path $workshopDir '.venv/bin/python' + +if (Test-Path $venvPython) { + $PythonBin = $venvPython +} + +Assert-Command $PythonBin + +$notebooks = Get-ChildItem -Path $workshopDir -Filter '*.ipynb' -File | Sort-Object Name +if ($notebooks.Count -eq 0) { + throw "No notebooks found in $workshopDir" +} + +Write-Step "Registering Jupyter kernel '$KernelName'" +$pythonVersion = (& $PythonBin -c "import sys; print('.'.join(map(str, sys.version_info[:3])))") | Select-Object -First 1 +& $PythonBin -m ipykernel install --user --name $KernelName --display-name "workshop ($pythonVersion)" *> $null + +$successCount = 0 +$failureCount = 0 + +foreach ($notebook in $notebooks) { + Write-Step "Executing $($notebook.Name)" + + & $PythonBin -m jupyter nbconvert ` + --to notebook ` + --execute ` + --inplace ` + --ExecutePreprocessor.kernel_name=$KernelName ` + --ExecutePreprocessor.timeout=$TimeoutSeconds ` + --ExecutePreprocessor.allow_errors=$AllowErrors ` + $notebook.FullName + + if ($LASTEXITCODE -eq 0) { + Write-Host "Completed: $($notebook.Name)" + $successCount++ + continue + } + + Write-Error "Failed: $($notebook.Name)" + $failureCount++ + + if (-not $AllowErrors) { + break + } +} + +Write-Host "`nSummary: $successCount completed, $failureCount failed" + +if ($failureCount -gt 0) { + exit 1 +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.sh b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.sh new file mode 100755 index 000000000..a0bb25832 --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/scripts/run-all-notebooks.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +log() { + printf '\n==> %s\n' "$1" +} + +require_command() { + if ! command -v "$1" >/dev/null 2>&1; then + printf 'Required command not found: %s\n' "$1" >&2 + exit 1 + fi +} + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +workshop_dir="$(cd "$script_dir/.." && pwd)" + +kernel_name="${KERNEL_NAME:-workshop}" +timeout_seconds="${TIMEOUT_SECONDS:-1200}" +allow_errors="${ALLOW_ERRORS:-true}" +python_bin="${PYTHON_BIN:-python}" + +cd "$workshop_dir" + +if [[ ! -d .venv ]]; then + printf 'Expected virtual environment not found: %s/.venv\n' "$workshop_dir" >&2 + exit 1 +fi + +# shellcheck disable=SC1091 +source .venv/bin/activate + +require_command "$python_bin" +require_command jupyter + +log "Registering Jupyter kernel '$kernel_name'" +"$python_bin" -m ipykernel install --user \ + --name "$kernel_name" \ + --display-name "workshop ($("$python_bin" -c 'import sys; print(".".join(map(str, sys.version_info[:3])))'))" \ + >/dev/null + +notebooks=() +while IFS= read -r -d '' notebook; do + notebooks+=("$notebook") +done < <(find "$workshop_dir" -maxdepth 1 -type f -name '*.ipynb' -print0 | sort -z) + +if [[ ${#notebooks[@]} -eq 0 ]]; then + printf 'No notebooks found in %s\n' "$workshop_dir" >&2 + exit 1 +fi + +success_count=0 +failure_count=0 + +for notebook in "${notebooks[@]}"; do + notebook_name="$(basename "$notebook")" + log "Executing $notebook_name" + + if "$python_bin" -m jupyter nbconvert \ + --to notebook \ + --execute \ + --inplace \ + --ExecutePreprocessor.kernel_name="$kernel_name" \ + --ExecutePreprocessor.timeout="$timeout_seconds" \ + --ExecutePreprocessor.allow_errors="$allow_errors" \ + "$notebook"; then + printf 'Completed: %s\n' "$notebook_name" + success_count=$((success_count + 1)) + continue + fi + + printf 'Failed: %s\n' "$notebook_name" >&2 + failure_count=$((failure_count + 1)) + + if [[ "$allow_errors" != "true" ]]; then + break + fi +done + +printf '\nSummary: %d completed, %d failed\n' "$success_count" "$failure_count" + +if (( failure_count > 0 )); then + exit 1 +fi diff --git a/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/uv.lock b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/uv.lock new file mode 100644 index 000000000..602b1e02d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/challenges/workshop/uv.lock @@ -0,0 +1,3197 @@ +version = 1 +requires-python = ">=3.13" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "agent-framework-core" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/59/4c212abdb93074677d643e31a3c21e33ff26a3ccc351145475cd1ffffad7/agent_framework_core-1.3.0.tar.gz", hash = "sha256:91c3659718b733f70dde6fb3626edb044733e0f7aa5f9726c9774e17fae328ef", size = 365395 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/f2/c4258333f2691ee10869bf72f51d423808962ccf0c195b1f893c06c348ad/agent_framework_core-1.3.0-py3-none-any.whl", hash = "sha256:b7a5baf2beb383e9042af057df79dae4fda0b836cbc8530b3b2a57a3c12bb7ac", size = 407978 }, +] + +[[package]] +name = "agent-framework-foundry-hosting" +version = "1.0.0a260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-ai-agentserver-invocations" }, + { name = "azure-ai-agentserver-responses" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/ef/fb8652ef44182f3fb31aceb10ca141d5ac107fe627769817d554f9a0f540/agent_framework_foundry_hosting-1.0.0a260507.tar.gz", hash = "sha256:ca1c95f753a0ee200c71f394eba2af037c8ae98745e6afb896a9625007c9372d", size = 14330 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/73/0e8d6fd866c6dd0d1986ecfa2b67d76c880f30ce8897180f172fef7be9b0/agent_framework_foundry_hosting-1.0.0a260507-py3-none-any.whl", hash = "sha256:189c767a0d304dcbba742385af239efbf900a8df6a0ecf6b29f922b67c574de2", size = 15050 }, +] + +[[package]] +name = "agent-framework-openai" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/23/cb0b7dc7ed0c72ce62e21d8310bcbeb3e42d000f15f93198eb01e601ea93/agent_framework_openai-1.2.0.tar.gz", hash = "sha256:3c0a5901f15afa3bfa52fccefae0ae17f3393bb652f8ea2d18049f93e7d1fcc9", size = 45974 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/90/4d6b7f110e8aabf311e05e94ed44477f864910a531e13364364303fc79e6/agent_framework_openai-1.2.0-py3-none-any.whl", hash = "sha256:a20402825bfc84051dd9b53ad6adde50b3677b0ee90558927c41413f3393fb02", size = 50838 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930 }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927 }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141 }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476 }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507 }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465 }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523 }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113 }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351 }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205 }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618 }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185 }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311 }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147 }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356 }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637 }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896 }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721 }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663 }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094 }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701 }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360 }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023 }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795 }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405 }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082 }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346 }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891 }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113 }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088 }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976 }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444 }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128 }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029 }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758 }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883 }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668 }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461 }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661 }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800 }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382 }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724 }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027 }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644 }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630 }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403 }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924 }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119 }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072 }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819 }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441 }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345 }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047 }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548 }, +] + +[[package]] +name = "azure-ai-agentserver-core" +version = "2.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "hypercorn" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-grpc" }, + { name = "opentelemetry-sdk" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/29/1a9606d5252b02d77070a1b633dd0c26fe65a0f4a0fb0cfdaa751e2ed458/azure_ai_agentserver_core-2.0.0b3.tar.gz", hash = "sha256:e295b19a65d53c513929f52f0862bbb815cc9e9fc29d2a2825452f3136260123", size = 42573 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9b/1fc87c05b55821f33c46c5e8a3b97a573aa2fc4bff387e75cca1a87800b4/azure_ai_agentserver_core-2.0.0b3-py3-none-any.whl", hash = "sha256:5ef921eb9fd9c0f15682fe930320fae50dccfa915d7518f9a16d99014bbcb3cb", size = 29127 }, +] + +[[package]] +name = "azure-ai-agentserver-invocations" +version = "1.0.0b3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-ai-agentserver-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/95/ebab2b06777352b33dd4c407fa5624765b7443d3b4b5fb6cb1f51660643b/azure_ai_agentserver_invocations-1.0.0b3.tar.gz", hash = "sha256:1eaad3ae8dc6a28038b9a16c7b5f853fda33202c1ea57559992a6c6fe71952a4", size = 31002 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/43/a421671296ae33b62af3a034869fa82ff1979e5f455a29924d30ae1b8307/azure_ai_agentserver_invocations-1.0.0b3-py3-none-any.whl", hash = "sha256:771a15a3509e049b56f71c43c87a3fdeecd12addddcae0f80339990adc41e678", size = 11433 }, +] + +[[package]] +name = "azure-ai-agentserver-responses" +version = "1.0.0b5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "azure-ai-agentserver-core" }, + { name = "azure-core" }, + { name = "isodate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/27/3ecb7fe704ff8764199bfbe4cc1e584a520a9affe042470d9d50b6e1e73a/azure_ai_agentserver_responses-1.0.0b5.tar.gz", hash = "sha256:0b627b810359c792ea7b6fa6782abaf6df32d9bc9e5a569ad722afcffd0ce8d9", size = 410908 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/91/1e5c0d7ce95ca8b022e69e4ca6b23e413fc2d57f0191429c4633e02213d2/azure_ai_agentserver_responses-1.0.0b5-py3-none-any.whl", hash = "sha256:4c2a6ab56e71eeb330aa52b7cb2cc71b8ec6b5bbe0e7dc84310f2c7fbda393a3", size = 268362 }, +] + +[[package]] +name = "azure-ai-projects" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "azure-identity" }, + { name = "azure-storage-blob" }, + { name = "isodate" }, + { name = "openai" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/76/3fdede8eddfe5927a571898a15f0288ba30fee78e5ba099f88df3ded70af/azure_ai_projects-2.1.0.tar.gz", hash = "sha256:f0749fa9a174255aa1a5550fb6078208521518472907a4c6dd552767d9b39caa", size = 543343 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/f6/4984e7772a97c7a9e6505a3de8e55a5070fa2b02cd7e980da91e0d9b9b97/azure_ai_projects-2.1.0-py3-none-any.whl", hash = "sha256:6f259d8eb9167d2dfd372006d0221a8118faeaeb05829fa898b595bc6f19c699", size = 274309 }, +] + +[[package]] +name = "azure-common" +version = "1.1.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/71/f6f71a276e2e69264a97ad39ef850dca0a04fce67b12570730cb38d0ccac/azure-common-1.1.28.zip", hash = "sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3", size = 20914 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/55/7f118b9c1b23ec15ca05d15a578d8207aa1706bc6f7c87218efffbbf875d/azure_common-1.1.28-py2.py3-none-any.whl", hash = "sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad", size = 14462 }, +] + +[[package]] +name = "azure-core" +version = "1.39.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/34/83/bbde3faa84ddcb8eb0eca4b3ffb3221252281db4ce351300fe248c5c70b1/azure_core-1.39.0.tar.gz", hash = "sha256:8a90a562998dd44ce84597590fff6249701b98c0e8797c95fcdd695b54c35d74", size = 367531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d6/8ebcd05b01a580f086ac9a97fb9fac65c09a4b012161cc97c21a336e880b/azure_core-1.39.0-py3-none-any.whl", hash = "sha256:4ac7b70fab5438c3f68770649a78daf97833caa83827f91df9c14e0e0ea7d34f", size = 218318 }, +] + +[[package]] +name = "azure-core-tracing-opentelemetry" +version = "1.0.0b13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/ab/a937e4af8afec9d437d55252f2a3a4419fc3fc7d5e5d54022622bd11b2b6/azure_core_tracing_opentelemetry-1.0.0b13.tar.gz", hash = "sha256:6cb2f8dfd5dee6c11843db0205fc92e2434e1a272c169c953afe92483aafc7eb", size = 25832 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/01/8898c2506cae6a57c1b76d930d2af94764a65354bc863feb2684235851ce/azure_core_tracing_opentelemetry-1.0.0b13-py3-none-any.whl", hash = "sha256:4dacd3a9f117f11f98e89305e161c951b8df85b984f3b56130614de9cd9887f9", size = 12112 }, +] + +[[package]] +name = "azure-cosmos" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/a3/0474e622bf9676e3206d61269461ed16a05958363c254ea3b15af16219b2/azure_cosmos-4.15.0.tar.gz", hash = "sha256:be1cf49837c197d9da880ec47fe020a24d679075b89e0e1e2aca8d376b3a5a24", size = 2100744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/5f/b6e3d3ae16fa121fdc17e62447800d378b7e716cd6103c3650977a6c4618/azure_cosmos-4.15.0-py3-none-any.whl", hash = "sha256:83c1da7386bcd0df9a15c52116cc35012225d8a72d4f1379938b83ea5eb19fff", size = 424870 }, +] + +[[package]] +name = "azure-identity" +version = "1.25.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "msal" }, + { name = "msal-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/0e/3a63efb48aa4a5ae2cfca61ee152fbcb668092134d3eb8bfda472dd5c617/azure_identity-1.25.3.tar.gz", hash = "sha256:ab23c0d63015f50b630ef6c6cf395e7262f439ce06e5d07a64e874c724f8d9e6", size = 286304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/9a/417b3a533e01953a7c618884df2cb05a71e7b68bdbce4fbdb62349d2a2e8/azure_identity-1.25.3-py3-none-any.whl", hash = "sha256:f4d0b956a8146f30333e071374171f3cfa7bdb8073adb8c3814b65567aa7447c", size = 192138 }, +] + +[[package]] +name = "azure-keyvault" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-keyvault-certificates" }, + { name = "azure-keyvault-keys" }, + { name = "azure-keyvault-secrets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/63cbc6b580d82ceb8e5f1fc740d5e85248110fcc3b1df02bc73ee19bd115/azure-keyvault-4.2.0.zip", hash = "sha256:731add108a3e29ab4fd501a3c477256c286c34d0996b383fb6a3945462933761", size = 13376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/a1/38e3c3ed438fbe8b486e2a4b9c1be586eed9206319d995f8b304ea2501f4/azure_keyvault-4.2.0-py2.py3-none-any.whl", hash = "sha256:16b29039244cbe8b940c98a0d795626d76d2a579cb9b8c559983ad208082c0de", size = 4334 }, +] + +[[package]] +name = "azure-keyvault-certificates" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/c5/ff90ae0c09f963df315768b9b057d48a45c2de52cde2280b3c9c8c194a8a/azure_keyvault_certificates-4.11.0.tar.gz", hash = "sha256:fe035696e43b17c414ad08ef6046d6c13e68770d65cc38bdaef8ed8acb6ef878", size = 196940 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/69/9628ecf04cc1afc920064a7256e610bb0f38aa2d38ef02cbd2f7af90755f/azure_keyvault_certificates-4.11.0-py3-none-any.whl", hash = "sha256:4e71d133567068bb5541df0878906952901beb413cc231f3a9175dff2a62c389", size = 161964 }, +] + +[[package]] +name = "azure-keyvault-keys" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/ed/450c9389d76be1a95a056528ec2b832a3721858dd47b1f4eb12dab7060a1/azure_keyvault_keys-4.11.0.tar.gz", hash = "sha256:f257b1917a2c3a88983e3f5675a6419449eb262318888d5b51e1cb3bed79779a", size = 241309 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/ac/fa42e6b316712604a63bf7b3cb60d619d92890e038b87e1b4bba7437bc36/azure_keyvault_keys-4.11.0-py3-none-any.whl", hash = "sha256:fa5febd5805f0fed4c0a1d13c9096081c72a6fa36ccae1299a137f34280eda53", size = 191303 }, +] + +[[package]] +name = "azure-keyvault-secrets" +version = "4.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/b8/03c7b4edd1e3355ad5fffb70e68af70cd09542963f45cf3a2aa9fb3930b5/azure_keyvault_secrets-4.11.0.tar.gz", hash = "sha256:ac14727b9159cca353173ec5a454d8d7b192a6f2f5e7eb540f9fbcf914fa0ca0", size = 112291 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/76/41c513917c0e8dc68de0c33578c6a0668b4a09eb4dc3e2782d382dc2947f/azure_keyvault_secrets-4.11.0-py3-none-any.whl", hash = "sha256:d8543b710423569bd00ada2a2533fe83d52d8e1fe026e1c47f41a3bc0fc73ef5", size = 103148 }, +] + +[[package]] +name = "azure-mgmt-apimanagement" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-common" }, + { name = "azure-mgmt-core" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/e4/3e536f0374bccb203886ef7aeb1148e063de6be31187034179ddcbf1adff/azure_mgmt_apimanagement-5.0.0.tar.gz", hash = "sha256:0ab7fe17e70fe3154cd840ff47d19d7a4610217003eaa7c21acf3511a6e57999", size = 625729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/d8/1406ff959601b1a93fb662b1a76067ecef7094680efcdd75c007457f9efd/azure_mgmt_apimanagement-5.0.0-py3-none-any.whl", hash = "sha256:b88c42a392333b60722fb86f15d092dfc19a8d67510dccd15c217381dff4e6ec", size = 1150262 }, +] + +[[package]] +name = "azure-mgmt-core" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/99/fa9e7551313d8c7099c89ebf3b03cd31beb12e1b498d575aa19bb59a5d04/azure_mgmt_core-1.6.0.tar.gz", hash = "sha256:b26232af857b021e61d813d9f4ae530465255cb10b3dde945ad3743f7a58e79c", size = 30818 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/26/c79f962fd3172b577b6f38685724de58b6b4337a51d3aad316a43a4558c6/azure_mgmt_core-1.6.0-py3-none-any.whl", hash = "sha256:0460d11e85c408b71c727ee1981f74432bc641bb25dfcf1bb4e90a49e776dbc4", size = 29310 }, +] + +[[package]] +name = "azure-monitor-opentelemetry" +version = "1.8.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "azure-core-tracing-opentelemetry" }, + { name = "azure-monitor-opentelemetry-exporter" }, + { name = "opentelemetry-instrumentation-django" }, + { name = "opentelemetry-instrumentation-fastapi" }, + { name = "opentelemetry-instrumentation-flask" }, + { name = "opentelemetry-instrumentation-logging" }, + { name = "opentelemetry-instrumentation-psycopg2" }, + { name = "opentelemetry-instrumentation-requests" }, + { name = "opentelemetry-instrumentation-urllib" }, + { name = "opentelemetry-instrumentation-urllib3" }, + { name = "opentelemetry-resource-detector-azure" }, + { name = "opentelemetry-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/42/ea67bebb400a7561b1ad1dd59d06b67e880daf8081ec0d41d3b0ce8fcc26/azure_monitor_opentelemetry-1.8.7.tar.gz", hash = "sha256:d0a430c69451f8fa09362769d2d65471713989fb78e4ad0f50832b597921efbb", size = 76970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/22/245a4f75a834430759a6fab9c5ab10e18719786ae684cf234c7bb6a693d1/azure_monitor_opentelemetry-1.8.7-py3-none-any.whl", hash = "sha256:0d3a228a183d76cf22698a3eed6e836d1cf57608b8ee879c634609b26f384eb2", size = 41268 }, +] + +[[package]] +name = "azure-monitor-opentelemetry-exporter" +version = "1.0.0b52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "azure-identity" }, + { name = "msrest" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "psutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/7e/bfc03436b88c48f5adc21a3ebbf4392b6b7fbbfe33ef3b1e88d07ba9f380/azure_monitor_opentelemetry_exporter-1.0.0b52.tar.gz", hash = "sha256:7eac679fca32dee9e426df65f2a538161db4514fc322fc66107f7826567d86e1", size = 326179 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/e8/d13e6a74c98ecc3011bce9ab09fc2e75aec48ab46288f72be57c2fa21460/azure_monitor_opentelemetry_exporter-1.0.0b52-py2.py3-none-any.whl", hash = "sha256:a38c503e5e2cc0ec8a4bf336b23cce23488719f5361a45cdd01a514080f0e7fc", size = 244751 }, +] + +[[package]] +name = "azure-storage-blob" +version = "12.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/24/072ba8e27b0e2d8fec401e9969b429d4f5fc4c8d4f0f05f4661e11f7234a/azure_storage_blob-12.28.0.tar.gz", hash = "sha256:e7d98ea108258d29aa0efbfd591b2e2075fa1722a2fae8699f0b3c9de11eff41", size = 604225 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/3a/6ef2047a072e54e1142718d433d50e9514c999a58f51abfff7902f3a72f8/azure_storage_blob-12.28.0-py3-none-any.whl", hash = "sha256:00fb1db28bf6a7b7ecaa48e3b1d5c83bfadacc5a678b77826081304bd87d6461", size = 431499 }, +] + +[[package]] +name = "certifi" +version = "2026.4.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707 }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230 }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043 }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446 }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101 }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948 }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422 }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499 }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928 }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302 }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909 }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402 }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780 }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320 }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487 }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049 }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793 }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300 }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244 }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828 }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926 }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328 }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650 }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687 }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773 }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013 }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593 }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354 }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480 }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584 }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443 }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487 }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726 }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627 }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008 }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303 }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282 }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595 }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986 }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711 }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036 }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998 }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056 }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537 }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176 }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723 }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085 }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819 }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915 }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234 }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042 }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706 }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727 }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882 }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860 }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564 }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276 }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238 }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189 }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352 }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024 }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869 }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541 }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634 }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384 }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133 }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257 }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851 }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393 }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251 }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609 }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014 }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979 }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110 }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824 }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103 }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194 }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827 }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168 }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018 }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294 }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257 }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034 }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672 }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234 }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169 }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859 }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062 }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932 }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024 }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578 }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524 }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730 }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897 }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751 }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486 }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106 }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548 }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297 }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023 }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157 }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570 }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713 }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189 }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251 }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810 }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871 }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264 }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819 }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650 }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833 }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692 }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424 }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300 }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769 }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892 }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748 }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554 }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118 }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555 }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295 }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027 }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428 }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331 }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831 }, +] + +[[package]] +name = "cryptography" +version = "47.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/b2/7ffa7fe8207a8c42147ffe70c3e360b228160c1d85dc3faff16aaa3244c0/cryptography-47.0.0.tar.gz", hash = "sha256:9f8e55fe4e63613a5e1cc5819030f27b97742d720203a087802ce4ce9ceb52bb", size = 830863 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/98/40dfe932134bdcae4f6ab5927c87488754bf9eb79297d7e0070b78dd58e9/cryptography-47.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:160ad728f128972d362e714054f6ba0067cab7fb350c5202a9ae8ae4ce3ef1a0", size = 7912214 }, + { url = "https://files.pythonhosted.org/packages/34/c6/2733531243fba725f58611b918056b277692f1033373dcc8bd01af1c05d4/cryptography-47.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b9a8943e359b7615db1a3ba587994618e094ff3d6fa5a390c73d079ce18b3973", size = 4644617 }, + { url = "https://files.pythonhosted.org/packages/00/e3/b27be1a670a9b87f855d211cf0e1174a5d721216b7616bd52d8581d912ed/cryptography-47.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5c15764f261394b22aef6b00252f5195f46f2ca300bec57149474e2538b31f8", size = 4668186 }, + { url = "https://files.pythonhosted.org/packages/81/b9/8443cfe5d17d482d348cee7048acf502bb89a51b6382f06240fd290d4ca3/cryptography-47.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9c59ab0e0fa3a180a5a9c59f3a5abe3ef90d474bc56d7fadfbe80359491b615b", size = 4651244 }, + { url = "https://files.pythonhosted.org/packages/5d/5e/13ed0cdd0eb88ba159d6dd5ebfece8cb901dbcf1ae5ac4072e28b55d3153/cryptography-47.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:34b4358b925a5ea3e14384ca781a2c0ef7ac219b57bb9eacc4457078e2b19f92", size = 5252906 }, + { url = "https://files.pythonhosted.org/packages/64/16/ed058e1df0f33d440217cd120d41d5dda9dd215a80b8187f68483185af82/cryptography-47.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0024b87d47ae2399165a6bfb20d24888881eeab83ae2566d62467c5ff0030ce7", size = 4701842 }, + { url = "https://files.pythonhosted.org/packages/02/e0/3d30986b30fdbd9e969abbdf8ba00ed0618615144341faeb57f395a084fe/cryptography-47.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:1e47422b5557bb82d3fff997e8d92cff4e28b9789576984f08c248d2b3535d93", size = 4289313 }, + { url = "https://files.pythonhosted.org/packages/df/fd/32db38e3ad0cb331f0691cb4c7a8a6f176f679124dee746b3af6633db4d9/cryptography-47.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:6f29f36582e6151d9686235e586dd35bb67491f024767d10b842e520dc6a07ac", size = 4650964 }, + { url = "https://files.pythonhosted.org/packages/86/53/5395d944dfd48cb1f67917f533c609c34347185ef15eb4308024c876f274/cryptography-47.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a9b761f012a943b7de0e828843c5688d0de94a0578d44d6c85a1bae32f87791f", size = 5207817 }, + { url = "https://files.pythonhosted.org/packages/34/4f/e5711b28e1901f7d480a2b1b688b645aa4c77c73f10731ed17e7f7db3f0d/cryptography-47.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4e1de79e047e25d6e9f8cea71c86b4a53aced64134f0f003bbcbf3655fd172c8", size = 4701544 }, + { url = "https://files.pythonhosted.org/packages/22/22/c8ddc25de3010fc8da447648f5a092c40e7a8fadf01dd6d255d9c0b9373d/cryptography-47.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef6b3634087f18d2155b1e8ce264e5345a753da2c5fa9815e7d41315c90f8318", size = 4783536 }, + { url = "https://files.pythonhosted.org/packages/66/b6/d4a68f4ea999c6d89e8498579cba1c5fcba4276284de7773b17e4fa69293/cryptography-47.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11dbb9f50a0f1bb9757b3d8c27c1101780efb8f0bdecfb12439c22a74d64c001", size = 4926106 }, + { url = "https://files.pythonhosted.org/packages/54/ed/5f524db1fade9c013aa618e1c99c6ed05e8ffc9ceee6cda22fed22dda3f4/cryptography-47.0.0-cp311-abi3-win32.whl", hash = "sha256:7fda2f02c9015db3f42bb8a22324a454516ed10a8c29ca6ece6cdbb5efe2a203", size = 3258581 }, + { url = "https://files.pythonhosted.org/packages/b2/dc/1b901990b174786569029f67542b3edf72ac068b6c3c8683c17e6a2f5363/cryptography-47.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:f5c3296dab66202f1b18a91fa266be93d6aa0c2806ea3d67762c69f60adc71aa", size = 3775309 }, + { url = "https://files.pythonhosted.org/packages/14/88/7aa18ad9c11bc87689affa5ce4368d884b517502d75739d475fc6f4a03c7/cryptography-47.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:be12cb6a204f77ed968bcefe68086eb061695b540a3dd05edac507a3111b25f0", size = 7904299 }, + { url = "https://files.pythonhosted.org/packages/07/55/c18f75724544872f234678fdedc871391722cb34a2aee19faa9f63100bb2/cryptography-47.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2ebd84adf0728c039a3be2700289378e1c164afc6748df1a5ed456767bef9ba7", size = 4631180 }, + { url = "https://files.pythonhosted.org/packages/ee/65/31a5cc0eaca99cec5bafffe155d407115d96136bb161e8b49e0ef73f09a7/cryptography-47.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f68d6fbc7fbbcfb0939fea72c3b96a9f9a6edfc0e1b1d29778a2066030418b1", size = 4653529 }, + { url = "https://files.pythonhosted.org/packages/e5/bc/641c0519a495f3bfd0421b48d7cd325c4336578523ccd76ea322b6c29c7a/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:6651d32eff255423503aa276739da98c30f26c40cbeffcc6048e0d54ef704c0c", size = 4638570 }, + { url = "https://files.pythonhosted.org/packages/2b/f2/300327b0a47f6dc94dd8b71b57052aefe178bb51745073d73d80604f11ab/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3fb8fa48075fad7193f2e5496135c6a76ac4b2aa5a38433df0a539296b377829", size = 5238019 }, + { url = "https://files.pythonhosted.org/packages/e9/5a/5b5cf994391d4bf9d9c7efd4c66aabe4d95227256627f8fea6cff7dfadbd/cryptography-47.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11438c7518132d95f354fa01a4aa2f806d172a061a7bed18cf18cbdacdb204d7", size = 4686832 }, + { url = "https://files.pythonhosted.org/packages/dc/2c/ae950e28fd6475c852fc21a44db3e6b5bcc1261d1e370f2b6e42fa800fef/cryptography-47.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8c1a736bbb3288005796c3f7ccb9453360d7fed483b13b9f468aea5171432923", size = 4269301 }, + { url = "https://files.pythonhosted.org/packages/67/fb/6a39782e150ffe5cc1b0018cb6ddc48bf7ca62b498d7539ffc8a758e977d/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:f1557695e5c2b86e204f6ce9470497848634100787935ab7adc5397c54abd7ab", size = 4638110 }, + { url = "https://files.pythonhosted.org/packages/8e/d7/0b3c71090a76e5c203164a47688b697635ece006dcd2499ab3a4dbd3f0bd/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:f9a034b642b960767fb343766ae5ba6ad653f2e890ddd82955aef288ffea8736", size = 5194988 }, + { url = "https://files.pythonhosted.org/packages/63/33/63a961498a9df51721ab578c5a2622661411fc520e00bd83b0cc64eb20c4/cryptography-47.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b1c76fca783aa7698eb21eb14f9c4aa09452248ee54a627d125025a43f83e7a7", size = 4686563 }, + { url = "https://files.pythonhosted.org/packages/b7/bf/5ee5b145248f92250de86145d1c1d6edebbd57a7fe7caa4dedb5d4cf06a1/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4f7722c97826770bab8ae92959a2e7b20a5e9e9bf4deae68fd86c3ca457bab52", size = 4770094 }, + { url = "https://files.pythonhosted.org/packages/92/43/21d220b2da5d517773894dacdcdb5c682c28d3fffce65548cb06e87d5501/cryptography-47.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:09f6d7bf6724f8db8b32f11eccf23efc8e759924bc5603800335cf8859a3ddbd", size = 4913811 }, + { url = "https://files.pythonhosted.org/packages/31/98/dc4ad376ac5f1a1a7d4a83f7b0c6f2bcad36b5d2d8f30aeb482d3a7d9582/cryptography-47.0.0-cp314-cp314t-win32.whl", hash = "sha256:6eebcaf0df1d21ce1f90605c9b432dd2c4f4ab665ac29a40d5e3fc68f51b5e63", size = 3237158 }, + { url = "https://files.pythonhosted.org/packages/bc/da/97f62d18306b5133468bc3f8cc73a3111e8cdc8cf8d3e69474d6e5fd2d1b/cryptography-47.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:51c9313e90bd1690ec5a75ed047c27c0b8e6c570029712943d6116ef9a90620b", size = 3758706 }, + { url = "https://files.pythonhosted.org/packages/e0/34/a4fae8ae7c3bc227460c9ae43f56abf1b911da0ec29e0ebac53bb0a4b6b7/cryptography-47.0.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:14432c8a9bcb37009784f9594a62fae211a2ae9543e96c92b2a8e4c3cd5cd0c4", size = 7904072 }, + { url = "https://files.pythonhosted.org/packages/01/64/d7b1e54fdb69f22d24a64bb3e88dc718b31c7fb10ef0b9691a3cf7eeea6e/cryptography-47.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:07efe86201817e7d3c18781ca9770bc0db04e1e48c994be384e4602bc38f8f27", size = 4635767 }, + { url = "https://files.pythonhosted.org/packages/8b/7b/cca826391fb2a94efdcdfe4631eb69306ee1cff0b22f664a412c90713877/cryptography-47.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b45761c6ec22b7c726d6a829558777e32d0f1c8be7c3f3480f9c912d5ee8a10", size = 4654350 }, + { url = "https://files.pythonhosted.org/packages/4c/65/4b57bcc823f42a991627c51c2f68c9fd6eb1393c1756aac876cba2accae2/cryptography-47.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:edd4da498015da5b9f26d38d3bfc2e90257bfa9cbed1f6767c282a0025ae649b", size = 4643394 }, + { url = "https://files.pythonhosted.org/packages/f4/c4/2c5fbeea70adbbca2bbae865e1d605d6a4a7f8dbd9d33eaf69645087f06c/cryptography-47.0.0-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9af828c0d5a65c70ec729cd7495a4bf1a67ecb66417b8f02ff125ab8a6326a74", size = 5225777 }, + { url = "https://files.pythonhosted.org/packages/7e/b8/ac57107ef32749d2b244e36069bb688792a363aaaa3acc9e3cf84c130315/cryptography-47.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:256d07c78a04d6b276f5df935a9923275f53bd1522f214447fdf365494e2d515", size = 4688771 }, + { url = "https://files.pythonhosted.org/packages/56/fc/9f1de22ff8be99d991f240a46863c52d475404c408886c5a38d2b5c3bb26/cryptography-47.0.0-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:5d0e362ff51041b0c0d219cc7d6924d7b8996f57ce5712bdcef71eb3c65a59cc", size = 4270753 }, + { url = "https://files.pythonhosted.org/packages/00/68/d70c852797aa68e8e48d12e5a87170c43f67bb4a59403627259dd57d15de/cryptography-47.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1581aef4219f7ca2849d0250edaa3866212fb74bf5667284f46aa92f9e65c1ca", size = 4642911 }, + { url = "https://files.pythonhosted.org/packages/a5/51/661cbee74f594c5d97ff82d34f10d5551c085ca4668645f4606ebd22bd5d/cryptography-47.0.0-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a49a3eb5341b9503fa3000a9a0db033161db90d47285291f53c2a9d2cd1b7f76", size = 5181411 }, + { url = "https://files.pythonhosted.org/packages/94/87/f2b6c374a82cf076cfa1416992ac8e8ec94d79facc37aec87c1a5cb72352/cryptography-47.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2207a498b03275d0051589e326b79d4cf59985c99031b05bb292ac52631c37fe", size = 4688262 }, + { url = "https://files.pythonhosted.org/packages/14/e2/8b7462f4acf21ec509616f0245018bb197194ab0b65c2ea21a0bdd53c0eb/cryptography-47.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a02675e2fabd0c0fc04c868b8781863cbf1967691543c22f5470500ff840b31", size = 4775506 }, + { url = "https://files.pythonhosted.org/packages/70/75/158e494e4c08dc05e039da5bb48553826bd26c23930cf8d3cd5f21fa8921/cryptography-47.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80887c5cbd1774683cb126f0ab4184567f080071d5acf62205acb354b4b753b7", size = 4912060 }, + { url = "https://files.pythonhosted.org/packages/06/bd/0a9d3edbf5eadbac926d7b9b3cd0c4be584eeeae4a003d24d9eda4affbbd/cryptography-47.0.0-cp38-abi3-win32.whl", hash = "sha256:ed67ea4e0cfb5faa5bc7ecb6e2b8838f3807a03758eec239d6c21c8769355310", size = 3248487 }, + { url = "https://files.pythonhosted.org/packages/60/80/5681af756d0da3a599b7bdb586fac5a1540f1bcefd2717a20e611ddade45/cryptography-47.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:835d2d7f47cdc53b3224e90810fb1d36ca94ea29cc1801fb4c1bc43876735769", size = 3755737 }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 }, +] + +[[package]] +name = "debugpy" +version = "1.8.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560 }, + { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272 }, + { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208 }, + { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930 }, + { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066 }, + { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425 }, + { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407 }, + { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521 }, + { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658 }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317 }, +] + +[[package]] +name = "fonttools" +version = "4.62.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155 }, + { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802 }, + { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926 }, + { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575 }, + { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693 }, + { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920 }, + { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928 }, + { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514 }, + { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442 }, + { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901 }, + { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608 }, + { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726 }, + { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422 }, + { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979 }, + { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733 }, + { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663 }, + { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288 }, + { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023 }, + { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599 }, + { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933 }, + { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232 }, + { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987 }, + { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021 }, + { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147 }, + { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647 }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717 }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651 }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417 }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391 }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048 }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549 }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833 }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363 }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314 }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365 }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763 }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110 }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717 }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628 }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882 }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676 }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235 }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742 }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725 }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506 }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161 }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676 }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638 }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067 }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101 }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901 }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395 }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659 }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492 }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034 }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749 }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127 }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698 }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749 }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298 }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015 }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038 }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130 }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845 }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131 }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542 }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308 }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210 }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972 }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536 }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330 }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627 }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238 }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738 }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739 }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186 }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196 }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830 }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289 }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318 }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814 }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762 }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470 }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042 }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148 }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451 }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507 }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/c8/f439cffde755cffa462bfbb156278fa6f9d09119719af9814b858fd4f81f/googleapis_common_protos-1.75.0.tar.gz", hash = "sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd", size = 151035 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631 }, +] + +[[package]] +name = "grpcio" +version = "1.80.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/48/af6173dbca4454f4637a4678b67f52ca7e0c1ed7d5894d89d434fecede05/grpcio-1.80.0.tar.gz", hash = "sha256:29aca15edd0688c22ba01d7cc01cb000d72b2033f4a3c72a81a19b56fd143257", size = 12978905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/3a/7c3c25789e3f069e581dc342e03613c5b1cb012c4e8c7d9d5cf960a75856/grpcio-1.80.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:e9e408fc016dffd20661f0126c53d8a31c2821b5c13c5d67a0f5ed5de93319ad", size = 6017243 }, + { url = "https://files.pythonhosted.org/packages/04/19/21a9806eb8240e174fd1ab0cd5b9aa948bb0e05c2f2f55f9d5d7405e6d08/grpcio-1.80.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:92d787312e613754d4d8b9ca6d3297e69994a7912a32fa38c4c4e01c272974b0", size = 12010840 }, + { url = "https://files.pythonhosted.org/packages/18/3a/23347d35f76f639e807fb7a36fad3068aed100996849a33809591f26eca6/grpcio-1.80.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac393b58aa16991a2f1144ec578084d544038c12242da3a215966b512904d0f", size = 6567644 }, + { url = "https://files.pythonhosted.org/packages/ff/40/96e07ecb604a6a67ae6ab151e3e35b132875d98bc68ec65f3e5ab3e781d7/grpcio-1.80.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:68e5851ac4b9afe07e7f84483803ad167852570d65326b34d54ca560bfa53fb6", size = 7277830 }, + { url = "https://files.pythonhosted.org/packages/9b/e2/da1506ecea1f34a5e365964644b35edef53803052b763ca214ba3870c856/grpcio-1.80.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:873ff5d17d68992ef6605330127425d2fc4e77e612fa3c3e0ed4e668685e3140", size = 6783216 }, + { url = "https://files.pythonhosted.org/packages/44/83/3b20ff58d0c3b7f6caaa3af9a4174d4023701df40a3f39f7f1c8e7c48f9d/grpcio-1.80.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2bea16af2750fd0a899bf1abd9022244418b55d1f37da2202249ba4ba673838d", size = 7385866 }, + { url = "https://files.pythonhosted.org/packages/47/45/55c507599c5520416de5eefecc927d6a0d7af55e91cfffb2e410607e5744/grpcio-1.80.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba0db34f7e1d803a878284cd70e4c63cb6ae2510ba51937bf8f45ba997cefcf7", size = 8391602 }, + { url = "https://files.pythonhosted.org/packages/10/bb/dd06f4c24c01db9cf11341b547d0a016b2c90ed7dbbb086a5710df7dd1d7/grpcio-1.80.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8eb613f02d34721f1acf3626dfdb3545bd3c8505b0e52bf8b5710a28d02e8aa7", size = 7826752 }, + { url = "https://files.pythonhosted.org/packages/f9/1e/9d67992ba23371fd63d4527096eb8c6b76d74d52b500df992a3343fd7251/grpcio-1.80.0-cp313-cp313-win32.whl", hash = "sha256:93b6f823810720912fd131f561f91f5fed0fda372b6b7028a2681b8194d5d294", size = 4142310 }, + { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833 }, + { url = "https://files.pythonhosted.org/packages/c5/6d/e65307ce20f5a09244ba9e9d8476e99fb039de7154f37fb85f26978b59c3/grpcio-1.80.0-cp314-cp314-linux_armv7l.whl", hash = "sha256:3d4147a97c8344d065d01bbf8b6acec2cf86fb0400d40696c8bdad34a64ffc0e", size = 6017376 }, + { url = "https://files.pythonhosted.org/packages/69/10/9cef5d9650c72625a699c549940f0abb3c4bfdb5ed45a5ce431f92f31806/grpcio-1.80.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8e11f167935b3eb089ac9038e1a063e6d7dbe995c0bb4a661e614583352e76f", size = 12018133 }, + { url = "https://files.pythonhosted.org/packages/04/82/983aabaad82ba26113caceeb9091706a0696b25da004fe3defb5b346e15b/grpcio-1.80.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f14b618fc30de822681ee986cfdcc2d9327229dc4c98aed16896761cacd468b9", size = 6574748 }, + { url = "https://files.pythonhosted.org/packages/07/d7/031666ef155aa0bf399ed7e19439656c38bbd143779ae0861b038ce82abd/grpcio-1.80.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4ed39fbdcf9b87370f6e8df4e39ca7b38b3e5e9d1b0013c7b6be9639d6578d14", size = 7277711 }, + { url = "https://files.pythonhosted.org/packages/e8/43/f437a78f7f4f1d311804189e8f11fb311a01049b2e08557c1068d470cb2e/grpcio-1.80.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2dcc70e9f0ba987526e8e8603a610fb4f460e42899e74e7a518bf3c68fe1bf05", size = 6785372 }, + { url = "https://files.pythonhosted.org/packages/93/3d/f6558e9c6296cb4227faa5c43c54a34c68d32654b829f53288313d16a86e/grpcio-1.80.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448c884b668b868562b1bda833c5fce6272d26e1926ec46747cda05741d302c1", size = 7395268 }, + { url = "https://files.pythonhosted.org/packages/06/21/0fdd77e84720b08843c371a2efa6f2e19dbebf56adc72df73d891f5506f0/grpcio-1.80.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a1dc80fe55685b4a543555e6eef975303b36c8db1023b1599b094b92aa77965f", size = 8392000 }, + { url = "https://files.pythonhosted.org/packages/f5/68/67f4947ed55d2e69f2cc199ab9fd85e0a0034d813bbeef84df6d2ba4d4b7/grpcio-1.80.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:31b9ac4ad1aa28ffee5503821fafd09e4da0a261ce1c1281c6c8da0423c83b6e", size = 7828477 }, + { url = "https://files.pythonhosted.org/packages/44/b6/8d4096691b2e385e8271911a0de4f35f0a6c7d05aff7098e296c3de86939/grpcio-1.80.0-cp314-cp314-win32.whl", hash = "sha256:367ce30ba67d05e0592470428f0ec1c31714cab9ef19b8f2e37be1f4c7d32fae", size = 4218563 }, + { url = "https://files.pythonhosted.org/packages/e5/8c/bbe6baf2557262834f2070cf668515fa308b2d38a4bbf771f8f7872a7036/grpcio-1.80.0-cp314-cp314-win_amd64.whl", hash = "sha256:3b01e1f5464c583d2f567b2e46ff0d516ef979978f72091fd81f5ab7fa6e2e7f", size = 5019457 }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "h2" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779 }, +] + +[[package]] +name = "hpack" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "hypercorn" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "h2" }, + { name = "priority" }, + { name = "wsproto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/01/39f41a014b83dd5c795217362f2ca9071cf243e6a75bdcd6cd5b944658cc/hypercorn-0.18.0.tar.gz", hash = "sha256:d63267548939c46b0247dc8e5b45a9947590e35e64ee73a23c074aa3cf88e9da", size = 68420 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/35/850277d1b17b206bd10874c8a9a3f52e059452fb49bb0d22cbb908f6038b/hypercorn-0.18.0-py3-none-any.whl", hash = "sha256:225e268f2c1c2f28f6d8f6db8f40cb8c992963610c5725e13ccfcddccb24b1cd", size = 61640 }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007 }, +] + +[[package]] +name = "idna" +version = "3.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865 }, +] + +[[package]] +name = "ipykernel" +version = "7.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788 }, +] + +[[package]] +name = "ipython" +version = "9.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "psutil" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274 }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074 }, +] + +[[package]] +name = "isodate" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jiter" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/c1/0cddc6eb17d4c53a99840953f95dd3accdc5cfc7a337b0e9b26476276be9/jiter-0.14.0.tar.gz", hash = "sha256:e8a39e66dac7153cf3f964a12aad515afa8d74938ec5cc0018adcdae5367c79e", size = 165725 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/2a/09f70020898507a89279659a1afe3364d57fc1b2c89949081975d135f6f5/jiter-0.14.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:af72f204cf4d44258e5b4c1745130ac45ddab0e71a06333b01de660ab4187a94", size = 315502 }, + { url = "https://files.pythonhosted.org/packages/d6/be/080c96a45cd74f9fce5db4fd68510b88087fb37ffe2541ff73c12db92535/jiter-0.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4b77da71f6e819be5fbcec11a453fde5b1d0267ef6ed487e2a392fd8e14e4e3a", size = 314870 }, + { url = "https://files.pythonhosted.org/packages/7d/5e/2d0fee155826a968a832cc32438de5e2a193292c8721ca70d0b53e58245b/jiter-0.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f4ea612fe8b84b8b04e51d0e78029ecf3466348e25973f953de6e6a59aa4c1", size = 343406 }, + { url = "https://files.pythonhosted.org/packages/70/af/bf9ee0d3a4f8dc0d679fc1337f874fe60cdbf841ebbb304b374e1c9aaceb/jiter-0.14.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62fe2451f8fcc0240261e6a4df18ecbcd58327857e61e625b2393ea3b468aac9", size = 369415 }, + { url = "https://files.pythonhosted.org/packages/0f/83/8e8561eadba31f4d3948a5b712fb0447ec71c3560b57a855449e7b8ddc98/jiter-0.14.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6112f26f5afc75bcb475787d29da3aa92f9d09c7858f632f4be6ffe607be82e9", size = 461456 }, + { url = "https://files.pythonhosted.org/packages/f6/c9/c5299e826a5fe6108d172b344033f61c69b1bb979dd8d9ddd4278a160971/jiter-0.14.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:215a6cb8fb7dc702aa35d475cc00ddc7f970e5c0b1417fb4b4ac5d82fa2a29db", size = 378488 }, + { url = "https://files.pythonhosted.org/packages/5d/37/c16d9d15c0a471b8644b1abe3c82668092a707d9bedcf076f24ff2e380cd/jiter-0.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ab96a30fb3cb2c7e0cd33f7616c8860da5f5674438988a54ac717caccdbaa", size = 353242 }, + { url = "https://files.pythonhosted.org/packages/58/ea/8050cb0dc654e728e1bfacbc0c640772f2181af5dedd13ae70145743a439/jiter-0.14.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:3a99c1387b1f2928f799a9de899193484d66206a50e98233b6b088a7f0c1edb2", size = 356823 }, + { url = "https://files.pythonhosted.org/packages/b0/3b/cf71506d270e5f84d97326bf220e47aed9b95e9a4a060758fb07772170ab/jiter-0.14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ab18d11074485438695f8d34a1b6da61db9754248f96d51341956607a8f39985", size = 392564 }, + { url = "https://files.pythonhosted.org/packages/b0/cc/8c6c74a3efb5bd671bfd14f51e8a73375464ca914b1551bc3b40e26ac2c9/jiter-0.14.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:801028dcfc26ac0895e4964cbc0fd62c73be9fd4a7d7b1aaf6e5790033a719b7", size = 520322 }, + { url = "https://files.pythonhosted.org/packages/41/24/68d7b883ec959884ddf00d019b2e0e82ba81b167e1253684fa90519ce33c/jiter-0.14.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ad425b087aafb4a1c7e1e98a279200743b9aaf30c3e0ba723aec93f061bd9bc8", size = 552619 }, + { url = "https://files.pythonhosted.org/packages/b6/89/b1a0985223bbf3150ff9e8f46f98fc9360c1de94f48abe271bbe1b465682/jiter-0.14.0-cp313-cp313-win32.whl", hash = "sha256:882bcb9b334318e233950b8be366fe5f92c86b66a7e449e76975dfd6d776a01f", size = 205699 }, + { url = "https://files.pythonhosted.org/packages/4c/19/3f339a5a7f14a11730e67f6be34f9d5105751d547b615ef593fa122a5ded/jiter-0.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:9b8c571a5dba09b98bd3462b5a53f27209a5cbbe85670391692ede71974e979f", size = 201323 }, + { url = "https://files.pythonhosted.org/packages/50/56/752dd89c84be0e022a8ea3720bcfa0a8431db79a962578544812ce061739/jiter-0.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:34f19dcc35cb1abe7c369b3756babf8c7f04595c0807a848df8f26ef8298ef92", size = 191099 }, + { url = "https://files.pythonhosted.org/packages/91/28/292916f354f25a1fe8cf2c918d1415c699a4a659ae00be0430e1c5d9ffea/jiter-0.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e89bcd7d426a75bb4952c696b267075790d854a07aad4c9894551a82c5b574ab", size = 320880 }, + { url = "https://files.pythonhosted.org/packages/ad/c7/b002a7d8b8957ac3d469bd59c18ef4b1595a5216ae0de639a287b9816023/jiter-0.14.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b25beaa0d4447ea8c7ae0c18c688905d34840d7d0b937f2f7bdd52162c98a40", size = 346563 }, + { url = "https://files.pythonhosted.org/packages/f9/3b/f8d07580d8706021d255a6356b8fab13ee4c869412995550ce6ed4ddf97d/jiter-0.14.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:651a8758dd413c51e3b7f6557cdc6921faf70b14106f45f969f091f5cda990ea", size = 357928 }, + { url = "https://files.pythonhosted.org/packages/47/5b/ac1a974da29e35507230383110ffec59998b290a8732585d04e19a9eb5ba/jiter-0.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e1a7eead856a5038a8d291f1447176ab0b525c77a279a058121b5fccee257f6f", size = 203519 }, + { url = "https://files.pythonhosted.org/packages/96/6d/9fc8433d667d2454271378a79747d8c76c10b51b482b454e6190e511f244/jiter-0.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e692633a12cda97e352fdcd1c4acc971b1c28707e1e33aeef782b0cbf051975", size = 190113 }, + { url = "https://files.pythonhosted.org/packages/4f/1e/354ed92461b165bd581f9ef5150971a572c873ec3b68a916d5aa91da3cc2/jiter-0.14.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:6f396837fc7577871ca8c12edaf239ed9ccef3bbe39904ae9b8b63ce0a48b140", size = 315277 }, + { url = "https://files.pythonhosted.org/packages/a6/95/8c7c7028aa8636ac21b7a55faef3e34215e6ed0cbf5ae58258427f621aa3/jiter-0.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a4d50ea3d8ba4176f79754333bd35f1bbcd28e91adc13eb9b7ca91bc52a6cef9", size = 315923 }, + { url = "https://files.pythonhosted.org/packages/47/40/e2a852a44c4a089f2681a16611b7ce113224a80fd8504c46d78491b47220/jiter-0.14.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce17f8a050447d1b4153bda4fb7d26e6a9e74eb4f4a41913f30934c5075bf615", size = 344943 }, + { url = "https://files.pythonhosted.org/packages/fc/1f/670f92adee1e9895eac41e8a4d623b6da68c4d46249d8b556b60b63f949e/jiter-0.14.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4f1c4b125e1652aefbc2e2c1617b60a160ab789d180e3d423c41439e5f32850", size = 369725 }, + { url = "https://files.pythonhosted.org/packages/01/2f/541c9ba567d05de1c4874a0f8f8c5e3fd78e2b874266623da9a775cf46e0/jiter-0.14.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be808176a6a3a14321d18c603f2d40741858a7c4fc982f83232842689fe86dd9", size = 461210 }, + { url = "https://files.pythonhosted.org/packages/ce/a9/c31cbec09627e0d5de7aeaec7690dba03e090caa808fefd8133137cf45bc/jiter-0.14.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26679d58ba816f88c3849306dd58cb863a90a1cf352cdd4ef67e30ccf8a77994", size = 380002 }, + { url = "https://files.pythonhosted.org/packages/50/02/3c05c1666c41904a2f607475a73e7a4763d1cbde2d18229c4f85b22dc253/jiter-0.14.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80381f5a19af8fa9aef743f080e34f6b25ebd89656475f8cf0470ec6157052aa", size = 354678 }, + { url = "https://files.pythonhosted.org/packages/7d/97/e15b33545c2b13518f560d695f974b9891b311641bdcf178d63177e8801e/jiter-0.14.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:004df5fdb8ecbd6d99f3227df18ba1a259254c4359736a2e6f036c944e02d7c5", size = 358920 }, + { url = "https://files.pythonhosted.org/packages/ad/d2/8b1461def6b96ba44530df20d07ef7a1c7da22f3f9bf1727e2d611077bf1/jiter-0.14.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cff5708f7ed0fa098f2b53446c6fa74c48469118e5cd7497b4f1cd569ab06928", size = 394512 }, + { url = "https://files.pythonhosted.org/packages/e3/88/837566dd6ed6e452e8d3205355afd484ce44b2533edfa4ed73a298ea893e/jiter-0.14.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:2492e5f06c36a976d25c7cc347a60e26d5470178d44cde1b9b75e60b4e519f28", size = 521120 }, + { url = "https://files.pythonhosted.org/packages/89/6b/b00b45c4d1b4c031777fe161d620b755b5b02cdade1e316dcb46e4471d63/jiter-0.14.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:7609cfbe3a03d37bfdbf5052012d5a879e72b83168a363deae7b3a26564d57de", size = 553668 }, + { url = "https://files.pythonhosted.org/packages/ad/d8/6fe5b42011d19397433d345716eac16728ac241862a2aac9c91923c7509a/jiter-0.14.0-cp314-cp314-win32.whl", hash = "sha256:7282342d32e357543565286b6450378c3cd402eea333fc1ebe146f1fabb306fc", size = 207001 }, + { url = "https://files.pythonhosted.org/packages/e5/43/5c2e08da1efad5e410f0eaaabeadd954812612c33fbbd8fd5328b489139d/jiter-0.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:bd77945f38866a448e73b0b7637366afa814d4617790ecd88a18ca74377e6c02", size = 202187 }, + { url = "https://files.pythonhosted.org/packages/aa/1f/6e39ac0b4cdfa23e606af5b245df5f9adaa76f35e0c5096790da430ca506/jiter-0.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:f2d4c61da0821ee42e0cdf5489da60a6d074306313a377c2b35af464955a3611", size = 192257 }, + { url = "https://files.pythonhosted.org/packages/05/57/7dbc0ffbbb5176a27e3518716608aa464aee2e2887dc938f0b900a120449/jiter-0.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1bf7ff85517dd2f20a5750081d2b75083c1b269cf75afc7511bdf1f9548beb3b", size = 323441 }, + { url = "https://files.pythonhosted.org/packages/83/6e/7b3314398d8983f06b557aa21b670511ec72d3b79a68ee5e4d9bff972286/jiter-0.14.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8ef8791c3e78d6c6b157c6d360fbb5c715bebb8113bc6a9303c5caff012754a", size = 348109 }, + { url = "https://files.pythonhosted.org/packages/ae/4f/8dc674bcd7db6dba566de73c08c763c337058baff1dbeb34567045b27cdc/jiter-0.14.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e74663b8b10da1fe0f4e4703fd7980d24ad17174b6bb35d8498d6e3ebce2ae6a", size = 368328 }, + { url = "https://files.pythonhosted.org/packages/3b/5f/188e09a1f20906f98bbdec44ed820e19f4e8eb8aff88b9d1a5a497587ff3/jiter-0.14.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1aca29ba52913f78362ec9c2da62f22cdc4c3083313403f90c15460979b84d9b", size = 463301 }, + { url = "https://files.pythonhosted.org/packages/ac/f0/19046ef965ed8f349e8554775bb12ff4352f443fbe12b95d31f575891256/jiter-0.14.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b39b7d87a952b79949af5fef44d2544e58c21a28da7f1bae3ef166455c61746", size = 378891 }, + { url = "https://files.pythonhosted.org/packages/c4/c3/da43bd8431ee175695777ee78cf0e93eacbb47393ff493f18c45231b427d/jiter-0.14.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d918a68b26e9fab068c2b5453577ef04943ab2807b9a6275df2a812599a310", size = 360749 }, + { url = "https://files.pythonhosted.org/packages/72/26/e054771be889707c6161dbdec9c23d33a9ec70945395d70f07cfea1e9a6f/jiter-0.14.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:b08997c35aee1201c1a5361466a8fb9162d03ae7bf6568df70b6c859f1e654a4", size = 358526 }, + { url = "https://files.pythonhosted.org/packages/c3/0f/7bea65ea2a6d91f2bf989ff11a18136644392bf2b0497a1fa50934c30a9c/jiter-0.14.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:260bf7ca20704d58d41f669e5e9fe7fe2fa72901a6b324e79056f5d52e9c9be2", size = 393926 }, + { url = "https://files.pythonhosted.org/packages/3c/a1/b1ff7d70deef61ac0b7c6c2f12d2ace950cdeecb4fdc94500a0926802857/jiter-0.14.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:37826e3df29e60f30a382f9294348d0238ef127f4b5d7f5f8da78b5b9e050560", size = 521052 }, + { url = "https://files.pythonhosted.org/packages/0b/7b/3b0649983cbaf15eda26a414b5b1982e910c67bd6f7b1b490f3cfc76896a/jiter-0.14.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:645be49c46f2900937ba0eaf871ad5183c96858c0af74b6becc7f4e367e36e06", size = 553716 }, + { url = "https://files.pythonhosted.org/packages/97/f8/33d78c83bd93ae0c0af05293a6660f88a1977caef39a6d72a84afab94ce0/jiter-0.14.0-cp314-cp314t-win32.whl", hash = "sha256:2f7877ed45118de283786178eceaf877110abacd04fde31efff3940ae9672674", size = 207957 }, + { url = "https://files.pythonhosted.org/packages/d6/ac/2b760516c03e2227826d1f7025d89bf6bf6357a28fe75c2a2800873c50bf/jiter-0.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:14c0cb10337c49f5eafe8e7364daca5e29a020ea03580b8f8e6c597fed4e1588", size = 204690 }, + { url = "https://files.pythonhosted.org/packages/dc/2e/a44c20c58aeed0355f2d326969a181696aeb551a25195f47563908a815be/jiter-0.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:5419d4aa2024961da9fe12a9cfe7484996735dca99e8e090b5c88595ef1951ff", size = 191338 }, + { url = "https://files.pythonhosted.org/packages/32/a1/ef34ca2cab2962598591636a1804b93645821201cc0095d4a93a9a329c9d/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a25ffa2dbbdf8721855612f6dca15c108224b12d0c4024d0ac3d7902132b4211", size = 311366 }, + { url = "https://files.pythonhosted.org/packages/60/bb/520576a532a6b8a6f42747afed289c8448c879a34d7802fe2c832d4fd38f/jiter-0.14.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ac9cbaa86c10996b92bd12c91659b60f939f8e28fcfa6bc11a0e90a774ce95b", size = 309873 }, + { url = "https://files.pythonhosted.org/packages/b2/7c/c16db114ea1f2f532f198aa8dc39585026af45af362c69a0492f31bc4821/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:844e73b6c56b505e9e169234ea3bdea2ea43f769f847f47ac559ba1d2361ebea", size = 344816 }, + { url = "https://files.pythonhosted.org/packages/99/8f/15e7741ff19e9bcd4d753f7ff22f988fd54592f134ca13701c13ea8c20e0/jiter-0.14.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e52c076f187405fc21523c746c04399c9af8ece566077ed147b2126f2bcba577", size = 351445 }, + { url = "https://files.pythonhosted.org/packages/21/42/9042c3f3019de4adcb8c16591c325ec7255beea9fcd33a42a43f3b0b1000/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:fbd9e482663ca9d005d051330e4d2d8150bb208a209409c10f7e7dfdf7c49da9", size = 308810 }, + { url = "https://files.pythonhosted.org/packages/60/cf/a7e19b308bd86bb04776803b1f01a5f9a287a4c55205f4708827ee487fbf/jiter-0.14.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:33a20d838b91ef376b3a56896d5b04e725c7df5bc4864cc6569cf046a8d73b6d", size = 308443 }, + { url = "https://files.pythonhosted.org/packages/ca/44/e26ede3f0caeff93f222559cb0cc4ca68579f07d009d7b6010c5b586f9b1/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:432c4db5255d86a259efde91e55cb4c8d18c0521d844c9e2e7efcce3899fb016", size = 343039 }, + { url = "https://files.pythonhosted.org/packages/da/e9/1f9ada30cef7b05e74bb06f52127e7a724976c225f46adb65c37b1dadfb6/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f00d94b281174144d6532a04b66a12cb866cbdc47c3af3bfe2973677f9861a", size = 349613 }, +] + +[[package]] +name = "jsonpatch" +version = "1.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpointer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898 }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659 }, +] + +[[package]] +name = "jupyter-client" +version = "8.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371 }, +] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032 }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166 }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395 }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065 }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903 }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751 }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793 }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041 }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292 }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865 }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369 }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989 }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645 }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237 }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573 }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998 }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700 }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537 }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514 }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848 }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542 }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447 }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918 }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856 }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580 }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018 }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804 }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482 }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328 }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410 }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231 }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489 }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063 }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913 }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782 }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815 }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925 }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322 }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857 }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376 }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680 }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905 }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086 }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577 }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794 }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646 }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511 }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858 }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539 }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310 }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244 }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154 }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377 }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288 }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158 }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260 }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403 }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687 }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032 }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262 }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036 }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295 }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987 }, +] + +[[package]] +name = "langchain" +version = "1.2.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "langgraph" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/3f/888a7099d2bd2917f8b0c3ffc7e347f1e664cf64267820b0b923c4f339fc/langchain-1.2.15.tar.gz", hash = "sha256:1717b6719daefae90b2728314a5e2a117ff916291e2862595b6c3d6fba33d652", size = 574732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/e8/a3b8cb0005553f6a876865073c81ef93bd7c5b18381bcb9ba4013af96ebc/langchain-1.2.15-py3-none-any.whl", hash = "sha256:e349db349cb3e9550c4044077cf90a1717691756cc236438404b23500e615874", size = 112714 }, +] + +[[package]] +name = "langchain-core" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonpatch" }, + { name = "langchain-protocol" }, + { name = "langsmith" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "uuid-utils" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/03/7219502e8ca728d65eb44d7a3eb60239230742a70dbfc9241b9bfd61c4ab/langchain_core-1.3.2.tar.gz", hash = "sha256:fd7a50b2f28ba561fd9d7f5d2760bc9e06cf00cdf820a3ccafe88a94ffa8d5b7", size = 911813 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/d5/8fa4431007cbb7cfed7590f4d6a5dea3ad724f4174d248f6642ef5ce7d05/langchain_core-1.3.2-py3-none-any.whl", hash = "sha256:d44a66127f9f8db735bdfd0ab9661bccb47a97113cfd3f2d89c74864422b7274", size = 542390 }, +] + +[[package]] +name = "langchain-openai" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "openai" }, + { name = "tiktoken" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/0e/d8e16c28aa67106d285e63b8ffc04c5af68341e345ce24a0751dbf2e167e/langchain_openai-1.2.1.tar.gz", hash = "sha256:ee4480b787706361b7125fad46930589a624df87aa158c6986ef1fad10d10675", size = 1146092 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/55/2865b18ee3a3dd11160b8c4b2cf37e75bf2a4a8d1d38868ffffc7b7cc180/langchain_openai-1.2.1-py3-none-any.whl", hash = "sha256:a80732185030d4f453dda6c25feef46f645f665423fdffe38ae3edf1ac3c6c4d", size = 98626 }, +] + +[[package]] +name = "langchain-protocol" +version = "0.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/51/1157009b6f94e6e58be58fa8b620187d657909a8b36a6bf5b0c52a2711f6/langchain_protocol-0.0.12.tar.gz", hash = "sha256:5e14c434290a705c9510fdb1a83ecf7561a5e6e0dfd053930ade80dba069269f", size = 6408 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/82/3431e3061c917439589fa88a6b23c9bc0e154cba0f05d2e895a68c76ff74/langchain_protocol-0.0.12-py3-none-any.whl", hash = "sha256:402b61f42d4139692528cf37226c367bb6efc8ff8165b29380accb0abfece7b2", size = 6639 }, +] + +[[package]] +name = "langgraph" +version = "1.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "langgraph-checkpoint" }, + { name = "langgraph-prebuilt" }, + { name = "langgraph-sdk" }, + { name = "pydantic" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/d5/9d9c65d5500a1ca7ea63d6d65aecfb248037018a74d7d4ef52e276bb4e4b/langgraph-1.1.9.tar.gz", hash = "sha256:bc5a49d5a5e71fda1f9c53c06c62f4caec9a95545b739d130a58b6ab3269e274", size = 560717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/58/0380420e66619d12c992c1f8cfda0c7a04e8f0fe8a84752245b9e7b1cba7/langgraph-1.1.9-py3-none-any.whl", hash = "sha256:7db13ceecde4ea643df6c097dcc9e534895dcd9fcc6500eeff2f2cde0fab16b2", size = 173744 }, +] + +[[package]] +name = "langgraph-checkpoint" +version = "4.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "ormsgpack" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/f2/cf8086e1f1a3358d9228805614e72602c281b18307f3fae64a5b854aad2d/langgraph_checkpoint-4.0.2.tar.gz", hash = "sha256:4f6f99cba8e272deabf81b2d8cdc96582af07a57a6ad591cdf216bb310497039", size = 160810 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/5a/6dba29dd89b0a46ae21c707da0f9d17e94f27d3e481ed15bc99d6bd20aa6/langgraph_checkpoint-4.0.2-py3-none-any.whl", hash = "sha256:59b0f29216128a629c58dd07c98aa004f82f51805d5573126ffb419b753ff253", size = 51000 }, +] + +[[package]] +name = "langgraph-prebuilt" +version = "1.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "langgraph-checkpoint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/bb/0e0b3eb33b1f2f32f8810a49aa24b7d11a5b0ed45f679386095946a59557/langgraph_prebuilt-1.0.11.tar.gz", hash = "sha256:0e71545f706a134b6a80a2a56916562797b499e3e4ab6eed5ce89396ac03d322", size = 171759 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/8c/f4c574cb75ae9b8a474215d03a029ea723c919f65771ca1c82fe532d0297/langgraph_prebuilt-1.0.11-py3-none-any.whl", hash = "sha256:7afbaf5d64959e452976664c75bb8ec24098d3510cf9c205919baf443e7342ec", size = 36832 }, +] + +[[package]] +name = "langgraph-sdk" +version = "0.3.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "orjson" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/db/77a45127dddcfea5e4256ba916182903e4c31dc4cfca305b8c386f0a9e53/langgraph_sdk-0.3.13.tar.gz", hash = "sha256:419ca5663eec3cec192ad194ac0647c0c826866b446073eb40f384f950986cd5", size = 196360 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ef/64d64e9f8eea47ce7b939aa6da6863b674c8d418647813c20111645fcc62/langgraph_sdk-0.3.13-py3-none-any.whl", hash = "sha256:aee09e345c90775f6de9d6f4c7b847cfc652e49055c27a2aed0d981af2af3bd0", size = 96668 }, +] + +[[package]] +name = "langsmith" +version = "0.7.37" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "uuid-utils" }, + { name = "xxhash" }, + { name = "zstandard" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/38/092f99a3326f0f6bb6ea62f388b16611d9cb619869ed7b0f3dae6c21c331/langsmith-0.7.37.tar.gz", hash = "sha256:e15ab27f5febbcfbaec4e6fa74ab71f0284f4c5965249cc732fe9344844290cb", size = 4433170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/76/fa99559d23ec9a39e1153f317a5ec99e7b967aec08b5faac04f8da603dd3/langsmith-0.7.37-py3-none-any.whl", hash = "sha256:64fc5fbf223fcdcc6ee44b08a5df4b2ab8a55e4d968e850c86b6b69fe0c258e3", size = 385948 }, +] + +[[package]] +name = "matplotlib" +version = "3.10.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331 }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461 }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091 }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027 }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269 }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588 }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913 }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019 }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645 }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194 }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684 }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790 }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571 }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292 }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276 }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218 }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145 }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085 }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358 }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970 }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785 }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999 }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543 }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800 }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561 }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884 }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333 }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516 }, +] + +[[package]] +name = "msal" +version = "1.36.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/cb/b02b0f748ac668922364ccb3c3bff5b71628a05f5adfec2ba2a5c3031483/msal-1.36.0.tar.gz", hash = "sha256:3f6a4af2b036b476a4215111c4297b4e6e236ed186cd804faefba23e4990978b", size = 174217 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/d3/414d1f0a5f6f4fe5313c2b002c54e78a3332970feb3f5fed14237aa17064/msal-1.36.0-py3-none-any.whl", hash = "sha256:36ecac30e2ff4322d956029aabce3c82301c29f0acb1ad89b94edcabb0e58ec4", size = 121547 }, +] + +[[package]] +name = "msal-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "msal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/99/5d239b6156eddf761a636bded1118414d161bd6b7b37a9335549ed159396/msal_extensions-1.3.1.tar.gz", hash = "sha256:c5b0fd10f65ef62b5f1d62f4251d51cbcaf003fcedae8c91b040a488614be1a4", size = 23315 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583 }, +] + +[[package]] +name = "msrest" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "certifi" }, + { name = "isodate" }, + { name = "requests" }, + { name = "requests-oauthlib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/77/8397c8fb8fc257d8ea0fa66f8068e073278c65f05acb17dcb22a02bfdc42/msrest-0.7.1.zip", hash = "sha256:6e7661f46f3afd88b75667b7187a92829924446c7ea1d169be8c4bb7eeb788b9", size = 175332 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384 }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174 }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116 }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524 }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368 }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952 }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317 }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132 }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140 }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277 }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291 }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156 }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742 }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221 }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664 }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490 }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695 }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884 }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122 }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175 }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460 }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930 }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582 }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031 }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596 }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492 }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899 }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970 }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060 }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888 }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554 }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341 }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391 }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422 }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770 }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109 }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573 }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190 }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486 }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219 }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132 }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420 }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510 }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094 }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786 }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483 }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403 }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315 }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528 }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784 }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980 }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602 }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930 }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074 }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471 }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401 }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143 }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507 }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358 }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884 }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878 }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542 }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403 }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889 }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982 }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415 }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337 }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788 }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842 }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237 }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008 }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542 }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719 }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319 }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933 }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532 }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661 }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539 }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806 }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682 }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810 }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394 }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556 }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311 }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060 }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302 }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407 }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631 }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691 }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241 }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767 }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169 }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477 }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487 }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002 }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353 }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914 }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005 }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974 }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591 }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700 }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781 }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959 }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768 }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181 }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035 }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958 }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020 }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758 }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948 }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325 }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883 }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474 }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500 }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755 }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643 }, +] + +[[package]] +name = "oauthlib" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 }, +] + +[[package]] +name = "openai" +version = "2.32.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/59/bdcc6b759b8c42dd73afaf5bf8f902c04b37987a5514dbc1c64dba390fef/openai-2.32.0.tar.gz", hash = "sha256:c54b27a9e4cb8d51f0dd94972ffd1a04437efeb259a9e60d8922b8bd26fe55e0", size = 693286 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/c1/d6e64ccd0536bf616556f0cad2b6d94a8125f508d25cfd814b1d2db4e2f1/openai-2.32.0-py3-none-any.whl", hash = "sha256:4dcc9badeb4bf54ad0d187453742f290226d30150890b7890711bda4f32f192f", size = 1162570 }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676 }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/bc/1559d46557fe6eca0b46c88d4c2676285f1f3be2e8d06bb5d15fbffc814a/opentelemetry_exporter_otlp_proto_common-1.40.0.tar.gz", hash = "sha256:1cbee86a4064790b362a86601ee7934f368b81cd4cc2f2e163902a6e7818a0fa", size = 20416 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ca/8f122055c97a932311a3f640273f084e738008933503d0c2563cd5d591fc/opentelemetry_exporter_otlp_proto_common-1.40.0-py3-none-any.whl", hash = "sha256:7081ff453835a82417bf38dccf122c827c3cbc94f2079b03bba02a3165f25149", size = 18369 }, +] + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-common" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/7f/b9e60435cfcc7590fa87436edad6822240dddbc184643a2a005301cc31f4/opentelemetry_exporter_otlp_proto_grpc-1.40.0.tar.gz", hash = "sha256:bd4015183e40b635b3dab8da528b27161ba83bf4ef545776b196f0fb4ec47740", size = 25759 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/6f/7ee0980afcbdcd2d40362da16f7f9796bd083bf7f0b8e038abfbc0300f5d/opentelemetry_exporter_otlp_proto_grpc-1.40.0-py3-none-any.whl", hash = "sha256:2aa0ca53483fe0cf6405087a7491472b70335bc5c7944378a0a8e72e86995c52", size = 20304 }, +] + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "packaging" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/37/6bf8e66bfcee5d3c6515b79cb2ee9ad05fe573c20f7ceb288d0e7eeec28c/opentelemetry_instrumentation-0.61b0.tar.gz", hash = "sha256:cb21b48db738c9de196eba6b805b4ff9de3b7f187e4bbf9a466fa170514f1fc7", size = 32606 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/3e/f6f10f178b6316de67f0dfdbbb699a24fbe8917cf1743c1595fb9dcdd461/opentelemetry_instrumentation-0.61b0-py3-none-any.whl", hash = "sha256:92a93a280e69788e8f88391247cc530fd81f16f2b011979d4d6398f805cfbc63", size = 33448 }, +] + +[[package]] +name = "opentelemetry-instrumentation-asgi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/3e/143cf5c034e58037307e6a24f06e0dd64b2c49ae60a965fc580027581931/opentelemetry_instrumentation_asgi-0.61b0.tar.gz", hash = "sha256:9d08e127244361dc33976d39dd4ca8f128b5aa5a7ae425208400a80a095019b5", size = 26691 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/78/154470cf9d741a7487fbb5067357b87386475bbb77948a6707cae982e158/opentelemetry_instrumentation_asgi-0.61b0-py3-none-any.whl", hash = "sha256:e4b3ce6b66074e525e717efff20745434e5efd5d9df6557710856fba356da7a4", size = 16980 }, +] + +[[package]] +name = "opentelemetry-instrumentation-dbapi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/ed/ba91c9e4a3ec65781e9c59982109f0a36de9fa574f622596b33d1985dab5/opentelemetry_instrumentation_dbapi-0.61b0.tar.gz", hash = "sha256:02fa800682c1de87dcad0e59f2092b3b6fb8b8ea0636518f989e1166b418dcb9", size = 16761 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/a5/d26c68f3fd33eb7410985cef7700bb426e2c4a26de9207902cbbffb19a3f/opentelemetry_instrumentation_dbapi-0.61b0-py3-none-any.whl", hash = "sha256:8f762c39c8edd20c6aef3282550a2cfbfec76c3f431bf5c36327dcf9ece2e5a0", size = 14134 }, +] + +[[package]] +name = "opentelemetry-instrumentation-django" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/ef/6bc1a6560630f26b1c010af86b28f42bfbe6a601bd1647d1436e0d3436aa/opentelemetry_instrumentation_django-0.61b0.tar.gz", hash = "sha256:9885154dc128578de0e6b5ce49e965c786f8ab071175bec005dcd454510be951", size = 25996 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/3b/74dad6d98fdee1d137f1c2748548d4159578508f21e3aef581c110e64041/opentelemetry_instrumentation_django-0.61b0-py3-none-any.whl", hash = "sha256:26c1b0b325a9783d4a2f4df660ba05cf929c3eda2ae9b07916b649bb44e1c5b6", size = 20773 }, +] + +[[package]] +name = "opentelemetry-instrumentation-fastapi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-asgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/35/aa727bb6e6ef930dcdc96a617b83748fece57b43c47d83ba8d83fbeca657/opentelemetry_instrumentation_fastapi-0.61b0.tar.gz", hash = "sha256:3a24f35b07c557ae1bbc483bf8412221f25d79a405f8b047de8b670722e2fa9f", size = 24800 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/05/acfeb2cccd434242a0a7d0ea29afaf077e04b42b35b485d89aee4e0d9340/opentelemetry_instrumentation_fastapi-0.61b0-py3-none-any.whl", hash = "sha256:a1a844d846540d687d377516b2ff698b51d87c781b59f47c214359c4a241047c", size = 13485 }, +] + +[[package]] +name = "opentelemetry-instrumentation-flask" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-wsgi" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/33/d6852d8f2c3eef86f2f8c858d6f5315983c7063e07e595519e96d4c31c06/opentelemetry_instrumentation_flask-0.61b0.tar.gz", hash = "sha256:e9faf58dfd9860a1868442d180142645abdafc1a652dd73d469a5efd106a7d49", size = 24071 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/41/619f3530324a58491f2d20f216a10dd7393629b29db4610dda642a27f4ed/opentelemetry_instrumentation_flask-0.61b0-py3-none-any.whl", hash = "sha256:e8ce474d7ce543bfbbb3e93f8a6f8263348af9d7b45502f387420cf3afa71253", size = 15996 }, +] + +[[package]] +name = "opentelemetry-instrumentation-logging" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/e0/69473f925acfe2d4edf5c23bcced36906ac3627aa7c5722a8e3f60825f3b/opentelemetry_instrumentation_logging-0.61b0.tar.gz", hash = "sha256:feaa30b700acd2a37cc81db5f562ab0c3a5b6cc2453595e98b72c01dcf649584", size = 17906 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/0e/2137db5239cc5e564495549a4d11488a7af9b48fc76520a0eea20e69ddae/opentelemetry_instrumentation_logging-0.61b0-py3-none-any.whl", hash = "sha256:6d87e5ded6a0128d775d41511f8380910a1b610671081d16efb05ac3711c0074", size = 17076 }, +] + +[[package]] +name = "opentelemetry-instrumentation-psycopg2" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-instrumentation-dbapi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/28/f28d52b1088e7a09761566f8700507b54d3d83a6f9c93c0ce02f53619e83/opentelemetry_instrumentation_psycopg2-0.61b0.tar.gz", hash = "sha256:863ccf9687b71e73dd489c7bb117278768bdf26aa0dafe7dc974a2425e05b5d7", size = 11676 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/f1/4341d0584c288765c73e28c30ba58e7aedb50c01108f17f947b872657f79/opentelemetry_instrumentation_psycopg2-0.61b0-py3-none-any.whl", hash = "sha256:36b96983beda05c927179bb66b6c72f07a8d9a591f76ce9da88b1dd1587cb083", size = 11491 }, +] + +[[package]] +name = "opentelemetry-instrumentation-requests" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/c7/7a47cb85c7aa93a9c820552e414889185bcf91245271d12e5d443e5f834d/opentelemetry_instrumentation_requests-0.61b0.tar.gz", hash = "sha256:15f879ce8fb206bd7e6fdc61663ea63481040a845218c0cf42902ce70bd7e9d9", size = 18379 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/a1/a7a133b273d1f53950f16a370fc94367eff472c9c2576e8e9e28c62dcc9f/opentelemetry_instrumentation_requests-0.61b0-py3-none-any.whl", hash = "sha256:cce19b379949fe637eb73ba39b02c57d2d0805447ca6d86534aa33fcb141f683", size = 14207 }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/37/77cd326b083390e74280c08bbd585153809619dad068e2d1b253fec1164d/opentelemetry_instrumentation_urllib-0.61b0.tar.gz", hash = "sha256:6a15ff862fc1603e0ea5ea75558f76f36436b02e0ae48daecedcb5e574cce160", size = 16894 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/fc/a88fbfd8b9eb16ba1c21f0514c12696441be7fc42c7e319f3ee793bf9e96/opentelemetry_instrumentation_urllib-0.61b0-py3-none-any.whl", hash = "sha256:d7e409876580fb41102e3522ce81a756e53a74073c036a267a1c280cc0fa09b0", size = 13970 }, +] + +[[package]] +name = "opentelemetry-instrumentation-urllib3" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/80/7ad8da30f479c6117768e72d6f2f3f0bd3495338707d6f61de042149578a/opentelemetry_instrumentation_urllib3-0.61b0.tar.gz", hash = "sha256:f00037bc8ff813153c4b79306f55a14618c40469a69c6c03a3add29dc7e8b928", size = 19325 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/0c/01359e55b9f2fb2b1d4d9e85e77773a96697207895118533f3be718a3326/opentelemetry_instrumentation_urllib3-0.61b0-py3-none-any.whl", hash = "sha256:9644f8c07870266e52f129e6226859ff3a35192555abe46fa0ef9bbbf5b6b46d", size = 14339 }, +] + +[[package]] +name = "opentelemetry-instrumentation-wsgi" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-instrumentation" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "opentelemetry-util-http" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/e5/189f2845362cfe78e356ba127eab21456309def411c6874aa4800c3de816/opentelemetry_instrumentation_wsgi-0.61b0.tar.gz", hash = "sha256:380f2ae61714e5303275a80b2e14c58571573cd1fddf496d8c39fb9551c5e532", size = 19898 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/75/d6b42ba26f3c921be6d01b16561b7bb863f843bad7ac3a5011f62617bcab/opentelemetry_instrumentation_wsgi-0.61b0-py3-none-any.whl", hash = "sha256:bd33b0824166f24134a3400648805e8d2e6a7951f070241294e8b8866611d7fa", size = 14628 }, +] + +[[package]] +name = "opentelemetry-proto" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/77/dd38991db037fdfce45849491cb61de5ab000f49824a00230afb112a4392/opentelemetry_proto-1.40.0.tar.gz", hash = "sha256:03f639ca129ba513f5819810f5b1f42bcb371391405d99c168fe6937c62febcd", size = 45667 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/b2/189b2577dde745b15625b3214302605b1353436219d42b7912e77fa8dc24/opentelemetry_proto-1.40.0-py3-none-any.whl", hash = "sha256:266c4385d88923a23d63e353e9761af0f47a6ed0d486979777fe4de59dc9b25f", size = 72073 }, +] + +[[package]] +name = "opentelemetry-resource-detector-azure" +version = "0.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/e4/0d359d48d03d447225b30c3dd889d5d454e3b413763ff721f9b0e4ac2e59/opentelemetry_resource_detector_azure-0.1.5.tar.gz", hash = "sha256:e0ba658a87c69eebc806e75398cd0e9f68a8898ea62de99bc1b7083136403710", size = 11503 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/ae/c26d8da88ba2e438e9653a408b0c2ad6f17267801250a8f3cc6405a93a72/opentelemetry_resource_detector_azure-0.1.5-py3-none-any.whl", hash = "sha256:4dcc5d54ab5c3b11226af39509bc98979a8b9e0f8a24c1b888783755d3bf00eb", size = 14252 }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.40.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951 }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621 }, +] + +[[package]] +name = "opentelemetry-semantic-conventions-ai" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-sdk" }, + { name = "opentelemetry-semantic-conventions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/02/10aeacc37a38a3a8fa16ff67bec1ae3bf882539f6f9efb0f70acf802ca2d/opentelemetry_semantic_conventions_ai-0.5.1.tar.gz", hash = "sha256:153906200d8c1d2f8e09bd78dbef526916023de85ac3dab35912bfafb69ff04c", size = 26533 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/22/41fb05f1dc5fda2c468e05a41814c20859016c85117b66c8a257cae814f6/opentelemetry_semantic_conventions_ai-0.5.1-py3-none-any.whl", hash = "sha256:25aeb22bd261543b4898a73824026d96770e5351209c7d07a0b1314762b1f6e4", size = 11250 }, +] + +[[package]] +name = "opentelemetry-util-http" +version = "0.61b0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/3c/f0196223efc5c4ca19f8fad3d5462b171ac6333013335ce540c01af419e9/opentelemetry_util_http-0.61b0.tar.gz", hash = "sha256:1039cb891334ad2731affdf034d8fb8b48c239af9b6dd295e5fabd07f1c95572", size = 11361 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/e5/c08aaaf2f64288d2b6ef65741d2de5454e64af3e050f34285fb1907492fe/opentelemetry_util_http-0.61b0-py3-none-any.whl", hash = "sha256:8e715e848233e9527ea47e275659ea60a57a75edf5206a3b937e236a6da5fc33", size = 9281 }, +] + +[[package]] +name = "orjson" +version = "3.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/1b/2024d06792d0779f9dbc51531b61c24f76c75b9f4ce05e6f3377a1814cea/orjson-3.11.8.tar.gz", hash = "sha256:96163d9cdc5a202703e9ad1b9ae757d5f0ca62f4fa0cc93d1f27b0e180cc404e", size = 5603832 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/7f/95fba509bb2305fab0073558f1e8c3a2ec4b2afe58ed9fcb7d3b8beafe94/orjson-3.11.8-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3f23426851d98478c8970da5991f84784a76682213cd50eb73a1da56b95239dc", size = 229180 }, + { url = "https://files.pythonhosted.org/packages/f6/9d/b237215c743ca073697d759b5503abd2cb8a0d7b9c9e21f524bcf176ab66/orjson-3.11.8-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ebaed4cef74a045b83e23537b52ef19a367c7e3f536751e355a2a394f8648559", size = 128754 }, + { url = "https://files.pythonhosted.org/packages/42/3d/27d65b6d11e63f133781425f132807aef793ed25075fec686fc8e46dd528/orjson-3.11.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97c8f5d3b62380b70c36ffacb2a356b7c6becec86099b177f73851ba095ef623", size = 131877 }, + { url = "https://files.pythonhosted.org/packages/dd/cc/faee30cd8f00421999e40ef0eba7332e3a625ce91a58200a2f52c7fef235/orjson-3.11.8-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:436c4922968a619fb7fef1ccd4b8b3a76c13b67d607073914d675026e911a65c", size = 130361 }, + { url = "https://files.pythonhosted.org/packages/5c/bb/a6c55896197f97b6d4b4e7c7fd77e7235517c34f5d6ad5aadd43c54c6d7c/orjson-3.11.8-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ab359aff0436d80bfe8a23b46b5fea69f1e18aaf1760a709b4787f1318b317f", size = 135521 }, + { url = "https://files.pythonhosted.org/packages/9c/7c/ca3a3525aa32ff636ebb1778e77e3587b016ab2edb1b618b36ba96f8f2c0/orjson-3.11.8-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f89b6d0b3a8d81e1929d3ab3d92bbc225688bd80a770c49432543928fe09ac55", size = 146862 }, + { url = "https://files.pythonhosted.org/packages/3c/0c/18a9d7f18b5edd37344d1fd5be17e94dc652c67826ab749c6e5948a78112/orjson-3.11.8-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c009e7a2ca9ad0ed1376ce20dd692146a5d9fe4310848904b6b4fee5c5c137", size = 132847 }, + { url = "https://files.pythonhosted.org/packages/23/91/7e722f352ad67ca573cee44de2a58fb810d0f4eb4e33276c6a557979fd8a/orjson-3.11.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b895b781b3e395c067129d8551655642dfe9437273211d5404e87ac752b53", size = 133637 }, + { url = "https://files.pythonhosted.org/packages/af/04/32845ce13ac5bd1046ddb02ac9432ba856cc35f6d74dde95864fe0ad5523/orjson-3.11.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:88006eda83858a9fdf73985ce3804e885c2befb2f506c9a3723cdeb5a2880e3e", size = 141906 }, + { url = "https://files.pythonhosted.org/packages/02/5e/c551387ddf2d7106d9039369862245c85738b828844d13b99ccb8d61fd06/orjson-3.11.8-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:55120759e61309af7fcf9e961c6f6af3dde5921cdb3ee863ef63fd9db126cae6", size = 423722 }, + { url = "https://files.pythonhosted.org/packages/00/a3/ecfe62434096f8a794d4976728cb59bcfc4a643977f21c2040545d37eb4c/orjson-3.11.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98bdc6cb889d19bed01de46e67574a2eab61f5cc6b768ed50e8ac68e9d6ffab6", size = 147801 }, + { url = "https://files.pythonhosted.org/packages/18/6d/0dce10b9f6643fdc59d99333871a38fa5a769d8e2fc34a18e5d2bfdee900/orjson-3.11.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:708c95f925a43ab9f34625e45dcdadf09ec8a6e7b664a938f2f8d5650f6c090b", size = 136460 }, + { url = "https://files.pythonhosted.org/packages/01/d6/6dde4f31842d87099238f1f07b459d24edc1a774d20687187443ab044191/orjson-3.11.8-cp313-cp313-win32.whl", hash = "sha256:01c4e5a6695dc09098f2e6468a251bc4671c50922d4d745aff1a0a33a0cf5b8d", size = 131956 }, + { url = "https://files.pythonhosted.org/packages/c1/f9/4e494a56e013db957fb77186b818b916d4695b8fa2aa612364974160e91b/orjson-3.11.8-cp313-cp313-win_amd64.whl", hash = "sha256:c154a35dd1330707450bb4d4e7dd1f17fa6f42267a40c1e8a1daa5e13719b4b8", size = 127410 }, + { url = "https://files.pythonhosted.org/packages/57/7f/803203d00d6edb6e9e7eef421d4e1adbb5ea973e40b3533f3cfd9aeb374e/orjson-3.11.8-cp313-cp313-win_arm64.whl", hash = "sha256:4861bde57f4d253ab041e374f44023460e60e71efaa121f3c5f0ed457c3a701e", size = 127338 }, + { url = "https://files.pythonhosted.org/packages/6d/35/b01910c3d6b85dc882442afe5060cbf719c7d1fc85749294beda23d17873/orjson-3.11.8-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ec795530a73c269a55130498842aaa762e4a939f6ce481a7e986eeaa790e9da4", size = 229171 }, + { url = "https://files.pythonhosted.org/packages/c2/56/c9ec97bd11240abef39b9e5d99a15462809c45f677420fd148a6c5e6295e/orjson-3.11.8-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:c492a0e011c0f9066e9ceaa896fbc5b068c54d365fea5f3444b697ee01bc8625", size = 128746 }, + { url = "https://files.pythonhosted.org/packages/3b/e4/66d4f30a90de45e2f0cbd9623588e8ae71eef7679dbe2ae954ed6d66a41f/orjson-3.11.8-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:883206d55b1bd5f5679ad5e6ddd3d1a5e3cac5190482927fdb8c78fb699193b5", size = 131867 }, + { url = "https://files.pythonhosted.org/packages/19/30/2a645fc9286b928675e43fa2a3a16fb7b6764aa78cc719dc82141e00f30b/orjson-3.11.8-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5774c1fdcc98b2259800b683b19599c133baeb11d60033e2095fd9d4667b82db", size = 124664 }, + { url = "https://files.pythonhosted.org/packages/db/44/77b9a86d84a28d52ba3316d77737f6514e17118119ade3f91b639e859029/orjson-3.11.8-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7381c83dd3d4a6347e6635950aa448f54e7b8406a27c7ecb4a37e9f1ae08b", size = 129701 }, + { url = "https://files.pythonhosted.org/packages/b3/ea/eff3d9bfe47e9bc6969c9181c58d9f71237f923f9c86a2d2f490cd898c82/orjson-3.11.8-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14439063aebcb92401c11afc68ee4e407258d2752e62d748b6942dad20d2a70d", size = 141202 }, + { url = "https://files.pythonhosted.org/packages/52/c8/90d4b4c60c84d62068d0cf9e4d8f0a4e05e76971d133ac0c60d818d4db20/orjson-3.11.8-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa72e71977bff96567b0f500fc5bfd2fdf915f34052c782a4c6ebbdaa97aa858", size = 127194 }, + { url = "https://files.pythonhosted.org/packages/8d/c7/ea9e08d1f0ba981adffb629811148b44774d935171e7b3d780ae43c4c254/orjson-3.11.8-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7679bc2f01bb0d219758f1a5f87bb7c8a81c0a186824a393b366876b4948e14f", size = 133639 }, + { url = "https://files.pythonhosted.org/packages/6c/8c/ddbbfd6ba59453c8fc7fe1d0e5983895864e264c37481b2a791db635f046/orjson-3.11.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:14f7b8fcb35ef403b42fa5ecfa4ed032332a91f3dc7368fbce4184d59e1eae0d", size = 141914 }, + { url = "https://files.pythonhosted.org/packages/4e/31/dbfbefec9df060d34ef4962cd0afcb6fa7a9ec65884cb78f04a7859526c3/orjson-3.11.8-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c2bdf7b2facc80b5e34f48a2d557727d5c5c57a8a450de122ae81fa26a81c1bc", size = 423800 }, + { url = "https://files.pythonhosted.org/packages/87/cf/f74e9ae9803d4ab46b163494adba636c6d7ea955af5cc23b8aaa94cfd528/orjson-3.11.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ccd7ba1b0605813a0715171d39ec4c314cb97a9c85893c2c5c0c3a3729df38bf", size = 147837 }, + { url = "https://files.pythonhosted.org/packages/64/e6/9214f017b5db85e84e68602792f742e5dc5249e963503d1b356bee611e01/orjson-3.11.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbc8c9c02463fef4d3c53a9ba3336d05496ec8e1f1c53326a1e4acc11f5c600", size = 136441 }, + { url = "https://files.pythonhosted.org/packages/24/dd/3590348818f58f837a75fb969b04cdf187ae197e14d60b5e5a794a38b79d/orjson-3.11.8-cp314-cp314-win32.whl", hash = "sha256:0b57f67710a8cd459e4e54eb96d5f77f3624eba0c661ba19a525807e42eccade", size = 131983 }, + { url = "https://files.pythonhosted.org/packages/3f/0f/b6cb692116e05d058f31ceee819c70f097fa9167c82f67fabe7516289abc/orjson-3.11.8-cp314-cp314-win_amd64.whl", hash = "sha256:735e2262363dcbe05c35e3a8869898022af78f89dde9e256924dc02e99fe69ca", size = 127396 }, + { url = "https://files.pythonhosted.org/packages/c0/d1/facb5b5051fabb0ef9d26c6544d87ef19a939a9a001198655d0d891062dd/orjson-3.11.8-cp314-cp314-win_arm64.whl", hash = "sha256:6ccdea2c213cf9f3d9490cbd5d427693c870753df41e6cb375bd79bcbafc8817", size = 127330 }, +] + +[[package]] +name = "ormsgpack" +version = "1.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/0c/f1761e21486942ab9bb6feaebc610fa074f7c5e496e6962dea5873348077/ormsgpack-1.12.2.tar.gz", hash = "sha256:944a2233640273bee67521795a73cf1e959538e0dfb7ac635505010455e53b33", size = 39031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/29/bb0eba3288c0449efbb013e9c6f58aea79cf5cb9ee1921f8865f04c1a9d7/ormsgpack-1.12.2-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5ea60cb5f210b1cfbad8c002948d73447508e629ec375acb82910e3efa8ff355", size = 378661 }, + { url = "https://files.pythonhosted.org/packages/6e/31/5efa31346affdac489acade2926989e019e8ca98129658a183e3add7af5e/ormsgpack-1.12.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3601f19afdbea273ed70b06495e5794606a8b690a568d6c996a90d7255e51c1", size = 203194 }, + { url = "https://files.pythonhosted.org/packages/eb/56/d0087278beef833187e0167f8527235ebe6f6ffc2a143e9de12a98b1ce87/ormsgpack-1.12.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29a9f17a3dac6054c0dce7925e0f4995c727f7c41859adf9b5572180f640d172", size = 210778 }, + { url = "https://files.pythonhosted.org/packages/1c/a2/072343e1413d9443e5a252a8eb591c2d5b1bffbe5e7bfc78c069361b92eb/ormsgpack-1.12.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39c1bd2092880e413902910388be8715f70b9f15f20779d44e673033a6146f2d", size = 212592 }, + { url = "https://files.pythonhosted.org/packages/a2/8b/a0da3b98a91d41187a63b02dda14267eefc2a74fcb43cc2701066cf1510e/ormsgpack-1.12.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50b7249244382209877deedeee838aef1542f3d0fc28b8fe71ca9d7e1896a0d7", size = 387164 }, + { url = "https://files.pythonhosted.org/packages/19/bb/6d226bc4cf9fc20d8eb1d976d027a3f7c3491e8f08289a2e76abe96a65f3/ormsgpack-1.12.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:5af04800d844451cf102a59c74a841324868d3f1625c296a06cc655c542a6685", size = 482516 }, + { url = "https://files.pythonhosted.org/packages/fb/f1/bb2c7223398543dedb3dbf8bb93aaa737b387de61c5feaad6f908841b782/ormsgpack-1.12.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cec70477d4371cd524534cd16472d8b9cc187e0e3043a8790545a9a9b296c258", size = 425539 }, + { url = "https://files.pythonhosted.org/packages/7b/e8/0fb45f57a2ada1fed374f7494c8cd55e2f88ccd0ab0a669aa3468716bf5f/ormsgpack-1.12.2-cp313-cp313-win_amd64.whl", hash = "sha256:21f4276caca5c03a818041d637e4019bc84f9d6ca8baa5ea03e5cc8bf56140e9", size = 117459 }, + { url = "https://files.pythonhosted.org/packages/7a/d4/0cfeea1e960d550a131001a7f38a5132c7ae3ebde4c82af1f364ccc5d904/ormsgpack-1.12.2-cp313-cp313-win_arm64.whl", hash = "sha256:baca4b6773d20a82e36d6fd25f341064244f9f86a13dead95dd7d7f996f51709", size = 111577 }, + { url = "https://files.pythonhosted.org/packages/94/16/24d18851334be09c25e87f74307c84950f18c324a4d3c0b41dabdbf19c29/ormsgpack-1.12.2-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:bc68dd5915f4acf66ff2010ee47c8906dc1cf07399b16f4089f8c71733f6e36c", size = 378717 }, + { url = "https://files.pythonhosted.org/packages/b5/a2/88b9b56f83adae8032ac6a6fa7f080c65b3baf9b6b64fd3d37bd202991d4/ormsgpack-1.12.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46d084427b4132553940070ad95107266656cb646ea9da4975f85cb1a6676553", size = 203183 }, + { url = "https://files.pythonhosted.org/packages/a9/80/43e4555963bf602e5bdc79cbc8debd8b6d5456c00d2504df9775e74b450b/ormsgpack-1.12.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c010da16235806cf1d7bc4c96bf286bfa91c686853395a299b3ddb49499a3e13", size = 210814 }, + { url = "https://files.pythonhosted.org/packages/78/e1/7cfbf28de8bca6efe7e525b329c31277d1b64ce08dcba723971c241a9d60/ormsgpack-1.12.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18867233df592c997154ff942a6503df274b5ac1765215bceba7a231bea2745d", size = 212634 }, + { url = "https://files.pythonhosted.org/packages/95/f8/30ae5716e88d792a4e879debee195653c26ddd3964c968594ddef0a3cc7e/ormsgpack-1.12.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b009049086ddc6b8f80c76b3955df1aa22a5fbd7673c525cd63bf91f23122ede", size = 387139 }, + { url = "https://files.pythonhosted.org/packages/dc/81/aee5b18a3e3a0e52f718b37ab4b8af6fae0d9d6a65103036a90c2a8ffb5d/ormsgpack-1.12.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:1dcc17d92b6390d4f18f937cf0b99054824a7815818012ddca925d6e01c2e49e", size = 482578 }, + { url = "https://files.pythonhosted.org/packages/bd/17/71c9ba472d5d45f7546317f467a5fc941929cd68fb32796ca3d13dcbaec2/ormsgpack-1.12.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f04b5e896d510b07c0ad733d7fce2d44b260c5e6c402d272128f8941984e4285", size = 425539 }, + { url = "https://files.pythonhosted.org/packages/2e/a6/ac99cd7fe77e822fed5250ff4b86fa66dd4238937dd178d2299f10b69816/ormsgpack-1.12.2-cp314-cp314-win_amd64.whl", hash = "sha256:ae3aba7eed4ca7cb79fd3436eddd29140f17ea254b91604aa1eb19bfcedb990f", size = 117493 }, + { url = "https://files.pythonhosted.org/packages/3a/67/339872846a1ae4592535385a1c1f93614138566d7af094200c9c3b45d1e5/ormsgpack-1.12.2-cp314-cp314-win_arm64.whl", hash = "sha256:118576ea6006893aea811b17429bfc561b4778fad393f5f538c84af70b01260c", size = 111579 }, + { url = "https://files.pythonhosted.org/packages/49/c2/6feb972dc87285ad381749d3882d8aecbde9f6ecf908dd717d33d66df095/ormsgpack-1.12.2-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7121b3d355d3858781dc40dafe25a32ff8a8242b9d80c692fd548a4b1f7fd3c8", size = 378721 }, + { url = "https://files.pythonhosted.org/packages/a3/9a/900a6b9b413e0f8a471cf07830f9cf65939af039a362204b36bd5b581d8b/ormsgpack-1.12.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ee766d2e78251b7a63daf1cddfac36a73562d3ddef68cacfb41b2af64698033", size = 203170 }, + { url = "https://files.pythonhosted.org/packages/87/4c/27a95466354606b256f24fad464d7c97ab62bce6cc529dd4673e1179b8fb/ormsgpack-1.12.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292410a7d23de9b40444636b9b8f1e4e4b814af7f1ef476e44887e52a123f09d", size = 212816 }, + { url = "https://files.pythonhosted.org/packages/73/cd/29cee6007bddf7a834e6cd6f536754c0535fcb939d384f0f37a38b1cddb8/ormsgpack-1.12.2-cp314-cp314t-win_amd64.whl", hash = "sha256:837dd316584485b72ef451d08dd3e96c4a11d12e4963aedb40e08f89685d8ec2", size = 117232 }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195 }, +] + +[[package]] +name = "pandas" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105 }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088 }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066 }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780 }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181 }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899 }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574 }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156 }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238 }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520 }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154 }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449 }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475 }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568 }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652 }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084 }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146 }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081 }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535 }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992 }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257 }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893 }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644 }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246 }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801 }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643 }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641 }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993 }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274 }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530 }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341 }, +] + +[[package]] +name = "parso" +version = "0.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837 }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528 }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401 }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094 }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402 }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005 }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669 }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194 }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423 }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667 }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580 }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896 }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266 }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508 }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927 }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624 }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252 }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550 }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114 }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667 }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966 }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241 }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592 }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542 }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765 }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848 }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515 }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159 }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185 }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386 }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384 }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599 }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021 }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360 }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628 }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321 }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723 }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400 }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835 }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225 }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541 }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251 }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807 }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935 }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720 }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498 }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413 }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084 }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152 }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579 }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348 }, +] + +[[package]] +name = "priority" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/3c/eb7c35f4dcede96fca1842dac5f4f5d15511aa4b52f3a961219e68ae9204/priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0", size = 24792 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/5f/82c8074f7e84978129347c2c6ec8b6c59f3584ff1a20bc3c940a3e061790/priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa", size = 8946 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457 }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835 }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545 }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886 }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261 }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184 }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534 }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500 }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994 }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884 }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464 }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588 }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667 }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463 }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621 }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649 }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636 }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872 }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257 }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696 }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378 }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283 }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616 }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773 }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664 }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643 }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595 }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711 }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247 }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102 }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964 }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546 }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330 }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521 }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662 }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928 }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650 }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912 }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300 }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208 }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633 }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724 }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069 }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099 }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391 }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626 }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781 }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570 }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436 }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373 }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554 }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395 }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653 }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914 }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567 }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542 }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845 }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985 }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999 }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779 }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796 }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023 }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448 }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329 }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172 }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813 }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140 }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036 }, +] + +[[package]] +name = "protobuf" +version = "6.33.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739 }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089 }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737 }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610 }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381 }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436 }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656 }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595 }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082 }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476 }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062 }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893 }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589 }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664 }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087 }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383 }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210 }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228 }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284 }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090 }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859 }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560 }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997 }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972 }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266 }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737 }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172 }, +] + +[[package]] +name = "pydantic" +version = "2.13.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/e4/40d09941a2cebcb20609b86a559817d5b9291c49dd6f8c87e5feffbe703a/pydantic-2.13.3.tar.gz", hash = "sha256:af09e9d1d09f4e7fe37145c1f577e1d61ceb9a41924bf0094a36506285d0a84d", size = 844068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl", hash = "sha256:6db14ac8dfc9a1e57f87ea2c0de670c251240f43cb0c30a5130e9720dc612927", size = 471981 }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/ef/f7abb56c49382a246fd2ce9c799691e3c3e7175ec74b14d99e798bcddb1a/pydantic_core-2.46.3.tar.gz", hash = "sha256:41c178f65b8c29807239d47e6050262eb6bf84eb695e41101e62e38df4a5bc2c", size = 471412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/3c/9b5e8eb9821936d065439c3b0fb1490ffa64163bfe7e1595985a47896073/pydantic_core-2.46.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:12bc98de041458b80c86c56b24df1d23832f3e166cbaff011f25d187f5c62c37", size = 2102109 }, + { url = "https://files.pythonhosted.org/packages/91/97/1c41d1f5a19f241d8069f1e249853bcce378cdb76eec8ab636d7bc426280/pydantic_core-2.46.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85348b8f89d2c3508b65b16c3c33a4da22b8215138d8b996912bb1532868885f", size = 1951820 }, + { url = "https://files.pythonhosted.org/packages/30/b4/d03a7ae14571bc2b6b3c7b122441154720619afe9a336fa3a95434df5e2f/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1105677a6df914b1fb71a81b96c8cce7726857e1717d86001f29be06a25ee6f8", size = 1977785 }, + { url = "https://files.pythonhosted.org/packages/ae/0c/4086f808834b59e3c8f1aa26df8f4b6d998cdcf354a143d18ef41529d1fe/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87082cd65669a33adeba5470769e9704c7cf026cc30afb9cc77fd865578ebaad", size = 2062761 }, + { url = "https://files.pythonhosted.org/packages/fa/71/a649be5a5064c2df0db06e0a512c2281134ed2fcc981f52a657936a7527c/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e5f66e12c4f5212d08522963380eaaeac5ebd795826cfd19b2dfb0c7a52b9c", size = 2232989 }, + { url = "https://files.pythonhosted.org/packages/a2/84/7756e75763e810b3a710f4724441d1ecc5883b94aacb07ca71c5fb5cfb69/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6cdf19bf84128d5e7c37e8a73a0c5c10d51103a650ac585d42dd6ae233f2b7f", size = 2303975 }, + { url = "https://files.pythonhosted.org/packages/6c/35/68a762e0c1e31f35fa0dac733cbd9f5b118042853698de9509c8e5bf128b/pydantic_core-2.46.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031bb17f4885a43773c8c763089499f242aee2ea85cf17154168775dccdecf35", size = 2095325 }, + { url = "https://files.pythonhosted.org/packages/77/bf/1bf8c9a8e91836c926eae5e3e51dce009bf495a60ca56060689d3df3f340/pydantic_core-2.46.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:bcf2a8b2982a6673693eae7348ef3d8cf3979c1d63b54fca7c397a635cc68687", size = 2133368 }, + { url = "https://files.pythonhosted.org/packages/e5/50/87d818d6bab915984995157ceb2380f5aac4e563dddbed6b56f0ed057aba/pydantic_core-2.46.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28e8cf2f52d72ced402a137145923a762cbb5081e48b34312f7a0c8f55928ec3", size = 2173908 }, + { url = "https://files.pythonhosted.org/packages/91/88/a311fb306d0bd6185db41fa14ae888fb81d0baf648a761ae760d30819d33/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:17eaface65d9fc5abb940003020309c1bf7a211f5f608d7870297c367e6f9022", size = 2186422 }, + { url = "https://files.pythonhosted.org/packages/8f/79/28fd0d81508525ab2054fef7c77a638c8b5b0afcbbaeee493cf7c3fef7e1/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:93fd339f23408a07e98950a89644f92c54d8729719a40b30c0a30bb9ebc55d23", size = 2332709 }, + { url = "https://files.pythonhosted.org/packages/b3/21/795bf5fe5c0f379308b8ef19c50dedab2e7711dbc8d0c2acf08f1c7daa05/pydantic_core-2.46.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:23cbdb3aaa74dfe0837975dbf69b469753bbde8eacace524519ffdb6b6e89eb7", size = 2372428 }, + { url = "https://files.pythonhosted.org/packages/45/b3/ed14c659cbe7605e3ef063077680a64680aec81eb1a04763a05190d49b7f/pydantic_core-2.46.3-cp313-cp313-win32.whl", hash = "sha256:610eda2e3838f401105e6326ca304f5da1e15393ae25dacae5c5c63f2c275b13", size = 1965601 }, + { url = "https://files.pythonhosted.org/packages/ef/bb/adb70d9a762ddd002d723fbf1bd492244d37da41e3af7b74ad212609027e/pydantic_core-2.46.3-cp313-cp313-win_amd64.whl", hash = "sha256:68cc7866ed863db34351294187f9b729964c371ba33e31c26f478471c52e1ed0", size = 2071517 }, + { url = "https://files.pythonhosted.org/packages/52/eb/66faefabebfe68bd7788339c9c9127231e680b11906368c67ce112fdb47f/pydantic_core-2.46.3-cp313-cp313-win_arm64.whl", hash = "sha256:f64b5537ac62b231572879cd08ec05600308636a5d63bcbdb15063a466977bec", size = 2035802 }, + { url = "https://files.pythonhosted.org/packages/7f/db/a7bcb4940183fda36022cd18ba8dd12f2dff40740ec7b58ce7457befa416/pydantic_core-2.46.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:afa3aa644f74e290cdede48a7b0bee37d1c35e71b05105f6b340d484af536d9b", size = 2097614 }, + { url = "https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018", size = 1951896 }, + { url = "https://files.pythonhosted.org/packages/87/92/37cf4049d1636996e4b888c05a501f40a43ff218983a551d57f9d5e14f0d/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e29908922ce9da1a30b4da490bd1d3d82c01dcfdf864d2a74aacee674d0bfa34", size = 1979314 }, + { url = "https://files.pythonhosted.org/packages/d8/36/9ff4d676dfbdfb2d591cf43f3d90ded01e15b1404fd101180ed2d62a2fd3/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c9ff69140423eea8ed2d5477df3ba037f671f5e897d206d921bc9fdc39613e7", size = 2056133 }, + { url = "https://files.pythonhosted.org/packages/bc/f0/405b442a4d7ba855b06eec8b2bf9c617d43b8432d099dfdc7bf999293495/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b675ab0a0d5b1c8fdb81195dc5bcefea3f3c240871cdd7ff9a2de8aa50772eb2", size = 2228726 }, + { url = "https://files.pythonhosted.org/packages/e7/f8/65cd92dd5a0bd89ba277a98ecbfaf6fc36bbd3300973c7a4b826d6ab1391/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0087084960f209a9a4af50ecd1fb063d9ad3658c07bb81a7a53f452dacbfb2ba", size = 2301214 }, + { url = "https://files.pythonhosted.org/packages/fd/86/ef96a4c6e79e7a2d0410826a68fbc0eccc0fd44aa733be199d5fcac3bb87/pydantic_core-2.46.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed42e6cc8e1b0e2b9b96e2276bad70ae625d10d6d524aed0c93de974ae029f9f", size = 2099927 }, + { url = "https://files.pythonhosted.org/packages/6d/53/269caf30e0096e0a8a8f929d1982a27b3879872cca2d917d17c2f9fdf4fe/pydantic_core-2.46.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f1771ce258afb3e4201e67d154edbbae712a76a6081079fe247c2f53c6322c22", size = 2128789 }, + { url = "https://files.pythonhosted.org/packages/00/b0/1a6d9b6a587e118482910c244a1c5acf4d192604174132efd12bf0ac486f/pydantic_core-2.46.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7610b6a5242a6c736d8ad47fd5fff87fcfe8f833b281b1c409c3d6835d9227f", size = 2173815 }, + { url = "https://files.pythonhosted.org/packages/87/56/e7e00d4041a7e62b5a40815590114db3b535bf3ca0bf4dca9f16cef25246/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:ff5e7783bcc5476e1db448bf268f11cb257b1c276d3e89f00b5727be86dd0127", size = 2181608 }, + { url = "https://files.pythonhosted.org/packages/e8/22/4bd23c3d41f7c185d60808a1de83c76cf5aeabf792f6c636a55c3b1ec7f9/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:9d2e32edcc143bc01e95300671915d9ca052d4f745aa0a49c48d4803f8a85f2c", size = 2326968 }, + { url = "https://files.pythonhosted.org/packages/24/ac/66cd45129e3915e5ade3b292cb3bc7fd537f58f8f8dbdaba6170f7cabb74/pydantic_core-2.46.3-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6e42d83d1c6b87fa56b521479cff237e626a292f3b31b6345c15a99121b454c1", size = 2369842 }, + { url = "https://files.pythonhosted.org/packages/a2/51/dd4248abb84113615473aa20d5545b7c4cd73c8644003b5259686f93996c/pydantic_core-2.46.3-cp314-cp314-win32.whl", hash = "sha256:07bc6d2a28c3adb4f7c6ae46aa4f2d2929af127f587ed44057af50bf1ce0f505", size = 1959661 }, + { url = "https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl", hash = "sha256:8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e", size = 2071686 }, + { url = "https://files.pythonhosted.org/packages/8c/db/1cf77e5247047dfee34bc01fa9bca134854f528c8eb053e144298893d370/pydantic_core-2.46.3-cp314-cp314-win_arm64.whl", hash = "sha256:5dcbbcf4d22210ced8f837c96db941bdb078f419543472aca5d9a0bb7cddc7df", size = 2026907 }, + { url = "https://files.pythonhosted.org/packages/57/c0/b3df9f6a543276eadba0a48487b082ca1f201745329d97dbfa287034a230/pydantic_core-2.46.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d0fe3dce1e836e418f912c1ad91c73357d03e556a4d286f441bf34fed2dbeecf", size = 2095047 }, + { url = "https://files.pythonhosted.org/packages/66/57/886a938073b97556c168fd99e1a7305bb363cd30a6d2c76086bf0587b32a/pydantic_core-2.46.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9ce92e58abc722dac1bf835a6798a60b294e48eb0e625ec9fd994b932ac5feee", size = 1934329 }, + { url = "https://files.pythonhosted.org/packages/0b/7c/b42eaa5c34b13b07ecb51da21761297a9b8eb43044c864a035999998f328/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03e6467f0f5ab796a486146d1b887b2dc5e5f9b3288898c1b1c3ad974e53e4a", size = 1974847 }, + { url = "https://files.pythonhosted.org/packages/e6/9b/92b42db6543e7de4f99ae977101a2967b63122d4b6cf7773812da2d7d5b5/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2798b6ba041b9d70acfb9071a2ea13c8456dd1e6a5555798e41ba7b0790e329c", size = 2041742 }, + { url = "https://files.pythonhosted.org/packages/0f/19/46fbe1efabb5aa2834b43b9454e70f9a83ad9c338c1291e48bdc4fecf167/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9be3e221bdc6d69abf294dcf7aff6af19c31a5cdcc8f0aa3b14be29df4bd03b1", size = 2236235 }, + { url = "https://files.pythonhosted.org/packages/77/da/b3f95bc009ad60ec53120f5d16c6faa8cabdbe8a20d83849a1f2b8728148/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13936129ce841f2a5ddf6f126fea3c43cd128807b5a59588c37cf10178c2e64", size = 2282633 }, + { url = "https://files.pythonhosted.org/packages/cc/6e/401336117722e28f32fb8220df676769d28ebdf08f2f4469646d404c43a3/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28b5f2ef03416facccb1c6ef744c69793175fd27e44ef15669201601cf423acb", size = 2109679 }, + { url = "https://files.pythonhosted.org/packages/fc/53/b289f9bc8756a32fe718c46f55afaeaf8d489ee18d1a1e7be1db73f42cc4/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:830d1247d77ad23852314f069e9d7ddafeec5f684baf9d7e7065ed46a049c4e6", size = 2108342 }, + { url = "https://files.pythonhosted.org/packages/10/5b/8292fc7c1f9111f1b2b7c1b0dcf1179edcd014fc3ea4517499f50b829d71/pydantic_core-2.46.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0793c90c1a3c74966e7975eaef3ed30ebdff3260a0f815a62a22adc17e4c01c", size = 2157208 }, + { url = "https://files.pythonhosted.org/packages/2b/9e/f80044e9ec07580f057a89fc131f78dda7a58751ddf52bbe05eaf31db50f/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d2d0aead851b66f5245ec0c4fb2612ef457f8bbafefdf65a2bf9d6bac6140f47", size = 2167237 }, + { url = "https://files.pythonhosted.org/packages/f8/84/6781a1b037f3b96be9227edbd1101f6d3946746056231bf4ac48cdff1a8d/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:2f40e4246676beb31c5ce77c38a55ca4e465c6b38d11ea1bd935420568e0b1ab", size = 2312540 }, + { url = "https://files.pythonhosted.org/packages/3e/db/19c0839feeb728e7df03255581f198dfdf1c2aeb1e174a8420b63c5252e5/pydantic_core-2.46.3-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:cf489cf8986c543939aeee17a09c04d6ffb43bfef8ca16fcbcc5cfdcbed24dba", size = 2369556 }, + { url = "https://files.pythonhosted.org/packages/e0/15/3228774cb7cd45f5f721ddf1b2242747f4eb834d0c491f0c02d606f09fed/pydantic_core-2.46.3-cp314-cp314t-win32.whl", hash = "sha256:ffe0883b56cfc05798bf994164d2b2ff03efe2d22022a2bb080f3b626176dd56", size = 1949756 }, + { url = "https://files.pythonhosted.org/packages/b8/2a/c79cf53fd91e5a87e30d481809f52f9a60dd221e39de66455cf04deaad37/pydantic_core-2.46.3-cp314-cp314t-win_amd64.whl", hash = "sha256:706d9d0ce9cf4593d07270d8e9f53b161f90c57d315aeec4fb4fd7a8b10240d8", size = 2051305 }, + { url = "https://files.pythonhosted.org/packages/0b/db/d8182a7f1d9343a032265aae186eb063fe26ca4c40f256b21e8da4498e89/pydantic_core-2.46.3-cp314-cp314t-win_arm64.whl", hash = "sha256:77706aeb41df6a76568434701e0917da10692da28cb69d5fb6919ce5fdb07374", size = 2026310 }, + { url = "https://files.pythonhosted.org/packages/66/7f/03dbad45cd3aa9083fbc93c210ae8b005af67e4136a14186950a747c6874/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:9715525891ed524a0a1eb6d053c74d4d4ad5017677fb00af0b7c2644a31bae46", size = 2105683 }, + { url = "https://files.pythonhosted.org/packages/26/22/4dc186ac8ea6b257e9855031f51b62a9637beac4d68ac06bee02f046f836/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:9d2f400712a99a013aff420ef1eb9be077f8189a36c1e3ef87660b4e1088a874", size = 1940052 }, + { url = "https://files.pythonhosted.org/packages/0d/ca/d376391a5aff1f2e8188960d7873543608130a870961c2b6b5236627c116/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd2aab0e2e9dc2daf36bd2686c982535d5e7b1d930a1344a7bb6e82baab42a76", size = 1988172 }, + { url = "https://files.pythonhosted.org/packages/0e/6b/523b9f85c23788755d6ab949329de692a2e3a584bc6beb67fef5e035aa9d/pydantic_core-2.46.3-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9d76736da5f362fabfeea6a69b13b7f2be405c6d6966f06b2f6bfff7e64531", size = 2128596 }, + { url = "https://files.pythonhosted.org/packages/34/42/f426db557e8ab2791bc7562052299944a118655496fbff99914e564c0a94/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b12dd51f1187c2eb489af8e20f880362db98e954b54ab792fa5d92e8bcc6b803", size = 2091877 }, + { url = "https://files.pythonhosted.org/packages/5c/4f/86a832a9d14df58e663bfdf4627dc00d3317c2bd583c4fb23390b0f04b8e/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f00a0961b125f1a47af7bcc17f00782e12f4cd056f83416006b30111d941dfa3", size = 1932428 }, + { url = "https://files.pythonhosted.org/packages/11/1a/fe857968954d93fb78e0d4b6df5c988c74c4aaa67181c60be7cfe327c0ca/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57697d7c056aca4bbb680200f96563e841a6386ac1129370a0102592f4dddff5", size = 1997550 }, + { url = "https://files.pythonhosted.org/packages/17/eb/9d89ad2d9b0ba8cd65393d434471621b98912abb10fbe1df08e480ba57b5/pydantic_core-2.46.3-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd35aa21299def8db7ef4fe5c4ff862941a9a158ca7b63d61e66fe67d30416b4", size = 2137657 }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151 }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726 }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669 }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252 }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081 }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159 }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626 }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613 }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115 }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427 }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090 }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246 }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814 }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809 }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454 }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355 }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175 }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228 }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194 }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429 }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912 }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108 }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641 }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901 }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132 }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261 }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272 }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923 }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062 }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341 }, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279 }, + { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645 }, + { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574 }, + { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995 }, + { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070 }, + { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121 }, + { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550 }, + { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184 }, + { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480 }, + { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993 }, + { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436 }, + { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301 }, + { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275 }, + { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469 }, + { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961 }, + { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282 }, + { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468 }, + { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394 }, + { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964 }, + { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029 }, + { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541 }, + { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197 }, + { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175 }, + { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427 }, + { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929 }, + { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193 }, + { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388 }, + { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316 }, + { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472 }, + { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401 }, + { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170 }, +] + +[[package]] +name = "regex" +version = "2026.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273 }, + { url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954 }, + { url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487 }, + { url = "https://files.pythonhosted.org/packages/88/2c/f83b93f85e01168f1070f045a42d4c937b69fdb8dd7ae82d307253f7e36e/regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17", size = 796646 }, + { url = "https://files.pythonhosted.org/packages/df/55/61a2e17bf0c4dc57e11caf8dd11771280d8aaa361785f9e3bc40d653f4a7/regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17", size = 865904 }, + { url = "https://files.pythonhosted.org/packages/45/32/1ac8ed1b5a346b5993a3d256abe0a0f03b0b73c8cc88d928537368ac65b6/regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae", size = 912304 }, + { url = "https://files.pythonhosted.org/packages/26/47/2ee5c613ab546f0eddebf9905d23e07beb933416b1246c2d8791d01979b4/regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e", size = 801126 }, + { url = "https://files.pythonhosted.org/packages/75/cd/41dacd129ca9fd20bd7d02f83e0fad83e034ac8a084ec369c90f55ef37e2/regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d", size = 776772 }, + { url = "https://files.pythonhosted.org/packages/89/6d/5af0b588174cb5f46041fa7dd64d3fd5cd2fe51f18766703d1edc387f324/regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27", size = 785228 }, + { url = "https://files.pythonhosted.org/packages/b7/3b/f5a72b7045bd59575fc33bf1345f156fcfd5a8484aea6ad84b12c5a82114/regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf", size = 860032 }, + { url = "https://files.pythonhosted.org/packages/39/a4/72a317003d6fcd7a573584a85f59f525dfe8f67e355ca74eb6b53d66a5e2/regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0", size = 765714 }, + { url = "https://files.pythonhosted.org/packages/25/1e/5672e16f34dbbcb2560cc7e6a2fbb26dfa8b270711e730101da4423d3973/regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa", size = 852078 }, + { url = "https://files.pythonhosted.org/packages/f7/0d/c813f0af7c6cc7ed7b9558bac2e5120b60ad0fa48f813e4d4bd55446f214/regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b", size = 789181 }, + { url = "https://files.pythonhosted.org/packages/ea/6d/a344608d1adbd2a95090ddd906cec09a11be0e6517e878d02a5123e0917f/regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62", size = 266690 }, + { url = "https://files.pythonhosted.org/packages/31/07/54049f89b46235ca6f45cd6c88668a7050e77d4a15555e47dd40fde75263/regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81", size = 277733 }, + { url = "https://files.pythonhosted.org/packages/0e/21/61366a8e20f4d43fb597708cac7f0e2baadb491ecc9549b4980b2be27d16/regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427", size = 270565 }, + { url = "https://files.pythonhosted.org/packages/f1/1e/3a2b9672433bef02f5d39aa1143ca2c08f311c1d041c464a42be9ae648dc/regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c", size = 494126 }, + { url = "https://files.pythonhosted.org/packages/4e/4b/c132a4f4fe18ad3340d89fcb56235132b69559136036b845be3c073142ed/regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141", size = 293882 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/eaa38092ce7a023656280f2341dbbd4ad5f05d780a70abba7bb4f4bea54c/regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717", size = 292334 }, + { url = "https://files.pythonhosted.org/packages/5f/f6/dd38146af1392dac33db7074ab331cec23cced3759167735c42c5460a243/regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07", size = 811691 }, + { url = "https://files.pythonhosted.org/packages/7a/f0/dc54c2e69f5eeec50601054998ec3690d5344277e782bd717e49867c1d29/regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca", size = 871227 }, + { url = "https://files.pythonhosted.org/packages/a1/af/cb16bd5dc61621e27df919a4449bbb7e5a1034c34d307e0a706e9cc0f3e3/regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520", size = 917435 }, + { url = "https://files.pythonhosted.org/packages/5c/71/8b260897f22996b666edd9402861668f45a2ca259f665ac029e6104a2d7d/regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883", size = 816358 }, + { url = "https://files.pythonhosted.org/packages/1c/60/775f7f72a510ef238254906c2f3d737fc80b16ca85f07d20e318d2eea894/regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b", size = 785549 }, + { url = "https://files.pythonhosted.org/packages/58/42/34d289b3627c03cf381e44da534a0021664188fa49ba41513da0b4ec6776/regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1", size = 801364 }, + { url = "https://files.pythonhosted.org/packages/fc/20/f6ecf319b382a8f1ab529e898b222c3f30600fcede7834733c26279e7465/regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b", size = 866221 }, + { url = "https://files.pythonhosted.org/packages/92/6a/9f16d3609d549bd96d7a0b2aee1625d7512ba6a03efc01652149ef88e74d/regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff", size = 772530 }, + { url = "https://files.pythonhosted.org/packages/fa/f6/aa9768bc96a4c361ac96419fbaf2dcdc33970bb813df3ba9b09d5d7b6d96/regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb", size = 856989 }, + { url = "https://files.pythonhosted.org/packages/4d/b4/c671db3556be2473ae3e4bb7a297c518d281452871501221251ea4ecba57/regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4", size = 803241 }, + { url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921 }, + { url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240 }, + { url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440 }, + { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343 }, + { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909 }, + { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692 }, + { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979 }, + { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744 }, + { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613 }, + { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551 }, + { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911 }, + { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751 }, + { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484 }, + { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939 }, + { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417 }, + { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056 }, + { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130 }, + { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992 }, + { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563 }, + { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191 }, + { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877 }, + { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410 }, + { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831 }, + { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199 }, + { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649 }, + { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388 }, + { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746 }, + { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483 }, + { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331 }, + { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673 }, + { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146 }, + { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463 }, + { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709 }, + { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622 }, + { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773 }, +] + +[[package]] +name = "requests" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947 }, +] + +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "oauthlib" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651 }, +] + +[[package]] +name = "tenacity" +version = "9.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926 }, +] + +[[package]] +name = "tiktoken" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802 }, + { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995 }, + { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948 }, + { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986 }, + { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222 }, + { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097 }, + { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117 }, + { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309 }, + { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712 }, + { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725 }, + { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875 }, + { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451 }, + { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794 }, + { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777 }, + { url = "https://files.pythonhosted.org/packages/72/05/3abc1db5d2c9aadc4d2c76fa5640134e475e58d9fbb82b5c535dc0de9b01/tiktoken-0.12.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a90388128df3b3abeb2bfd1895b0681412a8d7dc644142519e6f0a97c2111646", size = 1050188 }, + { url = "https://files.pythonhosted.org/packages/e3/7b/50c2f060412202d6c95f32b20755c7a6273543b125c0985d6fa9465105af/tiktoken-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:da900aa0ad52247d8794e307d6446bd3cdea8e192769b56276695d34d2c9aa88", size = 993978 }, + { url = "https://files.pythonhosted.org/packages/14/27/bf795595a2b897e271771cd31cb847d479073497344c637966bdf2853da1/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:285ba9d73ea0d6171e7f9407039a290ca77efcdb026be7769dccc01d2c8d7fff", size = 1129271 }, + { url = "https://files.pythonhosted.org/packages/f5/de/9341a6d7a8f1b448573bbf3425fa57669ac58258a667eb48a25dfe916d70/tiktoken-0.12.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:d186a5c60c6a0213f04a7a802264083dea1bbde92a2d4c7069e1a56630aef830", size = 1151216 }, + { url = "https://files.pythonhosted.org/packages/75/0d/881866647b8d1be4d67cb24e50d0c26f9f807f994aa1510cb9ba2fe5f612/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:604831189bd05480f2b885ecd2d1986dc7686f609de48208ebbbddeea071fc0b", size = 1194860 }, + { url = "https://files.pythonhosted.org/packages/b3/1e/b651ec3059474dab649b8d5b69f5c65cd8fcd8918568c1935bd4136c9392/tiktoken-0.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8f317e8530bb3a222547b85a58583238c8f74fd7a7408305f9f63246d1a0958b", size = 1254567 }, + { url = "https://files.pythonhosted.org/packages/80/57/ce64fd16ac390fafde001268c364d559447ba09b509181b2808622420eec/tiktoken-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:399c3dd672a6406719d84442299a490420b458c44d3ae65516302a99675888f3", size = 921067 }, + { url = "https://files.pythonhosted.org/packages/ac/a4/72eed53e8976a099539cdd5eb36f241987212c29629d0a52c305173e0a68/tiktoken-0.12.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2c714c72bc00a38ca969dae79e8266ddec999c7ceccd603cc4f0d04ccd76365", size = 1050473 }, + { url = "https://files.pythonhosted.org/packages/e6/d7/0110b8f54c008466b19672c615f2168896b83706a6611ba6e47313dbc6e9/tiktoken-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cbb9a3ba275165a2cb0f9a83f5d7025afe6b9d0ab01a22b50f0e74fee2ad253e", size = 993855 }, + { url = "https://files.pythonhosted.org/packages/5f/77/4f268c41a3957c418b084dd576ea2fad2e95da0d8e1ab705372892c2ca22/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:dfdfaa5ffff8993a3af94d1125870b1d27aed7cb97aa7eb8c1cefdbc87dbee63", size = 1129022 }, + { url = "https://files.pythonhosted.org/packages/4e/2b/fc46c90fe5028bd094cd6ee25a7db321cb91d45dc87531e2bdbb26b4867a/tiktoken-0.12.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:584c3ad3d0c74f5269906eb8a659c8bfc6144a52895d9261cdaf90a0ae5f4de0", size = 1150736 }, + { url = "https://files.pythonhosted.org/packages/28/c0/3c7a39ff68022ddfd7d93f3337ad90389a342f761c4d71de99a3ccc57857/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:54c891b416a0e36b8e2045b12b33dd66fb34a4fe7965565f1b482da50da3e86a", size = 1194908 }, + { url = "https://files.pythonhosted.org/packages/ab/0d/c1ad6f4016a3968c048545f5d9b8ffebf577774b2ede3e2e352553b685fe/tiktoken-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5edb8743b88d5be814b1a8a8854494719080c28faaa1ccbef02e87354fe71ef0", size = 1253706 }, + { url = "https://files.pythonhosted.org/packages/af/df/c7891ef9d2712ad774777271d39fdef63941ffba0a9d59b7ad1fd2765e57/tiktoken-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f61c0aea5565ac82e2ec50a05e02a6c44734e91b51c10510b084ea1b8e633a71", size = 920667 }, +] + +[[package]] +name = "tornado" +version = "6.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f8/f1/3173dfa4a18db4a9b03e5d55325559dab51ee653763bb8745a75af491286/tornado-6.5.5.tar.gz", hash = "sha256:192b8f3ea91bd7f1f50c06955416ed76c6b72f96779b962f07f911b91e8d30e9", size = 516006 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/77f5097695f4dd8255ecbd08b2a1ed8ba8b953d337804dd7080f199e12bf/tornado-6.5.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:487dc9cc380e29f58c7ab88f9e27cdeef04b2140862e5076a66fb6bb68bb1bfa", size = 445983 }, + { url = "https://files.pythonhosted.org/packages/ab/5e/7625b76cd10f98f1516c36ce0346de62061156352353ef2da44e5c21523c/tornado-6.5.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:65a7f1d46d4bb41df1ac99f5fcb685fb25c7e61613742d5108b010975a9a6521", size = 444246 }, + { url = "https://files.pythonhosted.org/packages/b2/04/7b5705d5b3c0fab088f434f9c83edac1573830ca49ccf29fb83bf7178eec/tornado-6.5.5-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e74c92e8e65086b338fd56333fb9a68b9f6f2fe7ad532645a290a464bcf46be5", size = 447229 }, + { url = "https://files.pythonhosted.org/packages/34/01/74e034a30ef59afb4097ef8659515e96a39d910b712a89af76f5e4e1f93c/tornado-6.5.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:435319e9e340276428bbdb4e7fa732c2d399386d1de5686cb331ec8eee754f07", size = 448192 }, + { url = "https://files.pythonhosted.org/packages/be/00/fe9e02c5a96429fce1a1d15a517f5d8444f9c412e0bb9eadfbe3b0fc55bf/tornado-6.5.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3f54aa540bdbfee7b9eb268ead60e7d199de5021facd276819c193c0fb28ea4e", size = 448039 }, + { url = "https://files.pythonhosted.org/packages/82/9e/656ee4cec0398b1d18d0f1eb6372c41c6b889722641d84948351ae19556d/tornado-6.5.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:36abed1754faeb80fbd6e64db2758091e1320f6bba74a4cf8c09cd18ccce8aca", size = 447445 }, + { url = "https://files.pythonhosted.org/packages/5a/76/4921c00511f88af86a33de770d64141170f1cfd9c00311aea689949e274e/tornado-6.5.5-cp39-abi3-win32.whl", hash = "sha256:dd3eafaaeec1c7f2f8fdcd5f964e8907ad788fe8a5a32c4426fbbdda621223b7", size = 448582 }, + { url = "https://files.pythonhosted.org/packages/2c/23/f6c6112a04d28eed765e374435fb1a9198f73e1ec4b4024184f21faeb1ad/tornado-6.5.5-cp39-abi3-win_amd64.whl", hash = "sha256:6443a794ba961a9f619b1ae926a2e900ac20c34483eea67be4ed8f1e58d3ef7b", size = 448990 }, + { url = "https://files.pythonhosted.org/packages/b7/c8/876602cbc96469911f0939f703453c1157b0c826ecb05bdd32e023397d4e/tornado-6.5.5-cp39-abi3-win_arm64.whl", hash = "sha256:2c9a876e094109333f888539ddb2de4361743e5d21eece20688e3e351e4990a6", size = 448016 }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611 }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321 }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584 }, +] + +[[package]] +name = "uuid-utils" +version = "0.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/d1/38a573f0c631c062cf42fa1f5d021d4dd3c31fb23e4376e4b56b0c9fbbed/uuid_utils-0.14.1.tar.gz", hash = "sha256:9bfc95f64af80ccf129c604fb6b8ca66c6f256451e32bc4570f760e4309c9b69", size = 22195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/b7/add4363039a34506a58457d96d4aa2126061df3a143eb4d042aedd6a2e76/uuid_utils-0.14.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:93a3b5dc798a54a1feb693f2d1cb4cf08258c32ff05ae4929b5f0a2ca624a4f0", size = 604679 }, + { url = "https://files.pythonhosted.org/packages/dd/84/d1d0bef50d9e66d31b2019997c741b42274d53dde2e001b7a83e9511c339/uuid_utils-0.14.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ccd65a4b8e83af23eae5e56d88034b2fe7264f465d3e830845f10d1591b81741", size = 309346 }, + { url = "https://files.pythonhosted.org/packages/ef/ed/b6d6fd52a6636d7c3eddf97d68da50910bf17cd5ac221992506fb56cf12e/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b56b0cacd81583834820588378e432b0696186683b813058b707aedc1e16c4b1", size = 344714 }, + { url = "https://files.pythonhosted.org/packages/a8/a7/a19a1719fb626fe0b31882db36056d44fe904dc0cf15b06fdf56b2679cf7/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb3cf14de789097320a3c56bfdfdd51b1225d11d67298afbedee7e84e3837c96", size = 350914 }, + { url = "https://files.pythonhosted.org/packages/1d/fc/f6690e667fdc3bb1a73f57951f97497771c56fe23e3d302d7404be394d4f/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60e0854a90d67f4b0cc6e54773deb8be618f4c9bad98d3326f081423b5d14fae", size = 482609 }, + { url = "https://files.pythonhosted.org/packages/54/6e/dcd3fa031320921a12ec7b4672dea3bd1dd90ddffa363a91831ba834d559/uuid_utils-0.14.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce6743ba194de3910b5feb1a62590cd2587e33a73ab6af8a01b642ceb5055862", size = 345699 }, + { url = "https://files.pythonhosted.org/packages/04/28/e5220204b58b44ac0047226a9d016a113fde039280cc8732d9e6da43b39f/uuid_utils-0.14.1-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:043fb58fde6cf1620a6c066382f04f87a8e74feb0f95a585e4ed46f5d44af57b", size = 372205 }, + { url = "https://files.pythonhosted.org/packages/c7/d9/3d2eb98af94b8dfffc82b6a33b4dfc87b0a5de2c68a28f6dde0db1f8681b/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c915d53f22945e55fe0d3d3b0b87fd965a57f5fd15666fd92d6593a73b1dd297", size = 521836 }, + { url = "https://files.pythonhosted.org/packages/a8/15/0eb106cc6fe182f7577bc0ab6e2f0a40be247f35c5e297dbf7bbc460bd02/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:0972488e3f9b449e83f006ead5a0e0a33ad4a13e4462e865b7c286ab7d7566a3", size = 625260 }, + { url = "https://files.pythonhosted.org/packages/3c/17/f539507091334b109e7496830af2f093d9fc8082411eafd3ece58af1f8ba/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1c238812ae0c8ffe77d8d447a32c6dfd058ea4631246b08b5a71df586ff08531", size = 587824 }, + { url = "https://files.pythonhosted.org/packages/2e/c2/d37a7b2e41f153519367d4db01f0526e0d4b06f1a4a87f1c5dfca5d70a8b/uuid_utils-0.14.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:bec8f8ef627af86abf8298e7ec50926627e29b34fa907fcfbedb45aaa72bca43", size = 551407 }, + { url = "https://files.pythonhosted.org/packages/65/36/2d24b2cbe78547c6532da33fb8613debd3126eccc33a6374ab788f5e46e9/uuid_utils-0.14.1-cp39-abi3-win32.whl", hash = "sha256:b54d6aa6252d96bac1fdbc80d26ba71bad9f220b2724d692ad2f2310c22ef523", size = 183476 }, + { url = "https://files.pythonhosted.org/packages/83/92/2d7e90df8b1a69ec4cff33243ce02b7a62f926ef9e2f0eca5a026889cd73/uuid_utils-0.14.1-cp39-abi3-win_amd64.whl", hash = "sha256:fc27638c2ce267a0ce3e06828aff786f91367f093c80625ee21dad0208e0f5ba", size = 187147 }, + { url = "https://files.pythonhosted.org/packages/d9/26/529f4beee17e5248e37e0bc17a2761d34c0fa3b1e5729c88adb2065bae6e/uuid_utils-0.14.1-cp39-abi3-win_arm64.whl", hash = "sha256:b04cb49b42afbc4ff8dbc60cf054930afc479d6f4dd7f1ec3bbe5dbfdde06b7a", size = 188132 }, +] + +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189 }, +] + +[[package]] +name = "workshop" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "agent-framework-core" }, + { name = "agent-framework-foundry-hosting" }, + { name = "agent-framework-openai" }, + { name = "azure-ai-projects" }, + { name = "azure-cosmos" }, + { name = "azure-identity" }, + { name = "azure-keyvault" }, + { name = "azure-mgmt-apimanagement" }, + { name = "azure-monitor-opentelemetry" }, + { name = "ipykernel" }, + { name = "langchain" }, + { name = "langchain-openai" }, + { name = "matplotlib" }, + { name = "openai" }, + { name = "opentelemetry-semantic-conventions-ai" }, + { name = "pandas" }, +] + +[package.metadata] +requires-dist = [ + { name = "agent-framework-core", specifier = ">=1.2.0" }, + { name = "agent-framework-foundry-hosting", specifier = ">=1.0.0a260507" }, + { name = "agent-framework-openai", specifier = ">=1.2.0" }, + { name = "azure-ai-projects", specifier = ">=2.1.0" }, + { name = "azure-cosmos", specifier = ">=4.9.0" }, + { name = "azure-identity", specifier = ">=1.25.3" }, + { name = "azure-keyvault", specifier = ">=4.2.0" }, + { name = "azure-mgmt-apimanagement", specifier = ">=5.0.0" }, + { name = "azure-monitor-opentelemetry", specifier = ">=1.6.0" }, + { name = "ipykernel", specifier = ">=7.2.0" }, + { name = "langchain", specifier = ">=1.2.15" }, + { name = "langchain-openai", specifier = ">=1.2.1" }, + { name = "matplotlib", specifier = ">=3.10.9" }, + { name = "openai", specifier = ">=2.32.0" }, + { name = "opentelemetry-semantic-conventions-ai", specifier = ">=0.5.1" }, + { name = "pandas", specifier = ">=3.0.2" }, +] + +[[package]] +name = "wrapt" +version = "1.17.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003 }, + { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025 }, + { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108 }, + { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072 }, + { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214 }, + { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105 }, + { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766 }, + { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711 }, + { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885 }, + { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896 }, + { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132 }, + { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091 }, + { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172 }, + { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163 }, + { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963 }, + { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945 }, + { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857 }, + { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178 }, + { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310 }, + { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266 }, + { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544 }, + { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283 }, + { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366 }, + { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571 }, + { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094 }, + { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659 }, + { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946 }, + { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717 }, + { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334 }, + { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471 }, + { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591 }, +] + +[[package]] +name = "wsproto" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/79/12135bdf8b9c9367b8701c2c19a14c913c120b882d50b014ca0d38083c2c/wsproto-1.3.2.tar.gz", hash = "sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294", size = 50116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl", hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405 }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813 }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121 }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624 }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638 }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323 }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362 }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874 }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185 }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033 }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140 }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291 }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532 }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990 }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876 }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495 }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331 }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037 }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744 }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406 }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125 }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555 }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668 }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524 }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768 }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576 }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123 }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491 }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793 }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993 }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887 }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407 }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119 }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751 }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961 }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703 }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894 }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357 }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600 }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980 }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840 }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966 }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784 }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932 }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644 }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003 }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655 }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664 }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317 }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457 }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894 }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409 }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135 }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379 }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447 }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660 }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076 }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990 }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590 }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442 }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356 }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898 }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519 }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191 }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604 }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271 }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284 }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701 }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646 }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125 }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633 }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899 }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116 }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012 }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534 }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217 }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906 }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057 }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886 }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015 }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457 }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738 }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127 }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962 }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643 }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522 }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807 }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796 }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547 }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854 }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351 }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711 }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014 }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557 }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559 }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502 }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027 }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369 }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565 }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813 }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632 }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895 }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356 }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515 }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785 }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719 }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690 }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851 }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874 }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710 }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033 }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817 }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482 }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949 }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839 }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696 }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865 }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234 }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295 }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784 }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313 }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932 }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786 }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455 }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752 }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291 }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026 }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355 }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417 }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422 }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915 }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690 }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750 }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685 }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009 }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033 }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483 }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175 }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871 }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093 }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384 }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019 }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894 }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979 }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943 }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786 }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307 }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904 }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728 }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964 }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882 }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797 }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023 }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227 }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302 }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202 }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558 }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610 }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041 }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288 }, +] + +[[package]] +name = "zipp" +version = "3.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378 }, +] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735 }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440 }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070 }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001 }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120 }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230 }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173 }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736 }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368 }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022 }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889 }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952 }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054 }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113 }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936 }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232 }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671 }, + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887 }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658 }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849 }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095 }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751 }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818 }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402 }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108 }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248 }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330 }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123 }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591 }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513 }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118 }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940 }, +] diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md b/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md index d6c17e5c4..2ff39e0d4 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/docs/phase4-evidence-tables.md @@ -2,7 +2,7 @@ **Date Generated:** 2026-08-20 **Scope:** Citadel Agentic Governance Hub → MicroHack Adaptation -**Validation Method:** Exhaustive local file inspection (Citadel workshop cloned to `reference/ai-hub-gateway-solution-accelerator/`) +**Validation Method:** Exhaustive local file inspection (Citadel workshop copied to `challenges/`) **Evidence Quality:** ALL data source-backed with exact file paths and line numbers **Target:** Full workshop parity (Notebooks 1–9 unchanged) @@ -10,7 +10,7 @@ ## Source Files Location -All evidence extracted from: `reference/ai-hub-gateway-solution-accelerator/` +All evidence extracted from: `challenges/` (originally cloned from the Citadel workshop repo) Inspection method: Local grep + file reading (NOT GitHub API inferences) --- diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep index 44a075901..f2b0dffde 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.bicep @@ -217,7 +217,7 @@ var aiFoundryModels = [ { name: 'gpt-4.1' publisher: 'OpenAI' - version: '2025-04-01-preview' + version: '2025-04-14' sku: 'GlobalStandard' capacity: 100 retirementDate: '2026-10-14' @@ -239,12 +239,12 @@ var aiFoundryModels = [ retirementDate: '2027-02-05' } { - name: 'DeepSeek-R1' - publisher: 'DeepSeek' + name: 'text-embedding-3-large' + publisher: 'OpenAI' version: '1' sku: 'GlobalStandard' - capacity: 1 - retirementDate: '2099-12-30' + capacity: 100 + retirementDate: '2027-04-14' } { name: 'Mistral-Large-3' @@ -257,23 +257,17 @@ var aiFoundryModels = [ { name: 'Phi-4' publisher: 'Microsoft' - version: '1' + version: '7' sku: 'GlobalStandard' capacity: 1 retirementDate: '2099-10-14' } - { - name: 'text-embedding-3-large' - publisher: 'OpenAI' - version: '1' - sku: 'GlobalStandard' - capacity: 100 - retirementDate: '2027-04-14' - } ] // ===== Phase 4: Model Deployments to Hub Foundry Account ===== // Each model deployed to support Notebooks 1-6 +// @batchSize(1) serializes deployments to avoid Cognitive Services account conflicts +@batchSize(1) resource hubFoundryModelDeployments 'Microsoft.CognitiveServices/accounts/deployments@2025-04-01-preview' = [for (model, index) in aiFoundryModels: { parent: hubFoundryAccount name: replace(model.name, '.', '-') @@ -283,7 +277,7 @@ resource hubFoundryModelDeployments 'Microsoft.CognitiveServices/accounts/deploy } properties: { model: { - format: 'OpenAI' + format: model.publisher name: model.name version: model.version } @@ -296,7 +290,7 @@ resource apimNamedValueUamiClientId 'Microsoft.ApiManagement/service/namedValues parent: apim name: 'uami-client-id' properties: { - displayName: 'Hub Foundry UAMI Client ID' + displayName: 'Hub.Foundry.UAMI.Client.ID' value: apimMI.properties.clientId secret: false } @@ -312,12 +306,6 @@ resource apimBackendHubFoundry 'Microsoft.ApiManagement/service/backends@2023-09 description: 'Hub Foundry AI Services backend for model inference via managed identity' url: 'https://${hubFoundryAccountName}.openai.azure.com' protocol: 'http' - credentials: { - authorization: { - scheme: 'Bearer' - parameter: '@{context.Variables["hubFoundryAccessToken"]}' - } - } circuitBreaker: { rules: [ { @@ -331,22 +319,22 @@ resource apimBackendHubFoundry 'Microsoft.ApiManagement/service/backends@2023-09 ] } } - { - name: 'clientErrorRule' - tripDuration: 'PT1M' - failureCondition: { - count: 10 - interval: 'PT1M' - statusCodeRanges: [ - { min: 400, max: 499 } - ] - } - } ] } } } +// RBAC: allow the APIM managed identity to call the hub Foundry account (Cognitive Services OpenAI User) +resource roleAssignmentApimMiOpenAiUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(hubFoundryAccount.id, apimMI.id, 'CognitiveServicesOpenAIUser') + scope: hubFoundryAccount + properties: { + principalId: apimMI.properties.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') + } +} + // ===== Phase 4: APIM Policy Fragments ===== // Policy fragment: Extract available models from deployment list resource policyFragmentGetAvailableModels 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { @@ -354,11 +342,12 @@ resource policyFragmentGetAvailableModels 'Microsoft.ApiManagement/service/polic name: 'get-available-models' properties: { format: 'xml' - value: 'application/json@{var models = new JArray(); models.Add("gpt-4.1"); models.Add("gpt-5.4-mini"); models.Add("gpt-5.2"); models.Add("DeepSeek-R1"); models.Add("Mistral-Large-3"); models.Add("Phi-4"); models.Add("text-embedding-3-large"); return models.ToString();}' + value: 'application/json@{var models = new JArray(); models.Add("gpt-4.1"); models.Add("gpt-5.4-mini"); models.Add("gpt-5.2"); models.Add("text-embedding-3-large"); models.Add("Mistral-Large-3"); models.Add("Phi-4"); return models.ToString();}' } } -// Policy fragment: Validate model access per subscription +// Policy fragment: Validate model access per subscription (DISABLED - XML syntax pending) +/* resource policyFragmentValidateModelAccess 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'validate-model-access' @@ -367,8 +356,10 @@ resource policyFragmentValidateModelAccess 'Microsoft.ApiManagement/service/poli value: '' } } +*/ -// Policy fragment: Set backend pools for model routing +// Policy fragment: Set backend pools for model routing (DISABLED) +/* resource policyFragmentSetBackendPools 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'set-backend-pools' @@ -377,8 +368,10 @@ resource policyFragmentSetBackendPools 'Microsoft.ApiManagement/service/policyFr value: '' } } +*/ -// Policy fragment: Set backend authorization +// Policy fragment: Set backend authorization (DISABLED - XML syntax pending) +/* resource policyFragmentSetBackendAuthorization 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'set-backend-authorization' @@ -387,8 +380,10 @@ resource policyFragmentSetBackendAuthorization 'Microsoft.ApiManagement/service/ value: '' } } +*/ -// Policy fragment: Set target backend pool +// Policy fragment: Set target backend pool (DISABLED - requires authorization to work) +/* resource policyFragmentSetTargetBackendPool 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'set-target-backend-pool' @@ -397,8 +392,10 @@ resource policyFragmentSetTargetBackendPool 'Microsoft.ApiManagement/service/pol value: '' } } +*/ -// Policy fragment: Extract LLM requested model +// Policy fragment: Extract LLM requested model (DISABLED) +/* resource policyFragmentSetLlmRequestedModel 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'set-llm-requested-model' @@ -407,8 +404,10 @@ resource policyFragmentSetLlmRequestedModel 'Microsoft.ApiManagement/service/pol value: '' } } +*/ -// Policy fragment: Track LLM usage +// Policy fragment: Track LLM usage (DISABLED) +/* resource policyFragmentSetLlmUsage 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'set-llm-usage' @@ -417,8 +416,10 @@ resource policyFragmentSetLlmUsage 'Microsoft.ApiManagement/service/policyFragme value: '' } } +*/ -// Policy fragment: PII Anonymization +// Policy fragment: PII Anonymization (DISABLED) +/* resource policyFragmentPiiAnonymization 'Microsoft.ApiManagement/service/policyFragments@2023-09-01-preview' = { parent: apim name: 'pii-anonymization' @@ -447,6 +448,7 @@ resource policyFragmentPiiStateSaving 'Microsoft.ApiManagement/service/policyFra value: 'https://${cosmosAccountName}.documents.azure.com/dbs/${cosmosDatabaseName}/colls/pii-usage-container/docsPOST' } } +*/ // ===== Phase 4: APIM API - Universal LLM API ===== // Universal endpoint for all LLM backends (/models/*) @@ -480,6 +482,17 @@ resource apiUniversalLlmOpGetModels 'Microsoft.ApiManagement/service/apis/operat } } +// Universal LLM API: Operation policy - GET /models/models +// Simplest possible policy: static JSON list of the 6 deployed models, no backend call +resource apiUniversalLlmOpGetModelsPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2023-09-01-preview' = { + parent: apiUniversalLlmOpGetModels + name: 'policy' + properties: { + format: 'xml' + value: 'application/json["gpt-4.1","gpt-5.4-mini","gpt-5.2","text-embedding-3-large","Mistral-Large-3","Phi-4"]' + } +} + // Universal LLM API: Operation - POST /models/chat/completions resource apiUniversalLlmOpChatCompletions 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { parent: apiUniversalLlm @@ -492,6 +505,23 @@ resource apiUniversalLlmOpChatCompletions 'Microsoft.ApiManagement/service/apis/ } } +// Model-routing policy: read the requested "model" from the request body, validate it +// against the chat-capable models deployed to backend-hub-foundry, then route to that +// model's own deployment. Auth via APIM managed identity. No access-contract/token-limit/ +// PII fragments are referenced here - those remain disabled. +resource apiUniversalLlmOpChatCompletionsPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2023-09-01-preview' = { + parent: apiUniversalLlmOpChatCompletions + name: 'policy' + properties: { + format: 'xml' + value: '@("Bearer " + (string)context.Variables["msi-access-token"])application/json@{return new JObject(new JProperty("error","unsupported model requested for chat completions"),new JProperty("requestedModel",(string)context.Variables["requestedModel"])).ToString();}' + } + dependsOn: [ + apimNamedValueUamiClientId + apimBackendHubFoundry + ] +} + // Universal LLM API: Operation - POST /models/embeddings resource apiUniversalLlmOpEmbeddings 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { parent: apiUniversalLlm @@ -504,6 +534,21 @@ resource apiUniversalLlmOpEmbeddings 'Microsoft.ApiManagement/service/apis/opera } } +// Embeddings policy: same shape as chat-completions, but only one embedding model is +// deployed (text-embedding-3-large), so routing is a straight validate-then-route. +resource apiUniversalLlmOpEmbeddingsPolicy 'Microsoft.ApiManagement/service/apis/operations/policies@2023-09-01-preview' = { + parent: apiUniversalLlmOpEmbeddings + name: 'policy' + properties: { + format: 'xml' + value: '@("Bearer " + (string)context.Variables["msi-access-token"])application/json@{return new JObject(new JProperty("error","unsupported model requested for embeddings"),new JProperty("requestedModel",(string)context.Variables["requestedModel"])).ToString();}' + } + dependsOn: [ + apimNamedValueUamiClientId + apimBackendHubFoundry + ] +} + // Universal LLM API: Operation - POST /models/responses (response tracking) resource apiUniversalLlmOpPostResponses 'Microsoft.ApiManagement/service/apis/operations@2023-09-01-preview' = { parent: apiUniversalLlm @@ -1131,28 +1176,28 @@ output SPOKE_FOUNDRY_PROJECT_ENDPOINT string = 'https://${spokeFoundryAccountNam output APIM_GATEWAY_URL string = 'https://${apim.name}.azure-api.net' // Phase 4: Subscription keys for products (for HackboxCredential distribution to notebooks) -output SALES_ASSISTANT_SUBSCRIPTION_KEY string = subscriptionSalesAssistant.properties.primaryKey +output SALES_ASSISTANT_SUBSCRIPTION_KEY string = listSecrets(subscriptionSalesAssistant.id, '2023-09-01-preview').primaryKey output SALES_ASSISTANT_SUBSCRIPTION_ID string = subscriptionSalesAssistant.name -output HR_CHATAGENT_SUBSCRIPTION_KEY string = subscriptionHrChatAgent.properties.primaryKey +output HR_CHATAGENT_SUBSCRIPTION_KEY string = listSecrets(subscriptionHrChatAgent.id, '2023-09-01-preview').primaryKey output HR_CHATAGENT_SUBSCRIPTION_ID string = subscriptionHrChatAgent.name -output SUPPORT_BOT_SUBSCRIPTION_KEY string = subscriptionSupportBot.properties.primaryKey +output SUPPORT_BOT_SUBSCRIPTION_KEY string = listSecrets(subscriptionSupportBot.id, '2023-09-01-preview').primaryKey output SUPPORT_BOT_SUBSCRIPTION_ID string = subscriptionSupportBot.name -output UNIVERSAL_LLM_TEST_SUBSCRIPTION_KEY string = subscriptionUniversalLlmTest.properties.primaryKey +output UNIVERSAL_LLM_TEST_SUBSCRIPTION_KEY string = listSecrets(subscriptionUniversalLlmTest.id, '2023-09-01-preview').primaryKey output UNIVERSAL_LLM_TEST_SUBSCRIPTION_ID string = subscriptionUniversalLlmTest.name -output UNIFIED_AI_TEST_SUBSCRIPTION_KEY string = subscriptionUnifiedAiTest.properties.primaryKey +output UNIFIED_AI_TEST_SUBSCRIPTION_KEY string = listSecrets(subscriptionUnifiedAiTest.id, '2023-09-01-preview').primaryKey output UNIFIED_AI_TEST_SUBSCRIPTION_ID string = subscriptionUnifiedAiTest.name -output PII_MASKING_SUBSCRIPTION_KEY string = subscriptionHrPiiMasking.properties.primaryKey +output PII_MASKING_SUBSCRIPTION_KEY string = listSecrets(subscriptionHrPiiMasking.id, '2023-09-01-preview').primaryKey output PII_MASKING_SUBSCRIPTION_ID string = subscriptionHrPiiMasking.name -output PII_BLOCKING_SUBSCRIPTION_KEY string = subscriptionCompliancePiiBlocking.properties.primaryKey +output PII_BLOCKING_SUBSCRIPTION_KEY string = listSecrets(subscriptionCompliancePiiBlocking.id, '2023-09-01-preview').primaryKey output PII_BLOCKING_SUBSCRIPTION_ID string = subscriptionCompliancePiiBlocking.name -output PII_ANALYTICS_SUBSCRIPTION_KEY string = subscriptionHrPiiAnalytics.properties.primaryKey +output PII_ANALYTICS_SUBSCRIPTION_KEY string = listSecrets(subscriptionHrPiiAnalytics.id, '2023-09-01-preview').primaryKey output PII_ANALYTICS_SUBSCRIPTION_ID string = subscriptionHrPiiAnalytics.name // Phase 4: LLM Backend Config (JSON array for Notebook 1 dynamic discovery) @@ -1176,10 +1221,10 @@ output LLM_BACKEND_CONFIG string = base64(string([ models: [ 'gpt-5.2' ] } { - name: 'DeepSeek-R1' + name: 'text-embedding-3-large' publisher: 'Azure OpenAI' endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' - models: [ 'DeepSeek-R1' ] + models: [ 'text-embedding-3-large' ] } { name: 'Mistral-Large-3' @@ -1193,12 +1238,6 @@ output LLM_BACKEND_CONFIG string = base64(string([ endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' models: [ 'Phi-4' ] } - { - name: 'text-embedding-3-large' - publisher: 'Azure OpenAI' - endpoint: 'https://${hubFoundryAccountName}.openai.azure.com' - models: [ 'text-embedding-3-large' ] - } ])) output COSMOS_ENDPOINT string = cosmosAccount.properties.documentEndpoint diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.json b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.json new file mode 100644 index 000000000..4aed55d5d --- /dev/null +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/infra/resources.json @@ -0,0 +1,1356 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.41.2.15936", + "templateHash": "1184583719695337759" + } + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Azure region for all resources. Validated for Foundry model availability." + } + }, + "resourceToken": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Deterministic suffix for resource naming (DNS-safe, lowercase)." + } + }, + "principalId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Primary Entra user object ID for initial RBAC grants. Multi-user RBAC is applied post-deployment." + } + }, + "principalType": { + "type": "string", + "defaultValue": "User", + "allowedValues": [ + "User", + "ServicePrincipal" + ], + "metadata": { + "description": "Principal type for RBAC assignments: User (interactive) or ServicePrincipal (CI/CD)." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags to apply to all resources." + } + } + }, + "variables": { + "hubFoundryAccountName": "[format('aif-hub-{0}', parameters('resourceToken'))]", + "hubFoundryProjectName": "citadel-hub-project", + "apimName": "[format('apim-citadel-{0}', parameters('resourceToken'))]", + "cosmosAccountName": "[format('cos-citadel-{0}', parameters('resourceToken'))]", + "eventHubNamespace": "[format('evh-citadel-{0}', parameters('resourceToken'))]", + "eventHubName": "usage-events", + "keyVaultName": "[format('kv-hub-{0}', parameters('resourceToken'))]", + "laName": "[format('law-citadel-{0}', parameters('resourceToken'))]", + "aiName": "[format('appi-citadel-{0}', parameters('resourceToken'))]", + "storageAccountName": "[format('stcitadel{0}', replace(parameters('resourceToken'), '-', ''))]", + "logicAppName": "[format('la-usage-ingestion-{0}', parameters('resourceToken'))]", + "apimMIName": "[format('mi-apim-{0}', parameters('resourceToken'))]", + "logicAppMIName": "[format('mi-logicapp-{0}', parameters('resourceToken'))]", + "spokeFoundryAccountName": "[format('aif-spoke-{0}', parameters('resourceToken'))]", + "spokeFoundryProjectName": "citadel-agents-project", + "spokeLaName": "[format('law-spoke-{0}', parameters('resourceToken'))]", + "spokeAiName": "[format('appi-spoke-{0}', parameters('resourceToken'))]", + "spokeKeyVaultName": "[format('kv-spoke-{0}', parameters('resourceToken'))]", + "spokeAcrName": "[format('acrcitadel{0}', replace(parameters('resourceToken'), '-', ''))]", + "cosmosApi": "2024-11-15", + "aiServicesApi": "2025-04-01-preview", + "cosmosDatabaseName": "citadel-hub", + "deployPolicyFragments": false, + "cosmosContainers": [ + { + "name": "usage-events", + "partitionKey": "/eventId" + }, + { + "name": "contracts", + "partitionKey": "/contractId" + }, + { + "name": "models", + "partitionKey": "/modelId" + }, + { + "name": "config", + "partitionKey": "/configId" + } + ], + "cosmosContainersPhase4": "[concat(variables('cosmosContainers'), createArray(createObject('name', 'ai-usage-container', 'partitionKey', '/productName'), createObject('name', 'pii-usage-container', 'partitionKey', '/type'), createObject('name', 'llm-usage-container', 'partitionKey', '/productName')))]", + "aiFoundryModels": [ + { + "name": "gpt-4.1", + "publisher": "OpenAI", + "version": "2025-04-14", + "sku": "GlobalStandard", + "capacity": 100, + "retirementDate": "2026-10-14" + }, + { + "name": "gpt-5.4-mini", + "publisher": "OpenAI", + "version": "2026-03-17", + "sku": "GlobalStandard", + "capacity": 100, + "retirementDate": "2026-09-30" + }, + { + "name": "gpt-5.2", + "publisher": "OpenAI", + "version": "2025-12-11", + "sku": "GlobalStandard", + "capacity": 100, + "retirementDate": "2027-02-05" + }, + { + "name": "text-embedding-3-large", + "publisher": "OpenAI", + "version": "1", + "sku": "GlobalStandard", + "capacity": 100, + "retirementDate": "2027-04-14" + }, + { + "name": "Mistral-Large-3", + "publisher": "Mistral AI", + "version": "1", + "sku": "GlobalStandard", + "capacity": 100, + "retirementDate": "2099-12-30" + }, + { + "name": "Phi-4", + "publisher": "Microsoft", + "version": "7", + "sku": "GlobalStandard", + "capacity": 1, + "retirementDate": "2099-10-14" + } + ] + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[variables('apimMIName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[variables('logicAppMIName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[variables('laName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "sku": { + "name": "PerGB2018" + }, + "retentionInDays": 30 + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[variables('aiName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', variables('laName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', variables('laName'))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[variables('keyVaultName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "enableRbacAuthorization": true, + "publicNetworkAccess": "Enabled" + } + }, + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[variables('hubFoundryAccountName')]", + "location": "[parameters('location')]", + "kind": "AIServices", + "tags": "[parameters('tags')]", + "sku": { + "name": "S0" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "customSubDomainName": "[variables('hubFoundryAccountName')]", + "publicNetworkAccess": "Enabled", + "allowProjectManagement": true + } + }, + { + "type": "Microsoft.CognitiveServices/accounts/projects", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', variables('hubFoundryAccountName'), variables('hubFoundryProjectName'))]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "description": "Citadel Agentic Governance Hub - Primary project for governance demonstrations" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', variables('hubFoundryAccountName'))]" + ] + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[variables('cosmosAccountName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "GlobalDocumentDB", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "databaseAccountOfferType": "Standard", + "disableLocalAuth": true, + "publicNetworkAccess": "Enabled", + "capabilities": [ + { + "name": "EnableServerless" + } + ], + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0 + } + ] + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', variables('cosmosAccountName'), variables('cosmosDatabaseName'))]", + "properties": { + "resource": { + "id": "[variables('cosmosDatabaseName')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts', variables('cosmosAccountName'))]" + ] + }, + { + "copy": { + "name": "cosmosContainers_resource", + "count": "[length(variables('cosmosContainersPhase4'))]" + }, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', variables('cosmosAccountName'), variables('cosmosDatabaseName'), variables('cosmosContainersPhase4')[copyIndex()].name)]", + "properties": { + "resource": { + "id": "[variables('cosmosContainersPhase4')[copyIndex()].name]", + "partitionKey": { + "paths": [ + "[variables('cosmosContainersPhase4')[copyIndex()].partitionKey]" + ], + "kind": "Hash" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', variables('cosmosAccountName'), variables('cosmosDatabaseName'))]" + ] + }, + { + "copy": { + "name": "hubFoundryModelDeployments", + "count": "[length(variables('aiFoundryModels'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', variables('hubFoundryAccountName'), replace(variables('aiFoundryModels')[copyIndex()].name, '.', '-'))]", + "sku": { + "name": "[variables('aiFoundryModels')[copyIndex()].sku]", + "capacity": "[variables('aiFoundryModels')[copyIndex()].capacity]" + }, + "properties": { + "model": { + "format": "[variables('aiFoundryModels')[copyIndex()].publisher]", + "name": "[variables('aiFoundryModels')[copyIndex()].name]", + "version": "[variables('aiFoundryModels')[copyIndex()].version]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', variables('hubFoundryAccountName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/namedValues", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'uami-client-id')]", + "properties": { + "displayName": "Hub.Foundry.UAMI.Client.ID", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('apimMIName')), '2023-01-31').clientId]", + "secret": false + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('apimMIName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/backends", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'backend-hub-foundry')]", + "properties": { + "title": "Hub Foundry Backend", + "description": "Hub Foundry AI Services backend for model inference via managed identity", + "url": "[format('https://{0}.openai.azure.com', variables('hubFoundryAccountName'))]", + "protocol": "http", + "circuitBreaker": { + "rules": [ + { + "name": "serverErrorRule", + "tripDuration": "PT1M", + "failureCondition": { + "count": 5, + "interval": "PT1M", + "statusCodeRanges": [ + { + "min": 500, + "max": 599 + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.CognitiveServices/accounts/projects', variables('hubFoundryAccountName'), variables('hubFoundryProjectName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/policyFragments", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'get-available-models')]", + "properties": { + "format": "xml", + "value": "application/json@{var models = new JArray(); models.Add(\"gpt-4.1\"); models.Add(\"gpt-5.4-mini\"); models.Add(\"gpt-5.2\"); models.Add(\"Mistral-Large-3\"); models.Add(\"Phi-4\"); models.Add(\"text-embedding-3-large\"); return models.ToString();}" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'universal-llm-api')]", + "properties": { + "displayName": "Universal LLM API", + "description": "Gateway for Azure OpenAI and AI Foundry models - supports GPT, embedding, and other inference types", + "path": "models", + "protocols": [ + "https" + ], + "subscriptionRequired": true, + "subscriptionKeyParameterNames": { + "header": "api-key", + "query": "api-key" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'get-models')]", + "properties": { + "displayName": "List Available Models", + "method": "GET", + "urlTemplate": "/models", + "description": "Returns list of available model deployments" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'post-chat-completions')]", + "properties": { + "displayName": "Create Chat Completion", + "method": "POST", + "urlTemplate": "/chat/completions", + "description": "Generate chat completions using requested model" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'post-embeddings')]", + "properties": { + "displayName": "Create Embeddings", + "method": "POST", + "urlTemplate": "/embeddings", + "description": "Generate embeddings using text-embedding-3-large" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'post-responses')]", + "properties": { + "displayName": "Record LLM Response", + "method": "POST", + "urlTemplate": "/responses", + "description": "Log LLM response for audit trail" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'get-response')]", + "properties": { + "displayName": "Get Response", + "method": "GET", + "urlTemplate": "/responses/{response_id}", + "templateParameters": [ + { + "name": "response_id", + "required": true, + "type": "string", + "description": "Response UUID" + } + ], + "description": "Retrieve stored LLM response" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'universal-llm-api', 'get-response-input-items')]", + "properties": { + "displayName": "Get Response Input Items", + "method": "GET", + "urlTemplate": "/responses/{response_id}/input_items", + "templateParameters": [ + { + "name": "response_id", + "required": true, + "type": "string", + "description": "Response UUID" + } + ], + "description": "Retrieve input items for stored response" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'universal-llm-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'unified-ai-api')]", + "properties": { + "displayName": "Unified AI API", + "description": "Multi-backend routing for Azure OpenAI, AI Foundry, and Gemini (Notebook 6 support)", + "path": "unified-ai", + "protocols": [ + "https" + ], + "subscriptionRequired": true, + "subscriptionKeyParameterNames": { + "header": "api-key", + "query": "api-key" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'unified-ai-api', 'get-deployments')]", + "properties": { + "displayName": "List Deployments", + "method": "GET", + "urlTemplate": "/deployments", + "description": "Returns available deployments across backends" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'unified-ai-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'unified-ai-api', 'get-deployment')]", + "properties": { + "displayName": "Get Deployment Details", + "method": "GET", + "urlTemplate": "/deployments/{model_name}", + "templateParameters": [ + { + "name": "model_name", + "required": true, + "type": "string", + "description": "Model deployment name" + } + ], + "description": "Get details for specific deployment" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'unified-ai-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'unified-ai-api', 'post-openai-chat-completions')]", + "properties": { + "displayName": "OpenAI Chat Completions", + "method": "POST", + "urlTemplate": "/openai/deployments/{model}/chat/completions", + "templateParameters": [ + { + "name": "model", + "required": true, + "type": "string", + "description": "Model deployment name (gpt-5.2, Mistral-Large-3, etc.)" + } + ], + "description": "Create chat completions in OpenAI format" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'unified-ai-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'unified-ai-api', 'post-foundry-chat-completions')]", + "properties": { + "displayName": "Foundry Inference API Chat Completions", + "method": "POST", + "urlTemplate": "/models/chat/completions", + "description": "Create chat completions in AI Foundry inference format" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'unified-ai-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/operations", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'unified-ai-api', 'post-gemini-chat-completions')]", + "properties": { + "displayName": "Gemini Chat Completions (Optional)", + "method": "POST", + "urlTemplate": "/v1beta/openai/chat/completions", + "description": "Create chat completions using Google Gemini (optional, deferred to Phase 5)" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', variables('apimName'), 'unified-ai-api')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-sales-assistant-dev')]", + "properties": { + "displayName": "LLM-Sales-Assistant-DEV", + "description": "Access contract for Sales Assistant use case - Chat + embedding models (Notebook 3)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Access to gpt-4.1, gpt-5.4-mini, text-embedding-3-large at 3000 TPM" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-sales-assistant-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-sales-assistant-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-chatagent-dev')]", + "properties": { + "displayName": "LLM-HR-ChatAgent-DEV", + "description": "Access contract for HR ChatAgent - High throughput agent use case (Notebooks 3, 4, 8, 9)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Access to gpt-4.1, gpt-5.4-mini at 5000 TPM with 500K monthly quota" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-hr-chatagent-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-chatagent-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-support-bot-dev')]", + "properties": { + "displayName": "LLM-Support-Bot-DEV", + "description": "Access contract for Support Bot - Alternative models with daily quota (Notebook 3)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Access to gpt-4.1, Mistral-Large-3 at 2000 TPM with 50K daily quota" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-support-bot-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-support-bot-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-testing-universalllm-dev')]", + "properties": { + "displayName": "LLM-Testing-UniversalLLMAllModels-DEV", + "description": "Test product with access to all deployed models (Notebook 2)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Unrestricted access to all deployed models for testing" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-testing-universalllm-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-universalllm-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-testing-unifiedai-dev')]", + "properties": { + "displayName": "LLM-Testing-UnifiedAI-DEV", + "description": "Test product for unified AI multi-backend routing (Notebook 6)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Access to gpt-5.2 and Mistral-Large-3 via multi-backend routing" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-testing-unifiedai-dev', 'unified-ai-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-unifiedai-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-piimasking-dev')]", + "properties": { + "displayName": "LLM-HR-PIIMasking-DEV", + "description": "PII masking and deanonymization (Notebook 5 - core feature)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "PII processing with anonymization/deanonymization for HR use case" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-hr-piimasking-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piimasking-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-compliance-piiblocking-dev')]", + "properties": { + "displayName": "LLM-Compliance-PIIBlocking-DEV", + "description": "PII detection and blocking policy (Notebook 5 - compliance feature)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Detects and blocks PII patterns in requests/responses" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-compliance-piiblocking-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-compliance-piiblocking-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-piianalytics-dev')]", + "properties": { + "displayName": "LLM-HR-PIIAnalytics-DEV", + "description": "PII analytics and telemetry (Notebook 5 - observability)", + "state": "published", + "subscriptionRequired": true, + "approvalRequired": false, + "terms": "Tracks PII events to Cosmos DB pii-usage-container" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/products/apis", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}/{2}', variables('apimName'), 'llm-hr-piianalytics-dev', 'universal-llm-api')]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piianalytics-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-sales-assistant-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-sales-assistant-dev')]", + "displayName": "LLM-Sales-Assistant-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-sales-assistant-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-chatagent-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-chatagent-dev')]", + "displayName": "LLM-HR-ChatAgent-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-chatagent-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-support-bot-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-support-bot-dev')]", + "displayName": "LLM-Support-Bot-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-support-bot-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-testing-universalllm-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-universalllm-dev')]", + "displayName": "LLM-Testing-UniversalLLMAllModels-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-universalllm-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-testing-unifiedai-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-unifiedai-dev')]", + "displayName": "LLM-Testing-UnifiedAI-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-testing-unifiedai-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-piimasking-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piimasking-dev')]", + "displayName": "LLM-HR-PIIMasking-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piimasking-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-compliance-piiblocking-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-compliance-piiblocking-dev')]", + "displayName": "LLM-Compliance-PIIBlocking-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-compliance-piiblocking-dev')]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/subscriptions", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'llm-hr-piianalytics-dev-sub-01')]", + "properties": { + "scope": "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piianalytics-dev')]", + "displayName": "LLM-HR-PIIAnalytics-DEV-SUB-01", + "state": "active", + "allowTracing": true + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]", + "[resourceId('Microsoft.ApiManagement/service/products', variables('apimName'), 'llm-hr-piianalytics-dev')]" + ] + }, + { + "type": "Microsoft.EventHub/namespaces", + "apiVersion": "2022-10-01-preview", + "name": "[variables('eventHubNamespace')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Basic", + "capacity": 1 + }, + "properties": { + "minimumTlsVersion": "1.2", + "publicNetworkAccess": "Enabled", + "kafkaEnabled": false + } + }, + { + "type": "Microsoft.EventHub/namespaces/eventhubs", + "apiVersion": "2022-10-01-preview", + "name": "[format('{0}/{1}', variables('eventHubNamespace'), variables('eventHubName'))]", + "properties": { + "messageRetentionInDays": 1, + "partitionCount": 1 + }, + "dependsOn": [ + "[resourceId('Microsoft.EventHub/namespaces', variables('eventHubNamespace'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-01-01", + "name": "[variables('storageAccountName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "properties": { + "accessTier": "Hot", + "minimumTlsVersion": "TLS1_2", + "publicNetworkAccess": "Enabled" + } + }, + { + "type": "Microsoft.ApiManagement/service", + "apiVersion": "2023-09-01-preview", + "name": "[variables('apimName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "StandardV2", + "capacity": 1 + }, + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('apimMIName')))]": {} + } + }, + "properties": { + "publisherEmail": "governance@citadel.ai", + "publisherName": "Citadel Governance", + "virtualNetworkType": "None", + "publicNetworkAccess": "Enabled" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('apimMIName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/loggers", + "apiVersion": "2023-09-01-preview", + "name": "[format('{0}/{1}', variables('apimName'), 'appinsights-logger')]", + "properties": { + "loggerType": "applicationInsights", + "resourceId": "[resourceId('Microsoft.Insights/components', variables('aiName'))]", + "credentials": { + "instrumentationKey": "[reference(resourceId('Microsoft.Insights/components', variables('aiName')), '2020-02-02').InstrumentationKey]" + }, + "isBuffered": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', variables('aiName'))]", + "[resourceId('Microsoft.ApiManagement/service', variables('apimName'))]" + ] + }, + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-04-01-preview", + "name": "[variables('spokeFoundryAccountName')]", + "location": "[parameters('location')]", + "kind": "AIServices", + "tags": "[parameters('tags')]", + "sku": { + "name": "S0" + }, + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "customSubDomainName": "[variables('spokeFoundryAccountName')]", + "publicNetworkAccess": "Enabled", + "allowProjectManagement": true + } + }, + { + "type": "Microsoft.CognitiveServices/accounts/projects", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', variables('spokeFoundryAccountName'), variables('spokeFoundryProjectName'))]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "description": "Citadel Agentic Governance - Attendee agent development environment" + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', variables('spokeFoundryAccountName'))]" + ] + }, + { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[variables('spokeLaName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "sku": { + "name": "PerGB2018" + }, + "retentionInDays": 30 + } + }, + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[variables('spokeAiName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "web", + "properties": { + "Application_Type": "web", + "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', variables('spokeLaName'))]" + }, + "dependsOn": [ + "[resourceId('Microsoft.OperationalInsights/workspaces', variables('spokeLaName'))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-07-01", + "name": "[variables('spokeKeyVaultName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "tenantId": "[subscription().tenantId]", + "sku": { + "family": "A", + "name": "standard" + }, + "enableRbacAuthorization": true, + "publicNetworkAccess": "Enabled" + } + }, + { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-07-01", + "name": "[replace(variables('spokeAcrName'), '-', '')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Basic" + }, + "properties": { + "adminUserEnabled": true, + "publicNetworkAccess": "Enabled" + } + }, + { + "type": "Microsoft.CognitiveServices/accounts/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', variables('hubFoundryAccountName'), format('{0}-appinsights', variables('hubFoundryAccountName')))]", + "properties": { + "category": "AppInsights", + "target": "[resourceId('Microsoft.Insights/components', variables('aiName'))]", + "authType": "ApiKey", + "isSharedToAll": true, + "credentials": { + "key": "[reference(resourceId('Microsoft.Insights/components', variables('aiName')), '2020-02-02').InstrumentationKey]" + }, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[resourceId('Microsoft.Insights/components', variables('aiName'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', variables('aiName'))]", + "[resourceId('Microsoft.CognitiveServices/accounts', variables('hubFoundryAccountName'))]" + ] + }, + { + "type": "Microsoft.CognitiveServices/accounts/connections", + "apiVersion": "2025-04-01-preview", + "name": "[format('{0}/{1}', variables('spokeFoundryAccountName'), format('{0}-appinsights', variables('spokeFoundryAccountName')))]", + "properties": { + "category": "AppInsights", + "target": "[resourceId('Microsoft.Insights/components', variables('spokeAiName'))]", + "authType": "ApiKey", + "isSharedToAll": true, + "credentials": { + "key": "[reference(resourceId('Microsoft.Insights/components', variables('spokeAiName')), '2020-02-02').InstrumentationKey]" + }, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[resourceId('Microsoft.Insights/components', variables('spokeAiName'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', variables('spokeAiName'))]", + "[resourceId('Microsoft.CognitiveServices/accounts', variables('spokeFoundryAccountName'))]" + ] + } + ], + "outputs": { + "HUB_FOUNDRY_PROJECT_ENDPOINT": { + "type": "string", + "value": "[format('https://{0}.services.ai.azure.com/api/projects/{1}', variables('hubFoundryAccountName'), variables('hubFoundryProjectName'))]" + }, + "SPOKE_FOUNDRY_PROJECT_ENDPOINT": { + "type": "string", + "value": "[format('https://{0}.services.ai.azure.com/api/projects/{1}', variables('spokeFoundryAccountName'), variables('spokeFoundryProjectName'))]" + }, + "APIM_GATEWAY_URL": { + "type": "string", + "value": "[format('https://{0}.azure-api.net', variables('apimName'))]" + }, + "SALES_ASSISTANT_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-sales-assistant-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "SALES_ASSISTANT_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-sales-assistant-dev-sub-01" + }, + "HR_CHATAGENT_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-hr-chatagent-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "HR_CHATAGENT_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-hr-chatagent-dev-sub-01" + }, + "SUPPORT_BOT_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-support-bot-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "SUPPORT_BOT_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-support-bot-dev-sub-01" + }, + "UNIVERSAL_LLM_TEST_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-testing-universalllm-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "UNIVERSAL_LLM_TEST_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-testing-universalllm-dev-sub-01" + }, + "UNIFIED_AI_TEST_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-testing-unifiedai-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "UNIFIED_AI_TEST_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-testing-unifiedai-dev-sub-01" + }, + "PII_MASKING_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-hr-piimasking-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "PII_MASKING_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-hr-piimasking-dev-sub-01" + }, + "PII_BLOCKING_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-compliance-piiblocking-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "PII_BLOCKING_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-compliance-piiblocking-dev-sub-01" + }, + "PII_ANALYTICS_SUBSCRIPTION_KEY": { + "type": "string", + "value": "[listSecrets(resourceId('Microsoft.ApiManagement/service/subscriptions', variables('apimName'), 'llm-hr-piianalytics-dev-sub-01'), '2023-09-01-preview').primaryKey]" + }, + "PII_ANALYTICS_SUBSCRIPTION_ID": { + "type": "string", + "value": "llm-hr-piianalytics-dev-sub-01" + }, + "LLM_BACKEND_CONFIG": { + "type": "string", + "value": "[base64(string(createArray(createObject('name', 'gpt-4.1', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('gpt-4.1')), createObject('name', 'gpt-5.4-mini', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('gpt-5.4-mini')), createObject('name', 'gpt-5.2', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('gpt-5.2')), createObject('name', 'Mistral-Large-3', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('Mistral-Large-3')), createObject('name', 'Phi-4', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('Phi-4')), createObject('name', 'text-embedding-3-large', 'publisher', 'Azure OpenAI', 'endpoint', format('https://{0}.openai.azure.com', variables('hubFoundryAccountName')), 'models', createArray('text-embedding-3-large')))))]" + }, + "COSMOS_ENDPOINT": { + "type": "string", + "value": "[reference(resourceId('Microsoft.DocumentDB/databaseAccounts', variables('cosmosAccountName')), '2024-11-15').documentEndpoint]" + }, + "COSMOS_DATABASE": { + "type": "string", + "value": "[variables('cosmosDatabaseName')]" + }, + "KEY_VAULT_URL": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', variables('keyVaultName')), '2023-07-01').vaultUri]" + }, + "KEY_VAULT_NAME": { + "type": "string", + "value": "[variables('keyVaultName')]" + }, + "LOG_ANALYTICS_WORKSPACE_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', variables('laName')), '2022-10-01').customerId]" + }, + "LOG_ANALYTICS_WORKSPACE_NAME": { + "type": "string", + "value": "[variables('laName')]" + }, + "LOG_ANALYTICS_WORKSPACE_RESOURCE_ID": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', variables('laName'))]" + }, + "APPLICATIONINSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', variables('aiName')), '2020-02-02').ConnectionString]" + }, + "APPLICATIONINSIGHTS_INSTRUMENTATION_KEY": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', variables('aiName')), '2020-02-02').InstrumentationKey]" + }, + "SPOKE_LOG_ANALYTICS_WORKSPACE_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', variables('spokeLaName')), '2022-10-01').customerId]" + }, + "SPOKE_LOG_ANALYTICS_WORKSPACE_NAME": { + "type": "string", + "value": "[variables('spokeLaName')]" + }, + "SPOKE_APP_INSIGHTS_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Insights/components', variables('spokeAiName')), '2020-02-02').ConnectionString]" + }, + "EVENT_HUB_NAMESPACE": { + "type": "string", + "value": "[variables('eventHubNamespace')]" + }, + "EVENT_HUB_CONNECTION_STRING": { + "type": "string", + "value": "[listKeys(format('{0}/AuthorizationRules/RootManageSharedAccessKey', resourceId('Microsoft.EventHub/namespaces', variables('eventHubNamespace'))), '2022-10-01-preview').primaryConnectionString]" + }, + "STORAGE_ACCOUNT_NAME": { + "type": "string", + "value": "[variables('storageAccountName')]" + }, + "STORAGE_ACCOUNT_CONNECTION_STRING": { + "type": "string", + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', variables('storageAccountName'), listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2023-01-01').keys[0].value)]" + }, + "APIM_NAME": { + "type": "string", + "value": "[variables('apimName')]" + }, + "APIM_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('apimMIName')), '2023-01-31').principalId]" + }, + "HUB_FOUNDRY_ACCOUNT_RESOURCE_ID": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', variables('hubFoundryAccountName'))]" + }, + "HUB_FOUNDRY_MANAGED_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', variables('hubFoundryAccountName')), '2025-04-01-preview', 'full').identity.principalId]" + }, + "SPOKE_FOUNDRY_ACCOUNT_RESOURCE_ID": { + "type": "string", + "value": "[resourceId('Microsoft.CognitiveServices/accounts', variables('spokeFoundryAccountName'))]" + }, + "SPOKE_FOUNDRY_MANAGED_IDENTITY_PRINCIPAL_ID": { + "type": "string", + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', variables('spokeFoundryAccountName')), '2025-04-01-preview', 'full').identity.principalId]" + }, + "SPOKE_FOUNDRY_ACCOUNT_NAME": { + "type": "string", + "value": "[variables('spokeFoundryAccountName')]" + }, + "SPOKE_FOUNDRY_PROJECT_NAME": { + "type": "string", + "value": "[variables('spokeFoundryProjectName')]" + }, + "SPOKE_KEY_VAULT_NAME": { + "type": "string", + "value": "[variables('spokeKeyVaultName')]" + }, + "SPOKE_ACR_NAME": { + "type": "string", + "value": "[variables('spokeAcrName')]" + }, + "SPOKE_ACR_LOGIN_SERVER": { + "type": "string", + "value": "[format('{0}.azurecr.io', replace(variables('spokeAcrName'), '-', ''))]" + }, + "RESOURCE_GROUP_NAME": { + "type": "string", + "value": "[resourceGroup().name]" + }, + "LOCATION": { + "type": "string", + "value": "[parameters('location')]" + } + } +} \ No newline at end of file diff --git a/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 b/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 index 9685d0c0e..40df35fbc 100644 --- a/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 +++ b/03-Azure/01-04-AI/05_AI_Governance/labautomation/setup-notebook-env.ps1 @@ -1,8 +1,8 @@ <# .SYNOPSIS Bootstraps a local azd environment so the unchanged Citadel workshop - notebooks (under reference/ai-hub-gateway-solution-accelerator/workshop) - can keep calling `azd env get-value ` unmodified. + notebooks (under challenges/workshop) can keep calling + `azd env get-value ` unmodified. .DESCRIPTION The workshop notebooks were written against `azd up` and read their @@ -18,7 +18,7 @@ working directory for the nearest `azure.yaml` (its project-root marker), the same way git finds `.git`. The workshop's `azure.yaml` lives at: - reference/ai-hub-gateway-solution-accelerator/azure.yaml + challenges/azure.yaml ...one level above the `workshop/` folder that contains the notebooks. For `azd env get-value` calls made *inside* a notebook (cwd = workshop/) @@ -102,10 +102,10 @@ if (-not (Get-Command azd -ErrorAction SilentlyContinue)) { # upward from cwd for this marker, so we must run every azd command from # here (or a descendant of it) for notebooks to see the same environment. $scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Definition -$azdProjectRoot = Resolve-Path (Join-Path $scriptRoot "../reference/ai-hub-gateway-solution-accelerator") -ErrorAction SilentlyContinue +$azdProjectRoot = Resolve-Path (Join-Path $scriptRoot "../challenges") -ErrorAction SilentlyContinue if (-not $azdProjectRoot) { - throw "Could not resolve the azd project root at '../reference/ai-hub-gateway-solution-accelerator' relative to this script. Ensure the workshop reference folder exists." + throw "Could not resolve the azd project root at '../challenges' relative to this script. Ensure the challenges folder exists." } $azureYaml = Join-Path $azdProjectRoot "azure.yaml"