Skip to content

feat(provider): add azure openai#62

Merged
bzp2010 merged 6 commits into
mainfrom
bzp/feat-azure-provider
Apr 25, 2026
Merged

feat(provider): add azure openai#62
bzp2010 merged 6 commits into
mainfrom
bzp/feat-azure-provider

Conversation

@bzp2010
Copy link
Copy Markdown
Collaborator

@bzp2010 bzp2010 commented Apr 25, 2026

Summary by CodeRabbit

  • New Features

    • Added Azure OpenAI provider: API key, required resource endpoint, optional API version; form includes API version field, trims inputs, validates endpoint before submit, and applies a default API version when omitted.
  • Bug Fixes

    • Improved URL path percent-encoding for provider endpoints and ensured api-version query handling preserves and overrides existing params correctly.
  • Localization

    • Added English and Chinese UI strings for Azure provider.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Azure OpenAI provider support across backend and frontend: JSON schema, Rust config enum, new Azure provider implementation and transforms, proxy auth/base-url handling with api-version normalization, path-segment percent-encoding helper, UI form/types/i18n, dependency, and tests.

Changes

Cohort / File(s) Summary
Schema & Rust config
src/config/entities/providers-schema.json, src/config/entities/providers.rs
Add "azure" provider type to JSON schema with conditional config.azure (api_key, api_base, optional api_version). Add ProviderConfig::Azure variant and validation tests.
Gateway provider implementation
src/gateway/providers/azure.rs, src/gateway/providers/mod.rs
New AzureDef and AzureProviderConfig implementing ProviderMeta/ChatTransform/EmbedTransform/ProviderCapabilities; transforms remove top-level model, inject stream_options.include_usage, construct deployment-scoped endpoints with percent-encoded segments and api-version, set api-key header; register & re-export provider; tests added.
Proxy auth & base URL logic
src/proxy/provider.rs
Add azure_auth_and_base_url helper: build ApiKey auth from api_key, parse/validate api_base, normalize querystring to include exactly one api-version (config or default) while preserving other params; unit tests added.
Path encoding utility & bedrock update
src/gateway/traits/provider.rs, src/gateway/providers/bedrock.rs
Introduce encode_path_segment() using a custom percent-encode set (adds percent-encoding dep) and apply it to model/deployment path segments; update Bedrock tests to cover reserved character encoding.
Frontend: form, types, i18n
ui/src/components/providers/provider-form.tsx, ui/src/lib/api/types.ts, ui/src/i18n/locales/en.json, ui/src/i18n/locales/zh-CN.json
Expose 'azure' provider type and AzureProviderConfig types; provider form adds/validates api_base, includes optional api_version input, updates labels/placeholders, and adds English/Chinese i18n strings.
Deps
Cargo.toml
Add dependency percent-encoding = "2" for path-segment encoding.

Sequence Diagram(s)

sequenceDiagram
  participant UI as User UI
  participant API as Backend Proxy
  participant Registry as Provider Registry
  participant Gateway as AzureDef
  participant Azure as Azure OpenAI API

  UI->>API: submit request (provider="azure", model, payload)
  API->>Registry: resolve provider "azure"
  Registry->>Gateway: provide AzureDef + config
  API->>Gateway: forward request metadata (model, body)
  Gateway->>Gateway: transform body (remove model, inject stream_options.include_usage)
  Gateway->>Gateway: encode deployment, build endpoint with api-version
  Gateway->>Azure: send HTTP request with `api-key` header and transformed body
  Azure-->>Gateway: respond (completion/embedding)
  Gateway-->>API: normalized response
  API-->>UI: return response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error AzureProviderConfig exposes api_key in HTTP API responses without redaction, causing plaintext credential exposure in POST/PUT/GET endpoints. Add #[serde(skip_serializing)] to api_key field or implement custom serializer. Apply fix to all providers (Azure, Anthropic, OpenAI, DeepSeek, Gemini).
E2e Test Quality Review ⚠️ Warning PR implements Azure OpenAI provider but lacks comprehensive E2E test coverage verifying full request flow through gateway, provider registry, and proxy routing to Azure endpoints. Add E2E tests validating complete request cycle including provider discovery, auth injection, transformation, and routing; test error paths like empty api_base and invalid URLs.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(provider): add azure openai' accurately summarizes the main change: adding Azure OpenAI as a new provider across the codebase (schema, config, gateway, proxy, and UI layers).
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bzp/feat-azure-provider

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
ui/src/components/providers/provider-form.tsx (1)

204-229: Consider resetting Azure-specific fields when switching provider types.

When a user fills in api_version for Azure and then switches to another provider type (e.g., OpenAI), the api_version value remains in form state even though it's not submitted. This is harmless but could be confusing if they switch back to Azure later.

♻️ Optional: Reset api_version on type change
 <Select
   value={field.state.value}
   onValueChange={(next) => {
     setClientError(undefined);
     field.handleChange(next as ProviderType);
+    // Reset Azure-specific field when switching away
+    if (field.state.value === 'azure' && next !== 'azure') {
+      form.setFieldValue('api_version', '');
+    }
   }}
 >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ui/src/components/providers/provider-form.tsx` around lines 204 - 229, When
the provider type select (form.Field name="type") changes, clear any
Azure-specific form state (the api_version field) so it doesn't persist when
switching away from Azure; update the onValueChange handler (where
setClientError and field.handleChange are called) to detect the new ProviderType
and, if it's not Azure, reset/clear the 'api_version' form field (e.g. via the
form's resetField or setValue API) so the Azure-only value is removed from form
state.
src/gateway/providers/azure.rs (2)

190-195: Consider using assert_matches! for enum variant assertions.

The coding guidelines recommend using assert_matches::assert_matches for better test output. This would simplify the match statement and provide clearer error messages on failure.

♻️ Suggested refactor
+    use assert_matches::assert_matches;
+
     #[test]
     fn azure_def_transforms_embeddings_request_without_model() {
         let provider = AzureDef;
         let request = serde_json::from_value(json!({
             "model": "text-embedding-3-large",
             "input": ["hello", "world"]
         }))
         .unwrap();

         let body = provider.transform_embeddings_request(&request).unwrap();

-        match body {
-            EmbedRequestBody::Json(value) => {
-                assert_eq!(value["input"][0], "hello");
-                assert_eq!(value.get("model"), None);
-            }
-        }
+        assert_matches!(body, EmbedRequestBody::Json(value) => {
+            assert_eq!(value["input"][0], "hello");
+            assert_eq!(value.get("model"), None);
+        });
     }

As per coding guidelines: "Use pretty_assertions::assert_eq and assert_matches::assert_matches for better test output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/azure.rs` around lines 190 - 195, Replace the manual
match on EmbedRequestBody with assert_matches::assert_matches to get better
failure output: assert that body matches EmbedRequestBody::Json(value) using
assert_matches!, then perform the inner assertions using
pretty_assertions::assert_eq for the two checks (value["input"][0] == "hello"
and value.get("model") == None); update imports to bring
assert_matches::assert_matches and pretty_assertions::assert_eq into scope and
remove the explicit match block around EmbedRequestBody::Json.

21-30: Consider adding doc comments to public items.

The coding guidelines specify using /// for doc comments on public items. Adding brief documentation to AzureProviderConfig and AzureDef would improve API discoverability.

📝 Suggested documentation
+/// Configuration for the Azure OpenAI provider.
 #[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
 pub struct AzureProviderConfig {
+    /// The API key for authenticating with Azure OpenAI.
     pub api_key: String,
+    /// The base URL for the Azure OpenAI resource (e.g., `https://<resource>.openai.azure.com`).
     pub api_base: String,

+    /// Optional API version override. Defaults to `2024-10-21`.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub api_version: Option<String>,
 }

+/// Azure OpenAI provider definition implementing chat and embedding transformations.
 pub struct AzureDef;

As per coding guidelines: "Use /// for doc comments on public items in Rust".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/azure.rs` around lines 21 - 30, Add doc comments for
the public types to follow the Rust coding guidelines: add a brief /// comment
above AzureProviderConfig describing that it holds configuration for the Azure
provider (include a short note about api_key, api_base, and optional
api_version) and add a /// comment above AzureDef describing its role (e.g.,
marker/type representing the Azure provider implementation). Ensure comments are
concise and appear immediately before the pub struct declarations for
AzureProviderConfig and AzureDef.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/gateway/providers/azure.rs`:
- Around line 190-195: Replace the manual match on EmbedRequestBody with
assert_matches::assert_matches to get better failure output: assert that body
matches EmbedRequestBody::Json(value) using assert_matches!, then perform the
inner assertions using pretty_assertions::assert_eq for the two checks
(value["input"][0] == "hello" and value.get("model") == None); update imports to
bring assert_matches::assert_matches and pretty_assertions::assert_eq into scope
and remove the explicit match block around EmbedRequestBody::Json.
- Around line 21-30: Add doc comments for the public types to follow the Rust
coding guidelines: add a brief /// comment above AzureProviderConfig describing
that it holds configuration for the Azure provider (include a short note about
api_key, api_base, and optional api_version) and add a /// comment above
AzureDef describing its role (e.g., marker/type representing the Azure provider
implementation). Ensure comments are concise and appear immediately before the
pub struct declarations for AzureProviderConfig and AzureDef.

