Skip to content

feat(realtime): add Azure OpenAI and Z.ai/Zhipu GLM realtime providers#396

Merged
SantiagoDePolonia merged 3 commits into
mainfrom
feat/realtime-more-providers
Jun 15, 2026
Merged

feat(realtime): add Azure OpenAI and Z.ai/Zhipu GLM realtime providers#396
SantiagoDePolonia merged 3 commits into
mainfrom
feat/realtime-more-providers

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

What

Extends realtime (speech-to-speech) support — shipped for OpenAI, xAI, and Bailian in #394 — to two more providers that speak OpenAI's realtime event schema, so they drop into the existing transparent websocket proxy with just a provider RealtimeTarget:

  • Z.ai / Zhipu GLM-Realtime — endpoint (…/api/paas/v4/realtime) and core events mirror OpenAI's. Derives via the shared providers.OpenAIRealtimeURL, honors the ZAI_BASE_URL region (api.z.ai vs open.bigmodel.cn), Bearer auth. Adds ChatCompatible.GetBaseURL so the base URL is read live (no staleable copy — same root-cause approach as feat(realtime): add OpenAI Realtime voice via websocket reverse proxy #394's late fix).
  • Azure OpenAI GPT Realtime — same schema, different dial shape: builds wss://<resource>/openai/realtime?api-version=…&deployment=… from the resource root and authenticates with the api-key header (not Bearer). The model selects the Azure deployment.

Both get compile-time core.RealtimeProvider assertions and unit tests (URL / auth / region / no-key / missing-model).

Deliberately skipped

  • MiniMax — researched, but its only documented realtime websocket (TTS) uses a non-OpenAI schema (task_start/task_continue/task_finish) and there's no published OpenAI-compatible conversational realtime endpoint. A transparent relay can't be confirmed, so shipping it would be a guess.
  • Gemini Live and AWS Nova Sonic — real S2S APIs but with their own event schemas (and SigV4 for AWS); they need a translation adapter, tracked as separate larger work.

Provider notes (documented in CLAUDE.md)

  • xAI voice models aren't in /models discovery → set XAI_MODELS; xAI bills per-minute (no token usage).
  • Azure realtime needs a realtime-capable AZURE_API_VERSION (default may be too old).

Testing

  • Unit tests + make test-race + make lint green.
  • ⚠️ Not live-verified: no Azure or Z.ai credentials available in this environment, unlike OpenAI/Bailian/xAI which were live-tested end-to-end in feat(realtime): add OpenAI Realtime voice via websocket reverse proxy #394. The URL/auth derivations are unit-tested and follow each provider's documented contract, but a live smoke test against real Azure/Z.ai keys is recommended before relying on them.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added realtime (WebSocket) support for Azure OpenAI and Z.ai/Zhipu GLM, with provider-specific URL derivation, model-based query parameters, and correct authentication headers.
  • Documentation
    • Expanded realtime configuration documentation with additional provider examples and clarified realtime schema relaying and credential handling.
  • Tests
    • Added unit tests validating URL normalization, required request validation, and header inclusion/omission for both providers.

Both use OpenAI's realtime event schema, so they slot into the existing
transparent proxy with just a provider RealtimeTarget.

- Z.ai/Zhipu GLM-Realtime: endpoint and core events mirror OpenAI
  (…/api/paas/v4/realtime); derives via the shared providers.OpenAIRealtimeURL,
  honors ZAI_BASE_URL region (api.z.ai vs open.bigmodel.cn), Bearer auth. Adds
  ChatCompatible.GetBaseURL so the base URL is read live (no staleable copy).
- Azure OpenAI GPT Realtime: same schema, different dial shape — builds
  wss://<resource>/openai/realtime?api-version=…&deployment=… from the resource
  root and authenticates with the api-key header (not Bearer). The model selects
  the deployment.
- Compile-time core.RealtimeProvider assertions and unit tests (URL/auth/region/
  no-key/missing-model) for both.

Skipped MiniMax: its only documented realtime websocket (TTS) uses a non-OpenAI
schema (task_start/continue/finish) and no OpenAI-compatible conversational
realtime endpoint is published, so a transparent relay can't be confirmed.
Gemini Live and AWS Nova Sonic remain out of scope (separate event schemas,
need a translation adapter).

Note: these were unit-tested only — no Azure/Z.ai credentials available locally
to live-verify, unlike OpenAI/Bailian/xAI in the prior PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81256be9-fae7-45e2-b274-c6b33d835101

📥 Commits

Reviewing files that changed from the base of the PR and between 7f14201 and 0b46228.

📒 Files selected for processing (2)
  • internal/providers/azure/realtime_test.go
  • internal/providers/zai/realtime_test.go

📝 Walkthrough

Walkthrough

Adds realtime WebSocket (RealtimeTarget) support to the Z.ai and Azure OpenAI providers. Both provider structs gain an apiKey field populated by their constructors. ChatCompatible gains a GetBaseURL() method enabling base URL access for realtime URL construction. Each provider implements core.RealtimeProvider with provider-specific URL construction and credential injection patterns. Unit tests and documentation are included.

