Skip to content

feat: Add Azure AI Foundry integration - LiteLLM spike#2011

Open
ricofurtado wants to merge 2 commits into
mainfrom
azure-ai-foundry-litellm
Open

feat: Add Azure AI Foundry integration - LiteLLM spike#2011
ricofurtado wants to merge 2 commits into
mainfrom
azure-ai-foundry-litellm

Conversation

@ricofurtado

@ricofurtado ricofurtado commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator
  • Implemented Azure AI Foundry settings form for user configuration.
  • Integrated Azure AI Foundry model fetching and validation in the ingest settings section.
  • Added Azure AI Foundry logo and settings dialog to the model providers component.
  • Enhanced model helpers to support Azure AI Foundry as a model provider.
  • Created API endpoints for fetching Azure AI Foundry models and validating credentials.
  • Updated configuration management to include Azure AI Foundry settings.
  • Implemented health check and completion tests for Azure AI Foundry.
  • Added support for Azure AI Foundry in Langflow global variable synchronization.
  • Updated settings models to accommodate Azure AI Foundry API key and endpoint.
  • Enhanced provider health checks to include Azure AI Foundry.

Summary by CodeRabbit

  • New Features

    • Added Azure AI Foundry as a selectable provider for language and embedding models.
    • Added a new setup dialog to configure or remove Azure AI Foundry credentials and deployment details.
    • Model lists, health status, and provider settings now include Azure AI Foundry information.
  • Bug Fixes

    • Improved validation and error handling when checking Azure AI Foundry connections and models.
    • Provider selection now updates correctly when Azure AI Foundry is the active provider.

- Implemented Azure AI Foundry settings form for user configuration.
- Integrated Azure AI Foundry model fetching and validation in the ingest settings section.
- Added Azure AI Foundry logo and settings dialog to the model providers component.
- Enhanced model helpers to support Azure AI Foundry as a model provider.
- Created API endpoints for fetching Azure AI Foundry models and validating credentials.
- Updated configuration management to include Azure AI Foundry settings.
- Implemented health check and completion tests for Azure AI Foundry.
- Added support for Azure AI Foundry in Langflow global variable synchronization.
- Updated settings models to accommodate Azure AI Foundry API key and endpoint.
- Enhanced provider health checks to include Azure AI Foundry.
@github-actions github-actions Bot added frontend 🟨 Issues related to the UI/UX backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) enhancement 🔵 New feature or request labels Jul 3, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

React Doctor found 3 new issues in 1 file · 3 warnings · score 84 / 100 (Needs work) · 0 fixed · vs main

3 warnings

app/settings/_components/azure-ai-foundry-settings-dialog.tsx

  • ⚠️ L4 Full Framer Motion import use-lazy-motion
  • ⚠️ L79 Event logic handled in an effect no-event-handler
  • ⚠️ L80 Missing effect dependencies exhaustive-deps

Reviewed by React Doctor for commit a987ad8. See inline comments for fixes.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds Azure AI Foundry as a new LLM/embedding provider throughout the stack: backend config, model discovery endpoint, provider health/validation checks, settings API (get/update/remove), model registry routing, and frontend settings UI (dialog, form, queries, provider metadata, and section wiring).

Changes

Azure AI Foundry Provider Support