In `@ui/src/components/providers/provider-form.tsx`:
- Around line 204-229: When the provider type select (form.Field name="type")
changes, clear any Azure-specific form state (the api_version field) so it
doesn't persist when switching away from Azure; update the onValueChange handler
(where setClientError and field.handleChange are called) to detect the new
ProviderType and, if it's not Azure, reset/clear the 'api_version' form field
(e.g. via the form's resetField or setValue API) so the Azure-only value is
removed from form state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5940aff-df5f-47b5-b592-98a87373c143

📥 Commits

Reviewing files that changed from the base of the PR and between 808047c and d7ab111.

📒 Files selected for processing (9)
  • src/config/entities/providers-schema.json
  • src/config/entities/providers.rs
  • src/gateway/providers/azure.rs
  • src/gateway/providers/mod.rs
  • src/proxy/provider.rs
  • ui/src/components/providers/provider-form.tsx
  • ui/src/i18n/locales/en.json
  • ui/src/i18n/locales/zh-CN.json
  • ui/src/lib/api/types.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/gateway/providers/azure.rs (1)

17-19: Add /// docs for exported constants.

IDENTIFIER and DEFAULT_API_VERSION are public but undocumented.

✍️ Suggested update
+/// Provider identifier used in registry/config.
 pub const IDENTIFIER: &str = "azure";
+/// Default Azure OpenAI REST API version used by this provider.
 pub const DEFAULT_API_VERSION: &str = "2024-10-21";

As per coding guidelines, "Use /// for doc comments on public items in Rust".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/providers/azure.rs` around lines 17 - 19, Add triple-slash
documentation comments for the public constants IDENTIFIER and
DEFAULT_API_VERSION in azure.rs: describe what each constant represents and any
important usage notes (e.g., IDENTIFIER is the provider key used across the
gateway, DEFAULT_API_VERSION is the Azure API version used by default). Place
the /// comments immediately above the respective const declarations (IDENTIFIER
and DEFAULT_API_VERSION) and keep DEFAULT_BASE_URL unchanged since it is
private.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/gateway/providers/azure.rs`:
- Around line 46-50: The string substitution model.replace('/', "%2F") only
escapes slashes; instead percent-encode the deployment segment properly using
the url crate's percent-encoding utilities (e.g. utf8_percent_encode with an
appropriate path-segment encode set) so reserved characters like ?, #, %, and
spaces are escaped; update the chat_endpoint_path function in azure.rs to use
that percent-encoding for the model/deployment value and apply the same change
to the Bedrock provider's equivalent endpoint builder (the function around the
noted line ~81) so both providers safely encode the deployment path segment.

---

Nitpick comments:
In `@src/gateway/providers/azure.rs`:
- Around line 17-19: Add triple-slash documentation comments for the public
constants IDENTIFIER and DEFAULT_API_VERSION in azure.rs: describe what each
constant represents and any important usage notes (e.g., IDENTIFIER is the
provider key used across the gateway, DEFAULT_API_VERSION is the Azure API
version used by default). Place the /// comments immediately above the
respective const declarations (IDENTIFIER and DEFAULT_API_VERSION) and keep
DEFAULT_BASE_URL unchanged since it is private.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 37bb300b-42e7-48c9-afd5-8f3fa95d9fe2

📥 Commits

Reviewing files that changed from the base of the PR and between d7ab111 and 1b7372a.

📒 Files selected for processing (1)
  • src/gateway/providers/azure.rs

Comment thread src/gateway/providers/azure.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/gateway/traits/provider.rs (1)

44-46: Add trace/doc annotations to the new crate-visible helper.

encode_path_segment is crate-visible but lacks tracing and rustdoc annotations expected by this repository's Rust conventions.

♻️ Suggested patch
+/// Percent-encode a dynamic URL path segment for safe URL construction.
+#[fastrace::trace]
 pub(crate) fn encode_path_segment(segment: &str) -> String {
     utf8_percent_encode(segment, PATH_SEGMENT_ENCODE_SET).to_string()
 }

As per coding guidelines, "src/**/*.rs: Use #[fastrace::trace] decorator for distributed tracing on public functions" and "**/*.rs: Use /// for doc comments on public items in Rust".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gateway/traits/provider.rs` around lines 44 - 46, The helper function
encode_path_segment is crate-visible and missing the repository's required
tracing and documentation annotations; add a doc comment (///) describing what
encode_path_segment does (percent-encodes a path segment using
PATH_SEGMENT_ENCODE_SET) and decorate the function with the tracing attribute
#[fastrace::trace] so distributed tracing is enabled for encode_path_segment;
update the function signature to include these annotations (keeping the existing
name and behavior) and ensure the doc comment follows Rustdoc conventions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/gateway/providers/azure.rs`:
- Around line 20-21: Add Rustdoc comments for the two exported constants by
placing /// comments directly above IDENTIFIER and DEFAULT_API_VERSION; describe
what each constant represents (e.g., IDENTIFIER is the provider string "azure"
used to identify this gateway provider, DEFAULT_API_VERSION is the Azure REST
API version used by requests) and include any usage notes or expected format.
Ensure the comments use triple-slash `///` so they appear in generated docs and
keep them concise and idiomatic for public items.
- Around line 28-35: The derived Debug for AzureProviderConfig exposes sensitive
api_key; replace the auto-derived Debug with a custom implementation that
redacts or masks api_key in any Debug output (keep
Serialize/Deserialize/ToSchema derives), i.e., remove Debug from the derive list
and implement std::fmt::Debug for AzureProviderConfig to print other fields
normally while showing a fixed redacted value (or masked form) for the api_key
field to avoid leaking credentials.

---

Nitpick comments:
In `@src/gateway/traits/provider.rs`:
- Around line 44-46: The helper function encode_path_segment is crate-visible
and missing the repository's required tracing and documentation annotations; add
a doc comment (///) describing what encode_path_segment does (percent-encodes a
path segment using PATH_SEGMENT_ENCODE_SET) and decorate the function with the
tracing attribute #[fastrace::trace] so distributed tracing is enabled for
encode_path_segment; update the function signature to include these annotations
(keeping the existing name and behavior) and ensure the doc comment follows
Rustdoc conventions.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 13461d36-8927-4f87-afb4-dbf1bf2dd3c8

📥 Commits

Reviewing files that changed from the base of the PR and between 1b7372a and 0c61c4a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • src/gateway/providers/azure.rs
  • src/gateway/providers/bedrock.rs
  • src/gateway/traits/provider.rs
✅ Files skipped from review due to trivial changes (1)
  • Cargo.toml

Comment thread src/gateway/providers/azure.rs Outdated
Comment thread src/gateway/providers/azure.rs
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@src/gateway/providers/azure.rs`:
- Around line 167-172: The test hardcodes "api-version=2024-10-21" for
chat_url.query() and embed_url.query(), which will break when
DEFAULT_API_VERSION changes; update the assertions to use the
DEFAULT_API_VERSION constant as the single source of truth (e.g., compare
chat_url.query() and embed_url.query() to a string built from
DEFAULT_API_VERSION) so the test uses the same constant used by the code—locate
DEFAULT_API_VERSION and the assertions around chat_url and embed_url in azure.rs
and replace the literal query expectations with a value derived from
DEFAULT_API_VERSION.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be9429ff-220d-4103-b500-fbc4fcb3c52b

📥 Commits

Reviewing files that changed from the base of the PR and between 0c61c4a and c3bdec8.

📒 Files selected for processing (1)
  • src/gateway/providers/azure.rs

Comment thread src/gateway/providers/azure.rs Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/proxy/provider.rs (1)

161-176: Use pretty_assertions::assert_eq for better test output.

The test imports pretty_assertions usage elsewhere in the codebase. Consider using pretty_assertions::assert_eq for the URL comparison assertions to get more readable diffs on failure.

As per coding guidelines: "{tests,src}/**/*.rs: Use pretty_assertions::assert_eq and assert_matches::assert_matches for better test output".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/proxy/provider.rs` around lines 161 - 176, The test
provider_auth_and_base_url_returns_azure_api_key_and_versioned_base_url uses std
assert_eq for comparing the returned URL; replace those assertions with
pretty_assertions::assert_eq to get better diff output on failure. Update the
two assert_eq calls in this test (the one checking auth.api_key_for and the one
checking base_url_override) to use pretty_assertions::assert_eq, ensuring
pretty_assertions is imported or referenced (e.g., use
pretty_assertions::assert_eq) and leave provider_auth_and_base_url and
AzureProviderConfig usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/proxy/provider.rs`:
- Around line 161-176: The test
provider_auth_and_base_url_returns_azure_api_key_and_versioned_base_url uses std
assert_eq for comparing the returned URL; replace those assertions with
pretty_assertions::assert_eq to get better diff output on failure. Update the
two assert_eq calls in this test (the one checking auth.api_key_for and the one
checking base_url_override) to use pretty_assertions::assert_eq, ensuring
pretty_assertions is imported or referenced (e.g., use
pretty_assertions::assert_eq) and leave provider_auth_and_base_url and
AzureProviderConfig usage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e0896c36-9e84-4964-9417-19812d754d00

📥 Commits

Reviewing files that changed from the base of the PR and between c3bdec8 and 7a6cc05.

📒 Files selected for processing (2)
  • src/gateway/providers/azure.rs
  • src/proxy/provider.rs

@bzp2010 bzp2010 merged commit 1a6acb4 into main Apr 25, 2026
10 checks passed
@bzp2010 bzp2010 deleted the bzp/feat-azure-provider branch April 25, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant