Polyweave

@polyweave/zai (1.0.10)

Published 2026-07-11 15:35:29 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/zai@1.0.10
"@polyweave/zai": "1.0.10"

About this package

@polyweave/zai

Z.AI (ZhipuAI/GLM) adapter for Polyweave — multi-capability adapter supporting chat, image generation, video generation, audio transcription, and voice sessions through a single frame duplex.

Synopsis

import { createZaiAdapter } from '@polyweave/zai';
import { createTextFrame, createImageFrame, createAudioFrame, createParamFrame, createFrameSink, FrameType } from '@polyweave/core';

var adapter = createZaiAdapter({ apiKey: process.env.ZAI_API_KEY });

adapter.source.pipe(createFrameSink(function(frame) {
  switch (frame.type) {
    case FrameType.TEXT:    console.log('Text:', frame.content.text); break;
    case FrameType.IMAGE:   console.log('Image:', frame.content.url); break;
    case FrameType.VIDEO:   console.log('Video:', frame.content.url); break;
    case FrameType.RECEIPT: console.log('Cost:', frame.content.usage); break;
  }
}));

adapter.sink.write(createParamFrame('model', 'glm-4.7'));
adapter.sink.write(createTextFrame('Explain quantum computing'));

// Image generation
adapter.sink.write(createParamFrame('task', 'image'));
adapter.sink.write(createParamFrame('model', 'cogview-4'));
adapter.sink.write(createTextFrame('A serene mountain lake at dawn'));

// Audio transcription (send base64 audio data)
adapter.sink.write(createParamFrame('model', 'glm-asr-2512'));
adapter.sink.write(createAudioFrame(audioBase64Data, { mimeType: 'audio/mpeg' }));

Install

npm install @polyweave/zai

Why

Z.AI provides GLM-4.7 with thinking mode, CogView-4 image generation, CogVideoX-3/Vidu video generation, GLM-ASR audio transcription, and GLM-4-Voice realtime sessions — all through a single provider. The adapter unifies all five capabilities under one Polyweave frame duplex, routing frames to the correct API endpoint based on model name, task PARAM, or frame type. Agents can switch between text, image, video, audio, and voice workloads without changing adapters.

API

createZaiAdapter(config)

Creates a frame duplex adapter for Z.AI. Returns an IFrameDuplex with source, sink, setReady, name, ready, and apiKey.

Name Type Required Default Description
config.apiKey string No process.env.ZAI_API_KEY or process.env.ZHIPU_API_KEY Z.AI API bearer token
config.baseURL string No https://api.z.ai/api/paas/v4 API base URL override
config.timeout number No 60000 HTTP request timeout in milliseconds
config.useCodingEndpoint boolean No false When true, uses https://api.z.ai/api/coding/paas/v4 as the default base URL for GLM Coding Plan
config.name string No null Logical name for the node, exposed on the duplex

Return value: IFrameDuplex — a bidirectional frame stream with additional properties.

Properties on the returned duplex:

  • .source — readable frame source (output frames from the adapter)
  • .sink — writable frame sink (input frames into the adapter)
  • .setReady(key) — provide the API key after construction; flushes buffered frames
  • .name — logical name (config.name)
  • .ready — (getter) whether the adapter has an API key and is accepting frames
  • .apiKey — (getter) current API key

isVoiceModel(model)

Returns true if the model string contains 'voice' or 'realtime', indicating it should use the GLM-4-Voice WebSocket realtime API.

Frame Protocol

Frames are routed to one of six handlers based on model name, task PARAM, and frame type. The routing priority is:

  1. Voice: If isVoiceModel(model) → WebSocket GLM-4-Voice session
  2. Image: If model starts with cogview → image generation handler
  3. Video: If model starts with cogvideo or vidu → video generation handler
  4. Audio: If model starts with glm-asr → audio transcription handler
  5. Chat: All other models default to chat completion handler

Within the chat path, the task PARAM can override routing:

  • task = 'image' or 'generation' → image handler
  • task = 'video' → video handler
  • task = 'transcription' or 'audio' → audio handler
Input frame Action
TEXT Routed per above rules. Default: sends POST /chat/completions
IMAGE Vision mode (sent to chat handler with image_url) unless task=video or vidu model routes to video handler
AUDIO Audio transcription via POST /audio/transcriptions (multipart form-data)
TOOL_RESULT Accumulated into conversation as {role:'tool', tool_call_id, content}
TOOL_CALL Accumulated into conversation as {role:'assistant', tool_calls:[...]}
PARAM Accumulates into adapter params. key=apiKey triggers setReady
COST_CHECK Enters dry-run mode, estimates cost from buffered frames, emits COST + END. Budget check emits ERROR(BUDGET_EXCEEDED) if exceeded
ESTIMATED_TEXT/IMAGE/AUDIO/VIDEO/OUTPUT Buffered in cost check mode; ignored outside cost check mode
START Ignored
END Ignored
TOPOLOGY_SCAN Emits TOPOLOGY_REPORT declaring all receives/generates/types, then forwards the scan
Output frame Trigger
TEXT Chat content + reasoning content (thinking mode deltas tagged metadata.reasoning: true)
IMAGE One per generated image from CogView-4
VIDEO One per generated video from CogVideoX-3 or Vidu (after async polling completes)
AUDIO Voice session output audio (binary PCM buffers)
TOOL_CALL Tool call invocations from chat response
RECEIPT Cost receipt. Supports receipt_mode: 'none' | 'final' | 'progressive'
PROGRESS Video generation polling progress (0–1)
COST Estimated cost from COST_CHECK dry-run
ERROR Errors (see Error Conditions) followed by END
END Always emitted after each request completes (including errors)
META Passthrough for unknown frame types (with passthrough: true)

PARAM Keys

All PARAM keys accumulate into the adapter's internal params object.

Chat parameters:

Key Type Default Description
model string glm-4.7 Model ID (also drives handler routing)
max_tokens number undefined Max completion tokens
temperature number undefined Sampling temperature
top_p number undefined Nucleus sampling
stream boolean false Enable SSE streaming (defaults to false, unlike most adapters)
system string undefined System prompt
tools object[] undefined Tool definitions
tool_choice string undefined Tool choice constraint
response_format object undefined Response format
logprobs boolean undefined Return log probabilities
top_logprobs number undefined Number of top log probabilities
do_sample boolean undefined Enable sampling
stop string[] undefined Stop sequences
thinking object undefined Thinking mode: {type: 'enabled'|'disabled', clear_thinking: boolean}
messages object[] [] Pre-built message history

Routing parameters:

Key Type Default Description
task string undefined Routing hint: 'image'/'generation' → image, 'video' → video, 'transcription'/'audio' → audio

Image generation (CogView-4):

Key Type Default Description
size string undefined Image size (e.g. '1024x1024')
quality string undefined Quality setting
n number 1 Number of images to generate

Video generation (CogVideoX-3, Vidu):

Key Type Default Description
duration number undefined Video duration
fps number undefined Frames per second

Audio transcription (GLM-ASR):

Key Type Default Description
language string undefined Language code (e.g. 'zh', 'en')

Receipt settings:

Key Type Default Description
receipt_mode string 'final' 'none' disables receipts, 'final' emits one receipt at end, 'progressive' emits receipts every receipt_interval tokens
receipt_interval number 50 Token count between progressive receipts

Auth:

Key Type Default Description
apiKey string undefined Set or override API key at runtime (triggers setReady)

Voice session (GLM-4-Voice):

Key Type Default Description
voice string undefined Voice preset
instructions string undefined Session instructions (alias for system)
modalities string[] undefined Response modalities (e.g. ['text', 'audio'])

Chat Handler

Endpoint: POST /chat/completions

Streaming is disabled by default (stream: false). Set params.stream = true to enable SSE streaming.

Vision support: When an IMAGE frame is routed to chat, it is sent as image_url content blocks with optional text prompt. Vision support is available on GLM-4.6V and GLM-4.5V models.

Thinking mode: GLM-4.5+ supports thinking mode via the thinking PARAM. Set params.thinking = { type: 'enabled' } to enable. Reasoning content is emitted as TEXT frames with metadata.reasoning: true.

Tool calling: TOOL_CALL frames from the API are emitted. TOOL_RESULT/TOOL_CALL input frames accumulate into the message history for multi-turn conversations. Progressive receipts track estimated output tokens every receipt_interval tokens.

Cached tokens: Z.AI reports cached prompt tokens in usage.prompt_tokens_details.cached_tokens. Cached tokens are priced at a reduced rate (e.g. $0.11/MTok vs $0.60/MTok for regular input on GLM-4.7).

Image Handler

Endpoint: POST /images/generations

Model: CogView-4 (default cogview-4). Supports any resolution within range.

Input: TEXT frame with frame.content.text as the prompt. An IMAGE frame routed to the image handler (via model name match) also uses its text content as prompt.

Output: One IMAGE frame per generated image with url and revisedPrompt in content. RECEIPT uses calculateMediaCost with imageCount. END frame includes imageCount in metadata.

Cost: CogView-4 is $0.01 per image.

Video Handler

Endpoint: POST /videos/generations (async — returns a task ID, polled via GET /async-result/{id})

Models: CogVideoX-3 (default cogvideox-3), ViduQ1-Text, ViduQ1-Image, ViduQ1-Start-End, Vidu2-Image, Vidu2-Start-End, Vidu2-Reference.

Input: TEXT frame (text-to-video) or IMAGE frame (image-to-video) with optional prompt, image_url, optional duration, fps, and size.

Output: Async flow — emits PROGRESS frames (0 at start, then progress updates every 5s poll) followed by a VIDEO frame when complete. RECEIPT uses calculateMediaCost with videoCount: 1. END frame terminates the stream.

Polling: Up to 120 attempts at 5-second intervals (~10 minutes total). On timeout, emits ERROR('Video generation timed out', QUEUE_TIMEOUT).

Costs:

  • CogVideoX-3: $0.20/video
  • ViduQ1 series: $0.40/video
  • Vidu2-Image, Vidu2-Start-End: $0.20/video
  • Vidu2-Reference: $0.40/video

Audio Handler

Endpoint: POST /audio/transcriptions (multipart/form-data)

Model: GLM-ASR-2512 (default glm-asr-2512). Pricing: $0.03/MTok (~$0.0024/minute).

Input: AUDIO frame with base64 data content and optional mimeType (defaults to audio/mpeg). Optional language and filename PARAMs. URL-based input is NOT supported — emits ERROR(INVALID_INPUT).

Output: TEXT frame containing the transcription text. RECEIPT uses calculateMediaCost with audioDuration (from API response or estimated from byte length: data.length / 16000 seconds). END frame includes duration in metadata.

Estimated cost: Duration is estimated as Buffer.from(data, 'base64').length / 16000 bytes-per-second (rough MP3 estimate) if not provided by the API response.

Voice Handler