Layer / File(s) Summary
Backend config and credential wiring
src/config/config_manager.py, src/config/settings.py, src/api/settings/langflow_sync.py
Adds AzureAIFoundryConfig, provider aggregation, env var overrides (AZURE_AI_API_KEY/AZURE_AI_API_BASE), and syncs credentials into Langflow globals and the patched client.
Model discovery endpoint and schema
src/api/models.py, src/app/routes/internal.py, src/api/settings/models.py
Adds AzureAIFoundryBody and get_azure_ai_foundry_models endpoint, registers /models/azure-ai-foundry, and extends SettingsUpdateBody/OnboardingBody/ProvidersConfig with Azure fields.
Provider health and validation
src/api/provider_health.py, src/api/provider_validation.py
Adds azure_ai_foundry to provider allowlists and endpoint details; adds Azure lightweight health, completion, and embedding validation helpers plus reformatted error-message parsing and logging across providers.
Settings endpoints
src/api/settings/endpoints.py, src/api/settings/helpers.py
Extends /settings GET/update to expose and persist Azure credentials, and adds remove_azure_ai_foundry_config removal logic with embedding-conflict checks and fallback provider selection.
Model registry and routing
src/services/models_service.py
Adds azure_ai prefix support, static Azure deployment registry population, and litellm model-name routing (azure_ai/{model_name}), plus typing normalization for other provider fetchers.
Frontend API types and hooks
frontend/app/api/mutations/useUpdateSettingsMutation.ts, frontend/app/api/queries/useGetModelsQuery.ts, frontend/app/api/queries/useGetSettingsQuery.ts
Extends request/response types and adds useGetAzureAIFoundryModelsQuery, wired into useGetCurrentProviderModelsQuery.
Frontend provider helpers and logo
frontend/app/settings/_helpers/model-helpers.tsx, frontend/components/icons/azure-ai-foundry-logo.tsx, frontend/components/provider-health-banner.tsx
Adds azure_ai_foundry to provider type/ordering, logo lookup, fallback models, a new logo icon, and banner title mapping.
Settings dialog and form
frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx, frontend/app/settings/_components/azure-ai-foundry-settings-form.tsx, frontend/app/settings/_components/model-providers.tsx
Adds a configuration/removal dialog with credential validation and form inputs, wired into the provider list UI.
Agent/ingest section integration
frontend/app/settings/_components/agent-settings-section.tsx, frontend/app/settings/_components/ingest-settings-section.tsx
Wires Azure model queries into LLM/embedding selectors and loading-state aggregation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AzureAIFoundrySettingsDialog
  participant SettingsAPI as "/api/settings"
  participant ModelsAPI as "/api/models/azure-ai-foundry"
  participant AzureEndpoint

  User->>AzureAIFoundrySettingsDialog: Enter endpoint/api key, click Save
  AzureAIFoundrySettingsDialog->>ModelsAPI: Validate credentials
  ModelsAPI->>AzureEndpoint: GET /models
  AzureEndpoint-->>ModelsAPI: model list or error
  ModelsAPI-->>AzureAIFoundrySettingsDialog: language/embedding models or error
  AzureAIFoundrySettingsDialog->>SettingsAPI: POST settings update (endpoint, api key, deployments)
  SettingsAPI-->>AzureAIFoundrySettingsDialog: success/error
  AzureAIFoundrySettingsDialog-->>User: Update provider health, show toast, close dialog
Loading

Suggested labels: enhancement

Suggested reviewers: edwinjosechittilappilly, mfortman11, mpawlow

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding Azure AI Foundry integration, and the extra qualifier is still relevant.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch azure-ai-foundry-litellm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 3, 2026
@ricofurtado ricofurtado changed the title feat: Add Azure AI Foundry integration feat: Add Azure AI Foundry integration - LiteLLM spike Jul 3, 2026
"use client";

import { useQueryClient } from "@tanstack/react-query";
import { AnimatePresence, motion } from "motion/react";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/use-lazy-motion (warning)

Importing "motion" ships about 30 kb of extra code and slows page load. Use "m" with LazyMotion instead.

Fix → Use import { LazyMotion, m } from "framer-motion" with domAnimation features. Saves about 30kb.

Docs

});