Changes

Realtime Provider Implementations

Layer / File(s) Summary
ChatCompatible.GetBaseURL() accessor
internal/providers/openai/chat_compatible.go
Adds GetBaseURL() delegating to the underlying compatible provider, enabling Z.ai and Azure realtime implementations to read the live base URL including any SetBaseURL overrides.
Z.ai provider apiKey field and constructors
internal/providers/zai/zai.go
Adds apiKey field to Provider struct and updates both New and NewWithHTTPClient constructors to populate it.
Z.ai RealtimeTarget implementation and tests
internal/providers/zai/realtime.go, internal/providers/zai/realtime_test.go
Implements RealtimeTarget with model validation, realtime URL construction using the provider's base URL with ws/wss normalization and fixed /api/paas/v4/realtime path, and optional Authorization: Bearer header. Five unit tests cover default URL format, SetBaseURL override, "coding plan" base path normalization, omitted auth header when key is empty, and missing model validation.
Azure provider apiKey field and constructors
internal/providers/azure/azure.go
Adds apiKey field to Provider struct and updates both New and NewWithHTTPClient constructors to populate it.
Azure RealtimeTarget implementation and tests
internal/providers/azure/realtime.go, internal/providers/azure/realtime_test.go
Implements RealtimeTarget with model validation, Azure-specific URL construction (realtimeURL helper: scheme normalization to wss/ws, path rewrite to /openai/realtime, deployment and api-version query parameters, api-key header injection). Four unit tests verify URL format with correct path and query params, normalization of pre-existing /openai path prefixes, omitted api-key header when key is empty, and missing model validation.
Documentation update
CLAUDE.md
Updates REALTIME_ENABLED configuration documentation with Z.ai and Azure OpenAI provider examples, credential injection patterns, provider-specific requirements (AZURE_API_VERSION), and usage-tracking spelling variants.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ZAI as zai.Provider
  participant Azure as azure.Provider
  participant ChatCompat as ChatCompatible
  participant ZAIHelper as zai.realtimeURL
  participant AzureHelper as azure.realtimeURL
  Caller->>ZAI: RealtimeTarget(ctx, req)
  Caller->>Azure: RealtimeTarget(ctx, req)
  ZAI->>ZAIHelper: build URL
  Azure->>AzureHelper: build URL
  ZAIHelper->>ChatCompat: GetBaseURL()
  ChatCompat-->>ZAIHelper: baseURL
  ZAIHelper->>ZAIHelper: normalize scheme, fix path, add model param
  AzureHelper->>AzureHelper: parse, normalize scheme, rewrite path, add query params
  ZAIHelper-->>ZAI: wss URL
  AzureHelper-->>Azure: wss URL
  ZAI-->>Caller: RealtimeTarget{URL, Authorization header}
  Azure-->>Caller: RealtimeTarget{URL, api-key header}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#242: Introduced the Z.ai provider in internal/providers/zai/zai.go, which this PR extends with apiKey storage and realtime target support.
  • ENTERPILOT/GoModel#394: Added the realtime routing/proxy infrastructure that both Azure and Z.ai realtime implementations now plug into via core.RealtimeProvider.

Poem

🐇 Hoppity-hop through the websocket gate,
Z.ai and Azure now stream in real-time great!
An api-key here, a Bearer token there,
wss:// paths built with the utmost care.
The gateway relays each event verbatim and true—
🎉 Two more providers, and all tests pass through!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately and concisely describes the main change: adding realtime provider support for Azure OpenAI and Z.ai/Zhipu GLM, which aligns with the substantial code additions across multiple files implementing RealtimeTarget for both providers.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/realtime-more-providers

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 and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds realtime websocket support for Azure OpenAI and Z.ai/Zhipu GLM. It changes:

  • Adds provider-specific realtime target builders for Azure and Z.ai.
  • Injects Azure api-key auth and Z.ai Bearer auth for upstream websocket dials.
  • Normalizes Azure and Z.ai realtime URLs from configured provider base URLs.
  • Adds tests for URL derivation, auth headers, region overrides, and missing model validation.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Reviews (2): Last reviewed commit: "fix(realtime): pin Z.ai realtime path; s..." | Re-trigger Greptile

Comment thread internal/providers/zai/realtime.go Outdated
Comment thread internal/providers/azure/realtime.go Outdated
}
u.Path = strings.TrimRight(u.Path, "/") + "/openai/realtime"
q := url.Values{}
q.Set("api-version", p.apiVersion)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Reject old API version

When AZURE_API_VERSION is omitted, this sends the provider default 2024-10-21 on realtime websocket dials. The PR documentation says Azure realtime requires a realtime-capable API version and that the default may be too old, so existing Azure configs can now fail with an opaque upstream websocket error instead of a clear local configuration error.

@codecov-commenter