Protocol: GLM-4-Voice realtime via WebSocket (wss://open.bigmodel.cn/api/paas/v4/realtime)

Model: Any model containing 'voice' or 'realtime' (e.g. glm-4-voice). Routed automatically via isVoiceModel().

Session lifecycle: Created on first frame for a given streamId, cached in duplex._voiceSessions. Reused for subsequent frames on the same stream.

Input frames:

  • AUDIOinput_audio_buffer.append with base64-encoded PCM audio
  • TEXTconversation.item.create + response.create
  • PARAM with key=voice/instructions/modalities → updates session config
  • END → closes WebSocket session

Output frames:

  • AUDIOresponse.audio.delta / output_audio.delta binary audio
  • TEXTresponse.text.delta / output_text.delta (spoken text) and response.audio_transcript.delta (transcript)
  • TOOL_CALLresponse.function_call_arguments.done / function_call events
  • RECEIPT — from response.done usage data, with audioBytesIn/audioBytesOut metadata
  • END — on session close or response.done

Cost Check (COST_CHECK)

When a COST_CHECK frame is received, the adapter enters dry-run mode:

  1. Subsequent content frames are buffered in costCheckBuffer
  2. ESTIMATED_OUTPUT frames are buffered but do not trigger estimation
  3. On the next non-estimation content frame, costEstimator.estimate(params, costCheckBuffer, requestType) is called
  4. If costCheckOptions.maxBudget is set and the estimate exceeds it, ERROR(BUDGET_EXCEEDED) + END is emitted with the estimate in error metadata
  5. Otherwise, a COST frame is emitted containing bounds, accuracy, estimateSource, forwardEstimate, inputBounds, and maxOutputTokens, followed by END
  6. Estimation frames (ESTIMATED_TEXT/IMAGE/AUDIO/VIDEO) outside cost check mode are silently ignored

Error Conditions

Every error emits an ERROR frame immediately followed by an END frame with metadata.error: true. Errors from handlers go through mapZaiError() which maps HTTP status codes and error types to standardized error codes.

Chat handler:

Condition Error Message Error Code
Network request error 'Chat request failed: ' + err.message PROVIDER_ERROR
HTTP ≥ 400 API error message from response body, or fallback message PROVIDER_ERROR
Stream error 'Stream error: ' + err.message PROVIDER_ERROR
JSON parse failure 'Failed to parse chat response' PROTOCOL_ERROR

Image handler:

Condition Error Message Error Code
Missing prompt 'Image generation requires a text prompt' INVALID_INPUT
Network request error 'Image generation request failed: ' + err.message PROVIDER_ERROR
HTTP ≥ 400 API error message PROVIDER_ERROR
JSON parse failure 'Failed to parse image generation response' PROTOCOL_ERROR

Video handler:

Condition Error Message Error Code
Network request error 'Video generation request failed: ' + err.message PROVIDER_ERROR
HTTP ≥ 400 API error message PROVIDER_ERROR
JSON parse failure 'Failed to parse video generation response' PROTOCOL_ERROR
Poll timeout (10 min) 'Video generation timed out' QUEUE_TIMEOUT
Async task FAILED Response message PROVIDER_ERROR

Audio handler:

Condition Error Message Error Code
URL input (not supported) 'Audio URL not supported - please provide audio data directly' INVALID_INPUT
Missing audio data 'Audio transcription requires audio data' INVALID_INPUT
Network request error 'Audio transcription request failed: ' + err.message PROVIDER_ERROR
HTTP ≥ 400 API error message PROVIDER_ERROR
JSON parse failure 'Failed to parse transcription response' PROTOCOL_ERROR

Voice handler:

Condition Error Message Error Code
WebSocket close (non-1000) 'GLM-4-Voice session closed: ' + code + ' ' + reason PROVIDER_ERROR
WebSocket error 'GLM-4-Voice WebSocket error: ' + err.message PROVIDER_ERROR
Server error event event.error.message or 'GLM-4-Voice error' provider error code or PROVIDER_ERROR

COST_CHECK:

Condition Error Message Error Code
Estimate exceeds maxBudget 'Estimated cost $X.XXXXXX exceeds budget $Y' BUDGET_EXCEEDED

Adapter-level:

Condition Error Message Error Code
Sink paused or ended None (frame dropped)
Missing API key Frames buffered in frameBuffer
Unknown frame type META passthrough frame emitted
Unexpected exception in write mapZaiError(error, frame) based on status/type Varies by error

Error Mapper (mapZaiError)

The mapZaiError(error, frame) function in src/utils/errorMapper.js maps API errors to standardized error frames:

  • status === 401 || 403'Z.AI authentication failed. Check your API key.'
  • status === 429'Z.AI rate limit exceeded. Please wait and try again.'
  • status === 400 || 422 → error message or 'Invalid request parameters'
  • status === 404 → error message or 'Resource not found'
  • status >= 500'Z.AI server error. Please try again later.'

Error type strings are also mapped: authentication_errorAUTHENTICATION_ERROR, invalid_request_errorINVALID_INPUT, rate_limit_errorRATE_LIMIT_EXCEEDED, server_errorPROVIDER_ERROR.

Cost Functions

Re-exported from @polyweave/costs:

  • getModelCost(provider, model) — returns cost config object with mtoksInput, mtoksOutput, mtoksCached, perImage, perVideo, perMinute
  • calculateTokenCost(inputTokens, outputTokens, costConfig) — returns { inputCost, outputCost, totalCost }
  • calculateMediaCost(costConfig, { imageCount, videoCount, audioDuration }) — returns { totalCost }

Tests

npm test

Runs rigor.test.js (property-based cost verification: non-negative costs, zero-cost at zero tokens, monotonicity, pricing table oracle) and zai.test.js (80+ unit tests covering: cost configs for all model families, token/media cost calculations, RECEIPT frame structure for all modalities, error mapper status code mappings, retryable error detection, receipt settings defaults, request type detection by model prefix, thinking mode parameters, vision model identification, cached token savings, and progressive receipt interval calculation).

Requirements

  • Node.js 22 or later (as per engines field in package.json)
  • ESM only ("type": "module")
  • Peer dependencies: @polyweave/core, @polyweave/errors, @polyweave/costs, @polyweave/push-websocket

Dependencies

Development Dependencies

ID Version
@polyweave/core *
@polyweave/costs *
@polyweave/errors *
@polyweave/push-websocket *
@rigor/core *

Peer Dependencies

ID Version
@polyweave/core *
@polyweave/costs *
@polyweave/errors *
@polyweave/push-websocket *

Keywords

polyweave zai ai adapter
Details
npm
2026-07-11 15:35:29 +00:00
4
John Dvorak
SEE LICENSE IN LICENSE
latest
21 KiB
Assets (1)
Versions (3) View all
1.0.10 2026-07-11
1.0.7 2026-07-11
1.0.6 2026-07-10