useEffect(() => {
if (open) methods.reset();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/no-event-handler (warning)

Faking an event handler with a prop plus a useEffect costs an extra render & runs late.

Fix → Run the side effect in the event handler that triggers it, instead of watching its state from a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers

Docs


useEffect(() => {
if (open) methods.reset();
}, [open]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Doctor · react-doctor/exhaustive-deps (warning)

useEffect can run with a stale methods.reset & show your users old data.

Fix → Don't blindly add missing dependencies. Read the hook callback first.

Bad:
useEffect(() => {
setCount(count + 1);
}, [count]);

Better:
useEffect(() => {
setCount((currentCount) => currentCount + 1);
}, []);

If the missing value is recreated every render, move it inside the hook or stabilize it before adding it to deps.

Docs

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 3, 2026
Comment thread src/api/models.py
)
except Exception as e:
return JSONResponse(
{"error": f"Could not connect to Azure AI Foundry endpoint: {str(e)}"},
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
frontend/app/api/queries/useGetModelsQuery.ts (1)

205-246: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider extracting a shared model-fetch hook factory.

useGetAzureAIFoundryModelsQuery duplicates the fetch/useQuery boilerplate already present in the OpenAI/Anthropic/Ollama/IBM hooks in this file (same body-building, fetch, error-throwing, and staleTime:0/gcTime:0/retry:false config). Consolidating these into a single parameterized factory (provider id, endpoint path, param mapping) would reduce duplication and ease adding the next provider.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/api/queries/useGetModelsQuery.ts` around lines 205 - 246, The
`useGetAzureAIFoundryModelsQuery` hook duplicates the same fetch-and-query setup
used by the other provider hooks in `useGetModelsQuery.ts`; refactor this into a
shared parameterized factory/helper that accepts the provider id, request path,
and body mapping. Move the common `useQuery` configuration (`staleTime`,
`gcTime`, `retry`) and the fetch/error handling into that reusable abstraction,
then have `useGetAzureAIFoundryModelsQuery` call it with the Azure AI
Foundry-specific endpoint and params.
src/api/models.py (1)

208-278: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Collapse the two identical GET calls into one request.

The credential-validation call (Lines 212-220) and the model-list call (Lines 252-260) hit the same endpoint.rstrip("/") with identical headers/timeout, doubling the round-trips. Fetch once, branch on the single response: return the 401/403 errors, then parse the body on 200.

♻️ Sketch of consolidated fetch
        language_models = []
        embedding_models = []
        try:
            async with httpx.AsyncClient() as client:
                response = await client.get(
                    endpoint.rstrip("/"),
                    headers={
                        "Authorization": f"Bearer {api_key}",
                        "Content-Type": "application/json",
                    },
                    timeout=10.0,
                )
            if response.status_code == 401:
                return JSONResponse(
                    {"error": "Invalid API key. Verify the key in Azure AI Foundry portal."},
                    status_code=400,
                )
            if response.status_code == 403:
                return JSONResponse(
                    {"error": "Access denied. Verify the API key has the required permissions."},
                    status_code=400,
                )
            if response.status_code == 200:
                entries = response.json().get("data", [])
                for entry in entries:
                    model_id = entry.get("id", "")
                    if not model_id:
                        continue
                    item = {"value": model_id, "label": model_id}
                    (embedding_models if "embed" in model_id.lower() else language_models).append(item)
        except httpx.TimeoutException:
            return JSONResponse(
                {"error": "Azure AI Foundry endpoint did not respond. Check the endpoint URL."},
                status_code=400,
            )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/models.py` around lines 208 - 278, The Azure AI Foundry flow in the
credential-check and model सूची retrieval path is making two identical GET
requests to the same endpoint with the same headers and timeout. Consolidate
this into a single httpx.AsyncClient request in the existing Azure AI Foundry
validation logic, then branch on the one response: return the 401/403
JSONResponse errors, and on 200 parse the same body to populate language_models
and embedding_models.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/app/settings/_helpers/model-helpers.tsx`:
- Around line 144-155: The azure_ai_foundry fallback entry in model-helpers is
using placeholder values as selectable model options, which can be persisted as
invalid deployment names. Update getFallbackModels so the language/embedding
hints are not treated as real choices, and make sure cloud-picker
ingest-settings only receives valid selectable options from
getFallbackModels().embedding. Use a disabled hint item or omit these
placeholders entirely and rely on empty-state messaging instead.

In `@src/api/provider_validation.py`:
- Around line 830-945: The Azure AI Foundry validation helpers are building the
wrong request URL shape by appending the deployment name to the endpoint. Update
`_test_azure_ai_foundry_completion` and `_test_azure_ai_foundry_embedding` to
use the documented models endpoint path and send the deployment/model identifier
in the JSON payload instead of as a URL segment. Keep the existing
response/error handling in these functions, but align the request construction
with the Azure AI Foundry contract so standard setups validate correctly.
- Around line 830-856: The Azure AI Foundry lightweight health check in
_test_azure_ai_foundry_lightweight_health currently treats any non-401/403
response as success, which lets unexpected 4xx/5xx responses pass validation.
Update this function to only accept the expected statuses (200 and 404) and
raise for every other status, mirroring the model-list health handler. Include
the response details (status and any useful body text) in the error path so
failures are explicit, and keep the existing timeout and logging behavior in
place.

---

Nitpick comments:
In `@frontend/app/api/queries/useGetModelsQuery.ts`:
- Around line 205-246: The `useGetAzureAIFoundryModelsQuery` hook duplicates the
same fetch-and-query setup used by the other provider hooks in
`useGetModelsQuery.ts`; refactor this into a shared parameterized factory/helper
that accepts the provider id, request path, and body mapping. Move the common
`useQuery` configuration (`staleTime`, `gcTime`, `retry`) and the fetch/error
handling into that reusable abstraction, then have
`useGetAzureAIFoundryModelsQuery` call it with the Azure AI Foundry-specific
endpoint and params.

In `@src/api/models.py`:
- Around line 208-278: The Azure AI Foundry flow in the credential-check and
model सूची retrieval path is making two identical GET requests to the same
endpoint with the same headers and timeout. Consolidate this into a single
httpx.AsyncClient request in the existing Azure AI Foundry validation logic,
then branch on the one response: return the 401/403 JSONResponse errors, and on
200 parse the same body to populate language_models and embedding_models.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 933eabc1-37c6-456a-84b6-0a9986e3f102

📥 Commits

Reviewing files that changed from the base of the PR and between 4f104ec and a987ad8.

📒 Files selected for processing (22)
  • frontend/app/api/mutations/useUpdateSettingsMutation.ts
  • frontend/app/api/queries/useGetModelsQuery.ts
  • frontend/app/api/queries/useGetSettingsQuery.ts
  • frontend/app/settings/_components/agent-settings-section.tsx
  • frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx
  • frontend/app/settings/_components/azure-ai-foundry-settings-form.tsx
  • frontend/app/settings/_components/ingest-settings-section.tsx
  • frontend/app/settings/_components/model-providers.tsx
  • frontend/app/settings/_helpers/model-helpers.tsx
  • frontend/components/icons/azure-ai-foundry-logo.tsx
  • frontend/components/provider-health-banner.tsx
  • src/api/models.py
  • src/api/provider_health.py
  • src/api/provider_validation.py
  • src/api/settings/endpoints.py
  • src/api/settings/helpers.py
  • src/api/settings/langflow_sync.py
  • src/api/settings/models.py
  • src/app/routes/internal.py
  • src/config/config_manager.py
  • src/config/settings.py
  • src/services/models_service.py

Comment on lines +144 to +155
case "azure_ai_foundry":
return {
language: [
{ value: "my-gpt4o-deployment", label: "Enter your deployment name" },
],
embedding: [
{
value: "my-embedding-deployment",
label: "Enter your deployment name",
},
],
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Placeholder fallback value can be silently selected as a real model.

Other providers' fallback lists use genuinely valid model identifiers, but here the value ("my-gpt4o-deployment" / "my-embedding-deployment") is just a placeholder while the label reads as an instruction ("Enter your deployment name"). Per getFallbackModels(...).embedding usage in frontend/components/cloud-picker/ingest-settings.tsx, this is rendered as a selectable dropdown option — a user could pick it, silently persisting an invalid deployment name that will fail downstream.

Consider rendering this as a disabled/non-selectable hint item, or omitting it from the selectable list entirely and relying on empty-state messaging instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/app/settings/_helpers/model-helpers.tsx` around lines 144 - 155, The
azure_ai_foundry fallback entry in model-helpers is using placeholder values as
selectable model options, which can be persisted as invalid deployment names.
Update getFallbackModels so the language/embedding hints are not treated as real
choices, and make sure cloud-picker ingest-settings only receives valid
selectable options from getFallbackModels().embedding. Use a disabled hint item
or omit these placeholders entirely and rely on empty-state messaging instead.

Comment on lines +830 to +856
async def _test_azure_ai_foundry_lightweight_health(api_key: str, endpoint: str) -> None:
"""Test Azure AI Foundry credentials with a lightweight GET to the models endpoint."""
if not api_key:
raise Exception("Azure AI Foundry API key is required.")
if not endpoint:
raise Exception("Azure AI Foundry endpoint URL is required.")

try:
async with httpx.AsyncClient() as client:
response = await client.get(
endpoint.rstrip("/"),
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
timeout=10.0,
)
if response.status_code == 401:
raise Exception("Invalid API key. Verify the key in Azure AI Foundry portal.")
if response.status_code == 403:
raise Exception("Access denied. Verify the API key has the required permissions.")
# 200 or 404 (empty catalog) both confirm the endpoint is reachable and the key is accepted.
logger.info("Azure AI Foundry lightweight health check passed")

except httpx.TimeoutException:
logger.error("Azure AI Foundry health check timed out")
raise Exception("Azure AI Foundry endpoint did not respond. Check the endpoint URL.")
except Exception as e:
logger.error(f"Azure AI Foundry health check failed: {str(e)}")
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the function and nearby validation helpers.
rg -n "_test_azure_ai_foundry_lightweight_health|Azure AI Foundry|_extract_error_details" src/api/provider_validation.py src -S

# Show the relevant slice with line numbers.
sed -n '800,880p' src/api/provider_validation.py

# Search for any docs or comments describing expected status codes for Azure AI Foundry.
rg -n "Azure AI Foundry|Foundry.*404|404.*Foundry|401|403|health check" -S README.md docs src || true

Repository: langflow-ai/openrag

Length of output: 49426


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the implementation around the lightweight health check.
python3 - <<'PY'
from pathlib import Path
p = Path('src/api/provider_validation.py')
lines = p.read_text().splitlines()
for i in range(820, 870):
    if i <= len(lines):
        print(f"{i:4d}: {lines[i-1]}")
PY

Repository: langflow-ai/openrag

Length of output: 2717


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for references to the Azure AI Foundry endpoint path and any status-handling logic.
rg -n "models endpoint|/models|empty catalog|404 \(empty catalog\)|Azure AI Foundry" src -S

Repository: langflow-ai/openrag

Length of output: 8622


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Compare Azure AI Foundry handling with the other lightweight checks in this file.
python3 - <<'PY'
from pathlib import Path
p = Path('src/api/provider_validation.py')
lines = p.read_text().splitlines()

ranges = {
    "openai": (250, 280),
    "ollama": (620, 640),
    "anthropic": (748, 770),
    "azure": (830, 850),
}
for name, (a, b) in ranges.items():
    print(f"\n### {name.upper()} {a}-{b}")
    for i in range(a, b + 1):
        if i <= len(lines):
            print(f"{i:4d}: {lines[i-1]}")
PY

Repository: langflow-ai/openrag

Length of output: 4948


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the Azure AI Foundry model-list validation route and its status handling.
python3 - <<'PY'
from pathlib import Path
p = Path('src/api/models.py')
lines = p.read_text().splitlines()
for i in range(200, 250):
    if i <= len(lines):
        print(f"{i:4d}: {lines[i-1]}")
PY

Repository: langflow-ai/openrag

Length of output: 2546


🌐 Web query:

Azure AI Foundry GET /models expected status codes 404 empty catalog 401 403 resource endpoint

💡 Result:

In Azure AI Foundry, when making requests to the GET /models endpoint (or related model management APIs), specific HTTP status codes indicate common configuration or authorization issues. Standard status codes for these operations generally follow the REST API guidelines defined for the platform [1]. Key Status Code Meanings: - 200 OK: The request was successful and returned the requested data [2][3][4]. - 401 Unauthorized: This indicates an authentication failure [1][5]. Common causes include missing, expired, or invalid tokens/API keys [1][5]. Ensure the Entra ID token uses the correct scope (e.g., https://ai.azure.com/.default) and that API keys are valid [5]. - 403 Forbidden: This indicates a permission issue, typically occurring when the caller lacks the necessary Azure RBAC role assignments (such as Azure AI Foundry User) at the resource or project scope [1][5]. - 404 Not Found: This frequently occurs due to an incorrect URL, API version, or path [1][6]. If you receive a 404 while attempting to list models or access a specific model deployment, verify that: - The endpoint URL is copied exactly from the Azure AI Foundry portal [7][6]. - The deployment name matches exactly what is configured in your project (note that while routing may be case-insensitive, special punctuation or spaces can cause failures) [7]. - The API version used is supported [6]. - For Azure OpenAI-compatible routes, you are targeting the correct base URL and resource endpoint [7][6]. An empty catalog or "404 Not Found" result is most commonly linked to mismatches in the endpoint configuration, missing model deployments, or incorrect project targeting rather than an actual empty service state [7][8][6]. If you are using the older Azure AI Inference beta path, note that it has been deprecated; official documentation recommends migrating to OpenAI v1-compatible routes [9].

Citations:


Fail closed on unexpected Azure AI Foundry statuses

This path currently marks any response other than 401/403 as healthy. That means 400/500s and unrelated error pages can pass validation. Mirror the model-list handler here: accept only the expected statuses (200/404) and raise on everything else with the response details.

🧰 Tools
🪛 GitHub Actions: autofix.ci / 0_ruff autofix.txt

[error] 853-853: Ruff (B904): Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling. Raise uses raise Exception("Azure AI Foundry endpoint did not respond. Check the endpoint URL.") without exception chaining.

🪛 GitHub Actions: autofix.ci / ruff autofix

[error] 853-853: Ruff B904: Within an except clause, raise exceptions with raise ... from err or raise ... from None.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/provider_validation.py` around lines 830 - 856, The Azure AI Foundry
lightweight health check in _test_azure_ai_foundry_lightweight_health currently
treats any non-401/403 response as success, which lets unexpected 4xx/5xx
responses pass validation. Update this function to only accept the expected
statuses (200 and 404) and raise for every other status, mirroring the
model-list health handler. Include the response details (status and any useful
body text) in the error path so failures are explicit, and keep the existing
timeout and logging behavior in place.

Comment on lines +830 to +945
async def _test_azure_ai_foundry_lightweight_health(api_key: str, endpoint: str) -> None:
"""Test Azure AI Foundry credentials with a lightweight GET to the models endpoint."""
if not api_key:
raise Exception("Azure AI Foundry API key is required.")
if not endpoint:
raise Exception("Azure AI Foundry endpoint URL is required.")

try:
async with httpx.AsyncClient() as client:
response = await client.get(
endpoint.rstrip("/"),
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
timeout=10.0,
)
if response.status_code == 401:
raise Exception("Invalid API key. Verify the key in Azure AI Foundry portal.")
if response.status_code == 403:
raise Exception("Access denied. Verify the API key has the required permissions.")
# 200 or 404 (empty catalog) both confirm the endpoint is reachable and the key is accepted.
logger.info("Azure AI Foundry lightweight health check passed")

except httpx.TimeoutException:
logger.error("Azure AI Foundry health check timed out")
raise Exception("Azure AI Foundry endpoint did not respond. Check the endpoint URL.")
except Exception as e:
logger.error(f"Azure AI Foundry health check failed: {str(e)}")
raise


async def _test_azure_ai_foundry_completion(api_key: str, llm_model: str, endpoint: str) -> None:
"""Test Azure AI Foundry chat completion with the given deployment."""
if not api_key:
raise Exception("Azure AI Foundry API key is required.")
if not endpoint:
raise Exception("Azure AI Foundry endpoint URL is required.")
if not llm_model:
raise Exception("A deployment name is required to test Azure AI Foundry completion.")

try:
completions_url = f"{endpoint.rstrip('/')}/{llm_model}/chat/completions"
payload = {
"messages": [{"role": "user", "content": "Hello"}],
"max_tokens": 10,
}
async with httpx.AsyncClient() as client:
response = await client.post(
completions_url,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=30.0,
)
if response.status_code != 200:
error_details = _extract_error_details(response)
if response.status_code == 401:
raise Exception("Invalid API key. Verify the key in Azure AI Foundry portal.")
if response.status_code == 403:
raise Exception(
"Access denied. Verify the API key has the required permissions."
)
if response.status_code == 404:
raise Exception(
f"Deployment '{llm_model}' not found. Check that the deployment name matches exactly what was created in Azure AI Foundry."
)
if response.status_code == 429:
raise Exception("Azure AI Foundry rate limit exceeded.")
raise Exception(f"Azure AI Foundry API error: {error_details}")
logger.info("Azure AI Foundry completion test passed")

except httpx.TimeoutException:
logger.error("Azure AI Foundry completion test timed out")
raise Exception("Request timed out")
except Exception as e:
logger.error(f"Azure AI Foundry completion test failed: {str(e)}")
raise


async def _test_azure_ai_foundry_embedding(
api_key: str, embedding_model: str, endpoint: str
) -> None:
"""Test Azure AI Foundry embedding generation with the given deployment."""
if not api_key:
raise Exception("Azure AI Foundry API key is required.")
if not endpoint:
raise Exception("Azure AI Foundry endpoint URL is required.")
if not embedding_model:
raise Exception("A deployment name is required to test Azure AI Foundry embeddings.")

try:
embeddings_url = f"{endpoint.rstrip('/')}/{embedding_model}/embeddings"
payload = {"input": ["test"]}
async with httpx.AsyncClient() as client:
response = await client.post(
embeddings_url,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
json=payload,
timeout=30.0,
)
if response.status_code != 200:
error_details = _extract_error_details(response)
if response.status_code == 401:
raise Exception("Invalid API key. Verify the key in Azure AI Foundry portal.")
if response.status_code == 404:
raise Exception(
f"Embedding deployment '{embedding_model}' not found. Check that the deployment name matches exactly what was created in Azure AI Foundry."
)
if response.status_code == 429:
raise Exception("Azure AI Foundry rate limit exceeded.")
raise Exception(f"Azure AI Foundry embedding error: {error_details}")
logger.info("Azure AI Foundry embedding test passed")

except httpx.TimeoutException:
logger.error("Azure AI Foundry embedding test timed out")
raise Exception("Request timed out")
except Exception as e:
logger.error(f"Azure AI Foundry embedding test failed: {str(e)}")
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Azure AI Foundry chat completions REST API endpoint format api-version deployment model field 2026

💡 Result:

For Azure AI Foundry chat completions, the REST API uses the following primary endpoint format [1][2]: POST {endpoint}/openai/v1/chat/completions The {endpoint} can typically be formatted as either: - https://{resource-name}.openai.azure.com/openai/v1/ [3][2] - https://{resource-name}.services.ai.azure.com/openai/v1/ [3][2] Regarding the parameters: - api-version: With the v1 GA API, api-version is generally no longer required for standard chat completions [3]. If explicitly included, the v1 API supports values such as v1 and preview [1][4]. - model: The model field is a required string in the request body, specifying the deployment ID (e.g., gpt-4o, o3) being used for the generation [1][5][4]. For administrative tasks (such as listing, creating, or updating deployments), the management plane REST API uses a different endpoint structure under management.azure.com (e.g.,.../providers/Microsoft.CognitiveServices/accounts/{accountName}/deployments/{deploymentName}?api-version=2025-06-01) [6][7][8]. In these management operations, the model property is specified within the request body as an object containing format, name, and version fields [6][7][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and find surrounding Azure AI Foundry helpers/docs.
git ls-files | rg 'src/api/provider_validation.py|README|docs|azure|foundry|provider_validation'

echo '--- outline ---'
ast-grep outline src/api/provider_validation.py --view expanded | sed -n '1,220p'

echo '--- relevant mentions ---'
rg -n "Azure AI Foundry|openai/v1|chat/completions|embeddings|api-version|deployment|models/chat|openai/deployments" src/api/provider_validation.py README* docs* . -g '!**/node_modules/**' -g '!**/.git/**' || true

Repository: langflow-ai/openrag

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant section with line numbers if the file is manageable.
wc -l src/api/provider_validation.py
sed -n '780,980p' src/api/provider_validation.py | cat -n

Repository: langflow-ai/openrag

Length of output: 8771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Azure AI Foundry settings/docs for the expected endpoint shape.
rg -n "Azure AI Foundry|openai/v1|services.ai.azure.com|openai.azure.com|api-version|deployment" \
  frontend app docs README.md src tests \
  -g '!**/node_modules/**' -g '!**/.git/**' | sed -n '1,240p'

Repository: langflow-ai/openrag

Length of output: 23814


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the Azure AI Foundry settings dialog/form and any docs references.
for f in \
  frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx \
  frontend/app/settings/_components/azure-ai-foundry-settings-form.tsx \
  docs/docs/reference/configuration.mdx \
  README.md
do
  if [ -f "$f" ]; then
    echo "### $f"
    wc -l "$f"
    sed -n '1,240p' "$f" | cat -n
    echo
  fi
done

Repository: langflow-ai/openrag

Length of output: 42817


🌐 Web query:

Azure AI Foundry REST API chat completions openai/v1 model field embeddings endpoint official documentation

💡 Result:

The Azure AI Foundry platform provides REST API endpoints for both chat completions and embeddings, following the standard OpenAI v1-compatible structure [1][2][3]. Chat Completions Endpoint The chat completions API is available at the following path [1][4]: POST {endpoint}/openai/v1/chat/completions Key details include: - Base URL: The {endpoint} corresponds to your Azure AI Foundry models API endpoint [1][4]. - API Version: You can specify an api-version query parameter, such as v1 or preview [1][4]. - Request Body: The API accepts standard OpenAI-style chat completion parameters, including messages (a list of conversation objects with roles) and the model field to specify the deployment name [1][4]. Embeddings Endpoint The embeddings API is available at the following path [2][3]: POST {endpoint}/openai/v1/embeddings Key details include: - Request Body: Requires an input field (string, array of strings, or array of integers/tokens) and a model field to specify the model name [2][3]. - Optional Parameters: You can include parameters such as dimensions (for specific models) and encoding_format (e.g., float or base64) to control the output [2][5][3]. - Limits: There are specific constraints on the input, such as a maximum of 8,192 tokens per input and a hard cap of 300,000 tokens summed across all inputs in a single request [2][6][3]. Documentation Note The Azure AI Foundry REST API documentation explicitly organizes these under the {endpoint}/openai/v1/ path, facilitating compatibility with OpenAI's SDKs and standard REST implementations [1][2][3]. Ensure you are referencing the current "Azure AI Foundry" documentation rather than legacy "classic" portal references, which may use different URL structures (e.g., containing /deployments/{deployment-id}/) [7][5][8][9].

Citations:


🌐 Web query:

site:learn.microsoft.com Azure AI Foundry embeddings REST API openai/v1 embeddings model field official

💡 Result:

In the Azure AI Foundry OpenAI v1 embeddings REST API, the model field is a required string parameter that specifies the ID of the model to be used for the request [1][2][3]. Official documentation clarifies that this field must be set to your specific model deployment name [4][5]. Key details regarding this field: - Required: The model field is mandatory in the request body [1][3]. - Value: It should correspond to the name you assigned to your model deployment when you created it in your Azure AI Foundry resource [4][5]. - Usage: You can retrieve a list of your available models and their deployment names using the List Models API [1][3]. Note that Azure AI Foundry also provides a separate Azure AI Model Inference API (distinct from the OpenAI v1-style API), which also uses a model parameter to identify the specific model deployed on an endpoint [6][7]. However, for the openai/v1/embeddings route specifically, the model field remains the standard way to reference your deployment [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrow inspection of Azure AI Foundry UX/docs and any endpoint hints.
files=(
  frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx
  frontend/app/settings/_components/azure-ai-foundry-settings-form.tsx
  docs/docs/reference/configuration.mdx
  README.md
)

for f in "${files[@]}"; do
  if [ -f "$f" ]; then
    echo "### $f"
    ast-grep outline "$f" --view expanded | sed -n '1,220p' || true
    echo "--- snippets ---"
    rg -n "Azure AI Foundry|endpoint|api-version|openai/v1|services.ai.azure.com|openai.azure.com|deployment" "$f" || true
    echo
  fi
done

Repository: langflow-ai/openrag

Length of output: 4818


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the Azure AI Foundry config and model lookup code to infer the intended endpoint contract.
sed -n '110,150p' src/config/config_manager.py | cat -n
echo '---'
sed -n '170,320p' src/api/models.py | cat -n
echo '---'
sed -n '1,120p' src/services/models_service.py | cat -n

Repository: langflow-ai/openrag

Length of output: 14390


Azure AI Foundry validation calls use the wrong request shape

_test_azure_ai_foundry_completion and _test_azure_ai_foundry_embedding append /{deployment}/... to an endpoint the app documents as .../models, which doesn’t match the Azure AI Foundry contract. These checks should use the documented request path and pass the deployment/model in the JSON body instead of as a raw URL segment, or standard Foundry setups will fail validation.

🧰 Tools
🪛 GitHub Actions: autofix.ci / 0_ruff autofix.txt

[error] 853-853: Ruff (B904): Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling. Raise uses raise Exception("Azure AI Foundry endpoint did not respond. Check the endpoint URL.") without exception chaining.


[error] 900-900: Ruff (B904): Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling. Raise uses raise Exception("Request timed out") without exception chaining.


[error] 942-942: Ruff (B904): Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling. Raise uses raise Exception("Request timed out") without exception chaining.

🪛 GitHub Actions: autofix.ci / ruff autofix

[error] 853-853: Ruff B904: Within an except clause, raise exceptions with raise ... from err or raise ... from None.


[error] 900-900: Ruff B904: Within an except clause, raise exceptions with raise ... from err or raise ... from None.


[error] 942-942: Ruff B904: Within an except clause, raise exceptions with raise ... from err or raise ... from None.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/provider_validation.py` around lines 830 - 945, The Azure AI Foundry
validation helpers are building the wrong request URL shape by appending the
deployment name to the endpoint. Update `_test_azure_ai_foundry_completion` and
`_test_azure_ai_foundry_embedding` to use the documented models endpoint path
and send the deployment/model identifier in the JSON payload instead of as a URL
segment. Keep the existing response/error handling in these functions, but align
the request construction with the Azure AI Foundry contract so standard setups
validate correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) enhancement 🔵 New feature or request frontend 🟨 Issues related to the UI/UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants