Skip to content

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

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

feat: Add Azure AI Foundry integration - LiteLLM spike#2011
ricofurtado wants to merge 5 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 LLM and embedding provider, including model browsing, provider grouping, and a dedicated provider logo/labels in settings.
    • Introduced an Azure AI Foundry configuration dialog to save/remove endpoint/API key and deployment names, with test connection support.
    • Integrated Azure AI Foundry into agent model selection, embedding model selection, provider settings, and provider health display.
  • Bug Fixes
    • Improved Azure AI Foundry provider validation and error messaging for connectivity, credential checks, and deployment testing, including clearer failure details and safer removal handling.

- 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 42eba425-a3aa-4855-aeb6-3d9a09a8b62d

📥 Commits

Reviewing files that changed from the base of the PR and between e89855c and b893d51.

📒 Files selected for processing (1)
  • src/api/provider_validation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/api/provider_validation.py

Walkthrough

Adds Azure AI Foundry support across backend configuration, model discovery and validation, settings persistence, model registry routing, and frontend settings UI, including provider-specific queries, dialogs, model selection, and provider metadata.

Changes

Azure AI Foundry Provider Support

Layer / File(s) Summary
Backend config and env wiring
src/config/config_manager.py, src/config/settings.py, src/api/settings/langflow_sync.py
Adds Azure provider config, file/env loading, and runtime environment syncing for Azure credentials and endpoint values.
Model discovery endpoint and settings schema
src/api/models.py, src/app/routes/internal.py, src/api/settings/models.py
Adds the Azure model discovery body and endpoint, registers the internal route, and extends settings/onboarding/provider config models.
Provider health and validation
src/api/provider_health.py, src/api/provider_validation.py
Expands provider health support and adds Azure-specific lightweight health, completion, and embedding validation helpers.
Settings read, update, and removal
src/api/settings/endpoints.py, src/api/settings/helpers.py
Returns, persists, and removes Azure provider settings, with fallback selection and embedding-conflict checks.
Model registry and LiteLLM routing
src/services/models_service.py
Adds Azure provider prefix support, registry population, and LiteLLM model-name routing.
Frontend API types and model queries
frontend/app/api/mutations/useUpdateSettingsMutation.ts, frontend/app/api/queries/useGetModelsQuery.ts, frontend/app/api/queries/useGetSettingsQuery.ts
Extends settings types and adds the Azure model query hook and current-provider routing.
Provider helpers, banner, 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 provider ordering, logo selection, fallback models, banner title mapping, and a new SVG icon.
Settings dialog, form, and provider wiring
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, frontend/app/settings/_components/agent-settings-section.tsx, frontend/app/settings/_components/ingest-settings-section.tsx
Adds the Azure configuration dialog and form, wires it into provider selection, and surfaces Azure models in agent and ingest sections.

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

Suggested labels: enhancement

Suggested reviewers: mpawlow, mfortman11, edwinjosechittilappilly

🚥 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 matches the main change: adding Azure AI Foundry integration, with the LiteLLM spike qualifier as minor extra context.
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 thread src/api/provider_validation.py
@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 6, 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: 2

🤖 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 `@src/api/models.py`:
- Around line 224-267: The test_completion branch in the route handler contains
business logic for deployment validation, inference calls, and error
aggregation, which should be moved out of the handler. Extract this flow into a
dedicated helper/service method in provider_validation or models_service, and
have the route only pass request data through and return the service result.
Keep the existing behavior of _test_azure_ai_foundry_completion,
_test_azure_ai_foundry_embedding, and the JSONResponse error handling, but
relocate the orchestration logic so the handler stays thin.
- Around line 243-263: Sanitize the Azure AI Foundry exception handling in the
model testing flow by avoiding `str(e)` in client-facing `JSONResponse` errors.
In the relevant `try/except` blocks around the LLM and embedding tests in
`src/api/models.py` (and the matching exception handler later in the same flow),
log the caught exception with `logger.exception` or equivalent, then
append/return a fixed user-facing message instead of provider or transport
details. Keep the existing success paths in `_test_azure_ai_foundry_embedding`
and related helpers unchanged, but make sure all 400 responses use a generic
message.
🪄 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: 4edcbfcc-2169-4489-ba24-a890cf4df089

📥 Commits

Reviewing files that changed from the base of the PR and between a987ad8 and 6e3e634.

📒 Files selected for processing (3)
  • frontend/app/api/queries/useGetModelsQuery.ts
  • frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx
  • src/api/models.py

Comment thread src/api/models.py
Comment on lines +224 to +267
# Full inference test path — validates deployment names with real calls
if test_completion:
if not llm_deployment_name and not embedding_deployment_name:
return JSONResponse(
{"error": "At least one deployment name is required to test the connection."},
status_code=400,
)

language_models = []
embedding_models = []
errors = []