codecov-commenter commented Jun 15, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 76.00000% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/providers/azure/realtime.go 74.19% 6 Missing and 2 partials ⚠️
internal/providers/zai/realtime.go 71.42% 4 Missing and 4 partials ⚠️
internal/providers/openai/chat_compatible.go 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@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 `@internal/providers/zai/realtime.go`:
- Around line 17-33: The RealtimeTarget method validates req.Model by trimming
it but then passes the untrimmed req.Model to the OpenAIRealtimeURL function.
Store the trimmed model value in a variable once at the beginning of the method
(before the validation check), then use this trimmed variable consistently in
both the empty check and when calling OpenAIRealtimeURL to avoid validating one
value while using another.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 4fe69372-98b7-4eee-9a21-761cc5f43047

📥 Commits

Reviewing files that changed from the base of the PR and between aa9cc0f and d666f5a.

📒 Files selected for processing (8)
  • CLAUDE.md
  • internal/providers/azure/azure.go
  • internal/providers/azure/realtime.go
  • internal/providers/azure/realtime_test.go
  • internal/providers/openai/chat_compatible.go
  • internal/providers/zai/realtime.go
  • internal/providers/zai/realtime_test.go
  • internal/providers/zai/zai.go

Comment thread internal/providers/zai/realtime.go
Address PR #396 review:
- Z.ai (Greptile P1): GLM-Realtime lives at a fixed /api/paas/v4/realtime path,
  but appending /realtime to the configured chat base broke the Coding Plan base
  (/api/coding/paas/v4 -> .../coding/paas/v4/realtime). Derive the realtime URL
  from the host with the pinned path instead (mirrors Bailian). Also trims the
  model once (CodeRabbit nitpick).
- Azure (Greptile P2): strip a trailing /openai or /openai/v1 from the resource
  root before appending /openai/realtime, so a base already rooted at /openai no
  longer yields /openai/openai/realtime.

Regression tests added for both (Coding Plan base; /openai[/v1] base).

Skipped Greptile P2 "reject old Azure API version": hardcoding which api-versions
support realtime is brittle (would break at GA); it is documented, and a failed
dial surfaces as a clear 502 rather than a silent error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/providers/azure/realtime_test.go (1)

12-84: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider adding test coverage for error paths in realtimeURL.

The Codecov report shows 71.42% coverage for azure/realtime.go with 6 missing lines, likely in the error paths of realtimeURL (invalid URL parse, unsupported scheme, empty host). Adding 2-3 test cases for these scenarios would improve coverage and ensure error handling behaves correctly.

Example scenarios to cover:

  • Invalid base URL that fails url.Parse
  • Base URL with an unsupported scheme (e.g., ftp://...)
  • Base URL with missing host
🤖 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 `@internal/providers/azure/realtime_test.go` around lines 12 - 84, Add test
coverage for the error paths in the realtimeURL function that are currently not
covered. Create 3 new test functions (similar in structure to
TestRealtimeTargetMissingModel) that test: (1) an invalid base URL that fails
url.Parse, (2) a base URL with an unsupported scheme like ftp://, and (3) a base
URL with a missing host. Each test should call RealtimeTarget with the
problematic configuration and verify that it returns a non-nil error. These
tests will exercise the error handling paths in realtimeURL that are currently
uncovered.
🤖 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 `@internal/providers/zai/realtime_test.go`:
- Line 58: The url.Parse call on line 58 ignores the error return value by using
the blank identifier, which creates an inconsistency with the error-checking
pattern shown earlier in TestRealtimeTarget (lines 24-27) and leaves the code
vulnerable to a panic when accessing u.Path if parsing fails. Capture and check
the error returned from url.Parse(target.URL), and handle it appropriately in
the same manner as the earlier error checks, ensuring that execution only
proceeds if the parse operation succeeds.

---

Outside diff comments:
In `@internal/providers/azure/realtime_test.go`:
- Around line 12-84: Add test coverage for the error paths in the realtimeURL
function that are currently not covered. Create 3 new test functions (similar in
structure to TestRealtimeTargetMissingModel) that test: (1) an invalid base URL
that fails url.Parse, (2) a base URL with an unsupported scheme like ftp://, and
(3) a base URL with a missing host. Each test should call RealtimeTarget with
the problematic configuration and verify that it returns a non-nil error. These
tests will exercise the error handling paths in realtimeURL that are currently
uncovered.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 87ec477c-bacf-4fe7-9226-b8e219854b33

📥 Commits

Reviewing files that changed from the base of the PR and between d666f5a and 7f14201.

📒 Files selected for processing (4)
  • internal/providers/azure/realtime.go
  • internal/providers/azure/realtime_test.go
  • internal/providers/zai/realtime.go
  • internal/providers/zai/realtime_test.go

Comment thread internal/providers/zai/realtime_test.go Outdated
Address CodeRabbit: the new Coding-Plan and /openai-strip tests ignored the
url.Parse error and would panic on a nil *url.URL if parsing failed. Check the
error consistently with the other realtime tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@SantiagoDePolonia SantiagoDePolonia merged commit 72bf0a1 into main Jun 15, 2026
19 checks passed
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.

2 participants