Skip to content

feat(providers): add ZAI provider#239

Open
gr3enarr0w wants to merge 1 commit into
ferro-labs:mainfrom
gr3enarr0w:feat/zai-provider
Open

feat(providers): add ZAI provider#239
gr3enarr0w wants to merge 1 commit into
ferro-labs:mainfrom
gr3enarr0w:feat/zai-provider

Conversation

@gr3enarr0w

@gr3enarr0w gr3enarr0w commented Jun 28, 2026

Copy link
Copy Markdown

Summary

  • Adds providers/zai — a direct single-vendor integration with Z.ai (api.z.ai), serving the Zhipu AI GLM model family
  • Implements core.Provider, core.StreamProvider, core.ProxiableProvider, and core.DiscoveryProvider via the existing internal/openaicompat helpers (same pattern as xAI, Moonshot, etc.)
  • Models: glm-5.1, glm-5-turbo, glm-5, glm-4.7, glm-4.6, glm-4.5, glm-4.5-air — sourced from live /models endpoint verification
  • Env var: ZAI_API_KEY (+ optional ZAI_BASE_URL)

This is a direct single-vendor provider (not an aggregator), so it avoids any aggregator-design concerns raised in the broader review of #226.

Splits the ZAI portion of #230 into its own PR as requested by @MitulShah1.

Test plan

  • go build ./... passes
  • Unit tests in providers/zai/zai_test.go cover New, SupportedModels, SupportsModel, Models, AuthHeaders, mock SSE streaming, and mock HTTP non-streaming
  • providers/stability_test.go extended with NameZAI stability case

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for the Z.ai provider, including chat, streaming, model discovery, and proxy capabilities.
    • Z.ai is now available in the provider list and can be selected like other built-in providers.
  • Bug Fixes

    • Improved provider name consistency so Z.ai appears with the correct canonical name.
    • Expanded automated coverage for Z.ai behavior, including authentication and response handling.

Adds providers/zai — a direct single-vendor integration with Z.ai (api.z.ai),
serving the GLM model family (glm-5.1, glm-5-turbo, glm-5, glm-4.7, glm-4.6,
glm-4.5, glm-4.5-air). Implements core.Provider, core.StreamProvider,
core.ProxiableProvider, and core.DiscoveryProvider via the existing
internal/openaicompat helpers.

Splits out the ZAI portion of PR ferro-labs#230 as requested by @MitulShah1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@gr3enarr0w

Copy link
Copy Markdown
Author

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ❌

230 - Partially compliant

Compliant requirements:

  • Add ZAI provider package (providers/zai/) implementing core.Provider, core.StreamProvider, core.ProxiableProvider, and core.DiscoveryProvider using internal/openaicompat helpers
  • Add providers/zai/zai_test.go with unit tests
  • Add NameZAI constant to providers/names.go
  • Register ZAI provider in providers/providers_list.go with ZAI_API_KEY / ZAI_BASE_URL env mappings
  • Add name-stability case for ZAI in providers/stability_test.go

Non-compliant requirements:

  • Add NanoGPT provider package (providers/nanogpt/)
  • Add providers/nanogpt/nanogpt_test.go
  • Add NameNanoGPT constant
  • Register NanoGPT provider with NANOGPT_API_KEY
  • Add NanoGPT name-stability case

Requires further human verification:

  • Manual testing: curl a chat completion through ZAI with a real API key

226 - Not compliant

Non-compliant requirements:

  • Add Requesty provider package (providers/requesty/)
  • Add Requesty unit tests
  • Add NameRequesty constant and AllProviderNames() entry
  • Register Requesty provider with REQUESTY_API_KEY / REQUESTY_BASE_URL
  • Add Requesty name-stability case
  • Update config examples with requesty virtual key
⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@MitulShah1

Copy link
Copy Markdown
Contributor

@gr3enarr0w holding this PR as i am working on streamline all providers.

@MitulShah1

Copy link
Copy Markdown
Contributor

@gr3enarr0w thanks for patient but i've completed my provider refactoring and streamline proces can you rebase this branch from main branch? i am planning to release in upcoming release.

@MitulShah1

Copy link
Copy Markdown
Contributor

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new Z.ai (Zhipu AI) provider implementation supporting chat completion, streaming, and model discovery via OpenAI-compatible endpoints, then registers it as NameZAI in the provider name constants, provider registry (with ZAI_API_KEY/ZAI_BASE_URL config mapping), and stability tests.

Changes

Z.ai Provider Integration

Layer / File(s) Summary
Z.ai provider client implementation
providers/zai/zai.go
Adds Provider struct, New constructor with base URL normalization, AuthHeaders, SupportedModels/Models, DiscoverModels, and Complete/CompleteStream methods calling OpenAI-compatible endpoints.
Z.ai provider unit tests
providers/zai/zai_test.go
Adds tests for construction, model listing, auth headers, and mock HTTP/SSE servers validating Complete and CompleteStream behavior.
Provider registry and naming wiring
providers/names.go, providers/providers_list.go, providers/stability_test.go
Adds NameZAI constant, registers ZAI in allProviders with capabilities and env var config, updates AllProviderNames(), and extends stability test cases.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Provider as zai.Provider
  participant ZAIAPI as Z.ai API

  Caller->>Provider: Complete(ctx, req)
  Provider->>ZAIAPI: POST /chat/completions (Bearer auth, JSON)
  ZAIAPI-->>Provider: response
  Provider-->>Caller: core.Response

  Caller->>Provider: CompleteStream(ctx, req)
  Provider->>ZAIAPI: POST /chat/completions (stream)
  ZAIAPI-->>Provider: SSE chunks
  Provider-->>Caller: chan core.StreamChunk
Loading

Poem

A new friend hops into the gateway den,
Z.ai joins the chat-completion pen. 🐇
With headers bearing tokens bright,
and streaming chunks that flow just right,
I nibble code and thump with glee—
one more provider, wild and free!

🚥 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 and concisely summarizes the main change: adding the ZAI provider.
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 unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/zai-provider

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

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
providers/providers_list.go 0.00% 3 Missing ⚠️
providers/zai/zai.go 93.18% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@providers/zai/zai.go`:
- Around line 90-109: Both Provider.Complete and Provider.CompleteStream are
duplicating the auth header construction instead of using the shared
Provider.AuthHeaders helper. Update these methods to call p.AuthHeaders() when
building the openaicompat.ChatParams Headers so the Authorization logic stays
centralized and any future auth changes only need to be made in one place.
🪄 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 Plus

Run ID: 7c7a6a65-ef1c-464b-a728-48406a5d69a8

📥 Commits

Reviewing files that changed from the base of the PR and between 17998ae and d3aeeda.

📒 Files selected for processing (5)
  • providers/names.go
  • providers/providers_list.go
  • providers/stability_test.go
  • providers/zai/zai.go
  • providers/zai/zai_test.go

Comment thread providers/zai/zai.go
Comment on lines +90 to +109
func (p *Provider) Complete(ctx context.Context, req core.Request) (*core.Response, error) {
return openaicompat.PostChat(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
}, req)
}

// CompleteStream sends a streaming chat completion request to Z.ai.
func (p *Provider) CompleteStream(ctx context.Context, req core.Request) (<-chan core.StreamChunk, error) {
return openaicompat.PostStream(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
}, req)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate header-building logic; reuse AuthHeaders().

Complete and CompleteStream each re-construct the Authorization header map instead of calling p.AuthHeaders() (line 59-61). If the auth scheme ever changes, only one of the three call sites is likely to be updated.

♻️ Suggested refactor
+func (p *Provider) requestHeaders() map[string]string {
+	h := p.AuthHeaders()
+	h["Content-Type"] = "application/json"
+	return h
+}
+
 func (p *Provider) Complete(ctx context.Context, req core.Request) (*core.Response, error) {
 	return openaicompat.PostChat(ctx, openaicompat.ChatParams{
 		HTTPClient: p.httpClient,
 		URL:        p.baseURL + "/chat/completions",
 		Provider:   p.name,
 		Label:      "zai",
-		Headers:    map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
+		Headers:    p.requestHeaders(),
 	}, req)
 }
 
 func (p *Provider) CompleteStream(ctx context.Context, req core.Request) (<-chan core.StreamChunk, error) {
 	return openaicompat.PostStream(ctx, openaicompat.ChatParams{
 		HTTPClient: p.httpClient,
 		URL:        p.baseURL + "/chat/completions",
 		Provider:   p.name,
 		Label:      "zai",
-		Headers:    map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
+		Headers:    p.requestHeaders(),
 	}, req)
 }
📝 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
func (p *Provider) Complete(ctx context.Context, req core.Request) (*core.Response, error) {
return openaicompat.PostChat(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
}, req)
}
// CompleteStream sends a streaming chat completion request to Z.ai.
func (p *Provider) CompleteStream(ctx context.Context, req core.Request) (<-chan core.StreamChunk, error) {
return openaicompat.PostStream(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: map[string]string{"Authorization": "Bearer " + p.apiKey, "Content-Type": "application/json"},
}, req)
}
func (p *Provider) requestHeaders() map[string]string {
h := p.AuthHeaders()
h["Content-Type"] = "application/json"
return h
}
func (p *Provider) Complete(ctx context.Context, req core.Request) (*core.Response, error) {
return openaicompat.PostChat(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: p.requestHeaders(),
}, req)
}
// CompleteStream sends a streaming chat completion request to Z.ai.
func (p *Provider) CompleteStream(ctx context.Context, req core.Request) (<-chan core.StreamChunk, error) {
return openaicompat.PostStream(ctx, openaicompat.ChatParams{
HTTPClient: p.httpClient,
URL: p.baseURL + "/chat/completions",
Provider: p.name,
Label: "zai",
Headers: p.requestHeaders(),
}, req)
}
🤖 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 `@providers/zai/zai.go` around lines 90 - 109, Both Provider.Complete and
Provider.CompleteStream are duplicating the auth header construction instead of
using the shared Provider.AuthHeaders helper. Update these methods to call
p.AuthHeaders() when building the openaicompat.ChatParams Headers so the
Authorization logic stays centralized and any future auth changes only need to
be made in one place.

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