Skip to content

Z.ai plugin

The zai plugin gives Genkit access to Z.ai’s GLM models through Z.ai’s OpenAI-compatible chat completions endpoint. Models are named under the zai/ provider prefix.

Terminal window
go get github.com/firebase/genkit/go

Add &zai.ZAI{} to your plugin list. The plugin reads the API key from the ZAI_API_KEY environment variable.

package main
import (
"context"
"fmt"
"log"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/compat_oai/zai"
)
func main() {
ctx := context.Background()
g := genkit.Init(ctx,
genkit.WithPlugins(&zai.ZAI{}),
genkit.WithDefaultModel("zai/glm-5.1"),
)
text, err := genkit.GenerateText(ctx, g, ai.WithPrompt("Share a joke about bananas."))
if err != nil {
log.Fatalf("could not generate: %v", err)
}
fmt.Println(text)
}

You must provide an API key from Z.ai. You can get one from the Z.ai platform. Set ZAI_API_KEY, or set the APIKey field. Extra OpenAI client request options ride in Opts, applied after the plugin defaults so they win on overlap; option is github.com/openai/openai-go/option.

g := genkit.Init(ctx, genkit.WithPlugins(&zai.ZAI{
APIKey: os.Getenv("MY_ZAI_KEY"),
Opts: []option.RequestOption{
option.WithBaseURL("https://api.z.ai/api/paas/v4"),
},
}))

genkit.Init panics when neither the APIKey field nor ZAI_API_KEY is set. The endpoint defaults to https://api.z.ai/api/paas/v4; override it with the ZAI_BASE_URL environment variable or with option.WithBaseURL in Opts.

As always, avoid embedding API keys directly in your code.

The plugin registers these GLM models when it initializes:

  • glm-5.1, glm-5-turbo, glm-5
  • glm-4.7, glm-4.7-flash, glm-4.7-flashx
  • glm-4.6
  • glm-4.5, glm-4.5-air, glm-4.5-x, glm-4.5-airx, glm-4.5-flash
  • glm-4-32b-0414-128k

The vision models, which take images as well as text:

  • glm-5v-turbo, glm-4.6v, glm-4.6v-flash, glm-4.6v-flashx, glm-4.5v

That list is a starting point rather than a limit. Any other GLM model ID resolves on demand and takes the plugin’s text-only defaults, so a model Z.ai releases later works without a Genkit upgrade.

No GLM model advertises tool choice, so tool selection is always automatic and a forced tool choice is refused before the request goes out. Constrained generation is unclaimed too: Z.ai’s response_format takes text or json_object only, not json_schema, so an output schema reaches the model as prompt instructions and comes back as the same typed result.

zai.ModelRef pairs a model ID with a typed zai.ChatConfig, so the config is checked where you write it and validated against the model’s schema before the request goes out.

resp, err := genkit.Generate(ctx, g,
ai.WithModel(zai.ModelRef("glm-5.1", &zai.ChatConfig{
Thinking: &zai.ThinkingConfig{Type: zai.ThinkingTypeDisabled},
MaxOutputTokens: 1024,
})),
ai.WithPrompt("Share a joke about bananas."),
)
if err != nil {
log.Fatalf("could not generate: %v", err)
}
fmt.Println(resp.Text())

The ID passed to ModelRef works bare or provider-prefixed. You can also name a model as a string with ai.WithModelName("zai/glm-5.1") or genkit.WithDefaultModel, and pass the config separately with ai.WithConfig(&zai.ChatConfig{...}). The Z.ai sample runs this as a streaming flow you can call from the Dev UI.

zai.ChatConfig carries the generation fields Z.ai accepts plus its own controls:

FieldTypeNotes
Temperature*float64Randomness of token selection, 0 to 1, not the 2 OpenAI allows. The default varies by model.
TopP*float64Nucleus sampling threshold, 0.01 to 1.
MaxOutputTokensintSent as the API’s max_tokens, up to 131072.
StopSequences[]stringUp to four.
Thinking*zai.ThinkingConfigType is zai.ThinkingTypeEnabled, the Z.ai default, or zai.ThinkingTypeDisabled. ClearThinking decides whether the reasoning is cleared from the response, and Z.ai defaults it to true.
DoSample*boolfalse turns sampling off, which makes Temperature and TopP inert.

Z.ai documents no penalties, no log probabilities, and no seed, so those fields are deliberately absent. Pointer fields separate unset from a deliberate zero.

ChatConfig also embeds compat_oai.RequestConfig, which every plugin in the family shares: a per-request APIKey, a Version pin, and an Extra map whose keys ride to the wire verbatim under Z.ai’s own names. See the OpenAI-compatible plugin page.

Correcting what the plugin knows about a model

Section titled “Correcting what the plugin knows about a model”

Every GLM model works without an entry in Models. Supply one only to correct or extend the capabilities the plugin resolves, most often for a model released after your Genkit version. Keys are the model ID, bare or provider-prefixed, and fields left at their zero value keep what the plugin resolved.

g := genkit.Init(ctx, genkit.WithPlugins(&zai.ZAI{
Models: map[string]ai.ModelOptions{
// A model the plugin does not curate resolves with the text-only
// defaults, so an entry is how you tell Genkit it takes images.
"glm-4.7v": {
Supports: &ai.ModelSupports{
Multiturn: true,
Tools: true,
SystemRole: true,
Media: true,
},
},
},
}))

Reasoning text, streamed token usage, cached prompt tokens, and mid-generation provider failures are handled the same way for every plugin in this family. See the OpenAI-compatible plugin page.