if llm_deployment_name:
try:
await _test_azure_ai_foundry_completion(api_key, llm_deployment_name, endpoint)
language_models.append(
{"value": llm_deployment_name, "label": llm_deployment_name}
)
logger.info(f"Azure AI Foundry LLM test passed for '{llm_deployment_name}'")
except Exception as e:
errors.append(f"LLM deployment '{llm_deployment_name}': {str(e)}")

if embedding_deployment_name:
try:
await _test_azure_ai_foundry_embedding(
api_key, embedding_deployment_name, endpoint
)
embedding_models.append(
{"value": embedding_deployment_name, "label": embedding_deployment_name}
)
logger.info(
f"Azure AI Foundry embedding test passed for '{embedding_deployment_name}'"
)
except Exception as e:
errors.append(
f"Embedding deployment '{embedding_deployment_name}': {str(e)}"
)

if errors:
return JSONResponse({"error": "; ".join(errors)}, status_code=400)

return JSONResponse(
{"language_models": language_models, "embedding_models": embedding_models}
)

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.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Extract the inference-test flow out of the route handler.

The new test_completion branch adds substantial business logic (deployment validation, sequential inference calls, error aggregation) directly in the route handler. Move this into a dedicated service/helper (e.g., in provider_validation or models_service) and keep the handler limited to request/response wiring.

As per path instructions: "No business logic in route handlers."

🤖 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 224 - 267, The test_completion branch in the
route handler contains business logic for deployment validation, inference
calls, and error aggregation, which should be moved out of the handler. Extract
this flow into a dedicated helper/service method in provider_validation or
models_service, and have the route only pass request data through and return the
service result. Keep the existing behavior of _test_azure_ai_foundry_completion,
_test_azure_ai_foundry_embedding, and the JSONResponse error handling, but
relocate the orchestration logic so the handler stays thin.

Source: Path instructions

Comment thread src/api/models.py
Comment on lines +243 to +263
except Exception as e:
errors.append(f"LLM deployment '{llm_deployment_name}': {str(e)}")

if embedding_deployment_name:
try:
await _test_azure_ai_foundry_embedding(
api_key, embedding_deployment_name, endpoint
)
embedding_models.append(
{"value": embedding_deployment_name, "label": embedding_deployment_name}
)
logger.info(
f"Azure AI Foundry embedding test passed for '{embedding_deployment_name}'"
)
except Exception as e:
errors.append(
f"Embedding deployment '{embedding_deployment_name}': {str(e)}"
)

if errors:
return JSONResponse({"error": "; ".join(errors)}, status_code=400)

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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file and inspect the relevant region first.
wc -l src/api/models.py
sed -n '210,290p' src/api/models.py

# Locate the helper functions referenced by the snippet.
rg -n "_test_azure_ai_foundry_(llm|embedding)|JSONResponse\\(|str\\(e\\)" src/api/models.py src -S

Repository: langflow-ai/openrag

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the Azure AI Foundry helper implementations and the related endpoint section.
sed -n '880,970p' src/api/provider_validation.py
sed -n '220,320p' src/api/models.py

# Show the nearby exception handling in the same file for comparison.
sed -n '288,310p' src/api/models.py

Repository: langflow-ai/openrag

Length of output: 8675


Sanitize Azure AI Foundry error responses before sending them back. These handlers pass str(e) to the client at lines 244, 259, and 300, which can expose provider or transport details in a 400 response. Log the exception and return a fixed user-facing message 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 `@src/api/models.py` around lines 243 - 263, Sanitize the Azure AI Foundry
exception handling in the model testing flow by avoiding `str(e)` in
client-facing `JSONResponse` errors. In the relevant `try/except` blocks around
the LLM and embedding tests in `src/api/models.py` (and the matching exception
handler later in the same flow), log the caught exception with
`logger.exception` or equivalent, then append/return a fixed user-facing message
instead of provider or transport details. Keep the existing success paths in
`_test_azure_ai_foundry_embedding` and related helpers unchanged, but make sure
all 400 responses use a generic message.

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 6, 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: 1

🤖 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 `@src/api/models.py`:
- Around line 342-356: The fallback in the Azure AI Foundry model selection
logic is too coarse because it only runs when both language_models and
embedding_models are empty. Update the fallback in the model-building path for
Azure AI Foundry so each category is filled independently: if language_models is
empty, use deployment_name or
config.providers.azure_ai_foundry.llm_deployment_name; if embedding_models is
empty, use deployment_name or
config.providers.azure_ai_foundry.embedding_deployment_name. Keep the existing
behavior in the model assembly code that populates language_models and
embedding_models, but remove the dependency between the two lists when applying
stored deployment-name fallbacks.
🪄 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: bf7f5b78-e67f-45c8-b21f-0d99d75f1128

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3e634 and e89855c.

📒 Files selected for processing (6)
  • frontend/app/api/queries/useGetSettingsQuery.ts
  • frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx
  • src/api/models.py
  • src/api/settings/endpoints.py
  • src/api/settings/models.py
  • src/config/config_manager.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/api/settings/models.py
  • frontend/app/settings/_components/azure-ai-foundry-settings-dialog.tsx
  • src/config/config_manager.py
  • src/api/settings/endpoints.py

Comment thread src/api/models.py
Comment on lines +342 to +356
# If the dynamic list came back empty, fall back to stored deployment names.
# These are persisted in the provider config independently of the active
# llm_provider/embedding_provider so they survive provider switches.
if not language_models and not embedding_models:
if deployment_name:
entry = {"value": deployment_name, "label": deployment_name}
language_models.append(entry)
embedding_models.append(entry)
else:
stored_llm = config.providers.azure_ai_foundry.llm_deployment_name
stored_embed = config.providers.azure_ai_foundry.embedding_deployment_name
if stored_llm:
language_models.append({"value": stored_llm, "label": stored_llm})
if stored_embed:
embedding_models.append({"value": stored_embed, "label": stored_embed})

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

Fallback only triggers when both lists are empty — partial results leave the other category unfilled.

if not language_models and not embedding_models: requires both lists to be empty before falling back to stored deployment names. If the dynamic GET /models parse yields entries for only one category (e.g., an embedding deployment matched the "embed" heuristic but no LLM deployment was found), embedding_models is non-empty, the combined condition is False, and language_models stays empty even though config.providers.azure_ai_foundry.llm_deployment_name may hold a valid stored value.

🐛 Proposed fix: fall back per-category independently
-        if not language_models and not embedding_models:
-            if deployment_name:
-                entry = {"value": deployment_name, "label": deployment_name}
-                language_models.append(entry)
-                embedding_models.append(entry)
-            else:
-                stored_llm = config.providers.azure_ai_foundry.llm_deployment_name
-                stored_embed = config.providers.azure_ai_foundry.embedding_deployment_name
-                if stored_llm:
-                    language_models.append({"value": stored_llm, "label": stored_llm})
-                if stored_embed:
-                    embedding_models.append({"value": stored_embed, "label": stored_embed})
+        if not language_models and not embedding_models and deployment_name:
+            entry = {"value": deployment_name, "label": deployment_name}
+            language_models.append(entry)
+            embedding_models.append(entry)
+        else:
+            if not language_models:
+                stored_llm = config.providers.azure_ai_foundry.llm_deployment_name
+                if stored_llm:
+                    language_models.append({"value": stored_llm, "label": stored_llm})
+            if not embedding_models:
+                stored_embed = config.providers.azure_ai_foundry.embedding_deployment_name
+                if stored_embed:
+                    embedding_models.append({"value": stored_embed, "label": stored_embed})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# If the dynamic list came back empty, fall back to stored deployment names.
# These are persisted in the provider config independently of the active
# llm_provider/embedding_provider so they survive provider switches.
if not language_models and not embedding_models:
if deployment_name:
entry = {"value": deployment_name, "label": deployment_name}
language_models.append(entry)
embedding_models.append(entry)
else:
stored_llm = config.providers.azure_ai_foundry.llm_deployment_name
stored_embed = config.providers.azure_ai_foundry.embedding_deployment_name
if stored_llm:
language_models.append({"value": stored_llm, "label": stored_llm})
if stored_embed:
embedding_models.append({"value": stored_embed, "label": stored_embed})
# If the dynamic list came back empty, fall back to stored deployment names.
# These are persisted in the provider config independently of the active
# llm_provider/embedding_provider so they survive provider switches.
if not language_models and not embedding_models and deployment_name:
entry = {"value": deployment_name, "label": deployment_name}
language_models.append(entry)
embedding_models.append(entry)
else:
if not language_models:
stored_llm = config.providers.azure_ai_foundry.llm_deployment_name
if stored_llm:
language_models.append({"value": stored_llm, "label": stored_llm})
if not embedding_models:
stored_embed = config.providers.azure_ai_foundry.embedding_deployment_name
if stored_embed:
embedding_models.append({"value": stored_embed, "label": stored_embed})
🤖 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 342 - 356, The fallback in the Azure AI
Foundry model selection logic is too coarse because it only runs when both
language_models and embedding_models are empty. Update the fallback in the
model-building path for Azure AI Foundry so each category is filled
independently: if language_models is empty, use deployment_name or
config.providers.azure_ai_foundry.llm_deployment_name; if embedding_models is
empty, use deployment_name or
config.providers.azure_ai_foundry.embedding_deployment_name. Keep the existing
behavior in the model assembly code that populates language_models and
embedding_models, but remove the dependency between the two lists when applying
stored deployment-name fallbacks.

@github-actions github-actions Bot added enhancement 🔵 New feature or request and removed enhancement 🔵 New feature or request labels Jul 7, 2026
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