This documentation plan provides a comprehensive strategy for creating, maintaining, and evolving the documentation for the Z.ai Ruby SDK. The plan focuses on making the SDK accessible to Ruby developers while ensuring documentation stays in sync with the codebase and follows Ruby community standards.
The Z.ai Ruby SDK supports:
- Ruby >= 3.2.8 (minimum version)
- JRuby >= 10.0.4.0 (minimum version)
These requirements ensure compatibility with modern Ruby features while maintaining broad support across different Ruby implementations.
| Ruby Version | Status | End of Life | Notes |
|---|---|---|---|
| 3.2.8+ | ✅ Supported | TBA | Minimum supported version |
| 3.3.0+ | ✅ Supported | Current | Recommended version |
| 3.1.x | ❌ Not Supported | 2025-03-31 | Below minimum requirement |
| 3.0.x | ❌ Not Supported | 2024-03-31 | Below minimum requirement |
| JRuby Version | Status | End of Life | Notes |
|---|---|---|---|
| 10.0.4.0+ | ✅ Supported | TBA | Minimum supported version |
| 9.4.x | ❌ Not Supported | 2024-12-31 | Below minimum requirement |
The SDK leverages Ruby 3.2+ features when available:
- Data Classes: Using
datakeyword for immutable structs - Enhanced Pattern Matching: Improved response parsing
- Performance Improvements: YJIT optimizations and memory enhancements
- Anonymous Method Parameters: Cleaner method signatures
On JRuby, the SDK provides:
- True Parallelism: Concurrent processing capabilities
- Java Integration: Access to Java libraries for performance
- Native Threads: Improved concurrency for batch operations
# Verify Ruby version (must be >= 3.2.8)
ruby --version
# Install the gem
gem install zai-ruby-sdk
# Or add to Gemfile
echo "gem 'zai-ruby-sdk'" >> Gemfile
bundle install# Install JRuby using rbenv
rbenv install jruby-10.0.4.0
rbenv local jruby-10.0.4.0
# Verify JRuby version
ruby --version
# Install with JRuby-specific optimizations
jruby -S gem install zai-ruby-sdkIf you're currently using Ruby 3.0 or 3.1:
- Upgrade Ruby: Upgrade to Ruby 3.2.8 or later
- Review Dependencies: Ensure all dependencies are compatible
- Test Application: Run your test suite after upgrading
- Update CI/CD: Update your CI pipelines to use Ruby 3.2.8+
Issue: Gem::InstallError: zai-ruby-sdk requires Ruby version >= 3.2.8
Solution:
# Using rbenv
rbenv install 3.2.8
rbenv local 3.2.8
# Using rvm
rvm install 3.2.8
rvm use 3.2.8
# Using system package manager (Ubuntu/Debian)
sudo apt-get install ruby3.2Issue: JRuby performance problems
Solution:
# Enable JRuby optimizations
require 'zai'
Zai::JRuby.optimize_performance!
# Or configure manually
if defined?(JRUBY_VERSION)
java.lang.System.setProperty("jruby.thread.pool.enabled", "true")
java.lang.System.setProperty("jruby.compile.invokedynamic", "true")
end# Z.ai Ruby SDK
[](https://badge.fury.io/rb/zai-ruby-sdk)
[](https://github.com/z-ai-ruby/sdk/actions)
[](https://yardoc.org/github/z-ai-ruby/sdk)
[](https://opensource.org/licenses/MIT)
[](https://www.ruby-lang.org/)
[](https://www.jruby.org/)
The official Ruby SDK for Z.ai's AI services, providing idiomatic Ruby interfaces to powerful AI models including GLM language models, embeddings, image generation, and more.
## Requirements
- **Ruby >= 3.2.8** (minimum supported version)
- **JRuby >= 10.0.4.0** (for JRuby users)
- **Ruby 3.3.x** (recommended for best performance)
## ⚡ Quick Start
### Installation
Add this line to your application's Gemfile:
```ruby
gem 'zai-ruby-sdk'And then execute:
$ bundle installOr install it yourself as:
$ gem install zai-ruby-sdkrequire 'zai'
# Initialize client for overseas API
client = Zai.overseas_client(api_key: "your-api-key")
# Simple chat completion
response = client.chat.completions(
messages: "Hello, world!",
model: "glm-5"
)
puts response.first_message- 🎯 Simple, idiomatic Ruby interface - Designed by Ruby developers for Ruby developers
- 💬 Chat completions with streaming support - Real-time response generation
- 🔍 Text embeddings - Convert text to vector representations
- 🖼️ Image generation - Create images from text descriptions
- 🎥 Video generation - Generate video content
- 🔊 Audio processing - Speech-to-text and text-to-speech
- 📁 File management - Upload and manage files
- 🤖 Assistant API - Build custom AI assistants
- 🔐 Robust authentication - API key and JWT token support
- 🔄 Automatic retry mechanism - Built-in resilience
- 🧪 Comprehensive test coverage - Reliable and well-tested
- 📖 Full API documentation - Complete reference guide
- API Reference - Complete API documentation
- Examples - Practical usage examples
- Guides - In-depth tutorials and guides
- Migration from Python SDK - For Python developers
client = Zai.client(
api_key: "your-key",
base_url: "https://api.z.ai/api/paas/v4", # or China URL
timeout: 30,
max_retries: 3,
disable_token_cache: false
)export ZAI_API_KEY="your-api-key"
export ZAI_BASE_URL="https://api.z.ai/api/paas/v4"
export ZAI_TIMEOUT=30# Simple completion
response = client.chat.completions(
messages: "What is Ruby?",
model: "glm-5",
max_tokens: 100
)
# Conversation with context
messages = [
{ role: "system", content: "You are a helpful Ruby expert." },
{ role: "user", content: "What's the difference between Proc and lambda?" },
{ role: "assistant", content: "In Ruby..." },
{ role: "user", content: "Can you give me an example?" }
]
response = client.chat.completions(messages: messages)# Block-style streaming (Ruby idiomatic)
client.chat.stream(messages: "Tell me a story") do |chunk|
print chunk.dig(:choices, 0, :delta, :content)
end
# Enumerator-style (functional)
enum = client.chat.stream(messages: "Explain quantum computing")
enum.each { |chunk| process_chunk(chunk) }response = client.embeddings.embeddings(
input: ["Hello world", "Goodbye world"],
model: "text-embedding-ada-002"
)
vectors = response.data.map { |item| item.embedding }begin
response = client.chat.completions(messages: "Hello")
rescue Zai::AuthenticationError => e
puts "Authentication failed: #{e.message}"
rescue Zai::RateLimitError => e
puts "Rate limit exceeded. Please wait."
rescue Zai::APIError => e
puts "API error (#{e.status_code}): #{e.message}"
end# Create a custom client
client = Zai.client do |config|
config.api_key = "your-key"
config.base_url = "https://api.z.ai/api/paas/v4"
config.timeout = 60
config.max_retries = 5
config.disable_token_cache = false
config.source_channel = "my-app"
endThe SDK follows a modular architecture:
Zai
├── Client # Main client interface
├── Configuration # Configuration management
├── API
│ ├── Chat # Chat completions API
│ ├── Embeddings # Text embeddings API
│ ├── Images # Image generation API
│ ├── Videos # Video generation API
│ ├── Audio # Audio processing API
│ ├── Files # File management API
│ └── Assistants # Assistant API
├── Models # Data models and types
├── Auth # Authentication modules
└── Utils # Utility functions
Run the test suite:
bundle exec rake specBug reports and pull requests are welcome on GitHub at https://github.com/z-ai-ruby/sdk. Please see our Contributing Guide for details.
The gem is available as open source under the terms of the MIT License.
See our Roadmap for upcoming features and releases.
### 1.2 CHANGELOG.md Format
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- New feature descriptions
### Changed
- Changes in existing functionality
### Deprecated
- Features that will be removed in future versions
### Removed
- Features removed in this version
### Fixed
- Bug fixes
### Security
- Security-related changes
## [1.0.0] - 2024-01-15
### Added
- Initial release of Z.ai Ruby SDK
- Chat completions API with streaming support
- Text embeddings API
- Image generation API
- Authentication with API keys and JWT tokens
- Automatic retry mechanism
- Comprehensive error handling
- Full API documentation
## [0.9.0] - 2024-01-01
### Added
- Beta release
- Core chat functionality
- Basic authentication
- Initial documentation
Using YARD for API documentation with consistent formatting:
module Zai
module API
# Chat API for generating completions using Z.ai's language models
#
# This class provides methods to interact with Z.ai's chat completion API,
# supporting both synchronous and streaming responses.
#
# @example Simple completion
# client = Zai.overseas_client(api_key: "your-key")
# response = client.chat.completions(
# messages: "Hello, world!",
# model: "glm-5"
# )
# puts response.first_message
#
# @example Streaming completion with block
# client.chat.stream(messages: "Count to 10") do |chunk|
# print chunk.dig(:choices, 0, :delta, :content)
# end
#
# @example Conversation with context
# messages = [
# { role: "system", content: "You are a helpful assistant." },
# { role: "user", content: "What is Ruby?" }
# ]
# response = client.chat.completions(messages: messages)
#
# @see https://platform.z.ai/docs/api-reference/chat
# @since 1.0.0
class Chat < Base
# Generate chat completions using Z.ai's language models
#
# Creates a model response for the given chat conversation.
#
# @param messages [String, Array<Hash, ChatMessage>] The conversation messages
# - When String: Creates a single user message
# - When Array: Array of message objects with role and content
# @param model [String] The model to use for completion (default: "glm-5")
# @param temperature [Float] Sampling temperature between 0.0 and 1.0
# - Higher values (like 0.8) make output more random
# - Lower values (like 0.2) make output more focused and deterministic
# @param max_tokens [Integer] Maximum number of tokens to generate
# @param top_p [Float] Nucleus sampling parameter (0.0 to 1.0)
# @param stream [Boolean] Whether to stream the response
# @param stop [String, Array<String>] Sequences where the API will stop generating
# @param presence_penalty [Float] Penalize new tokens based on presence
# @param frequency_penalty [Float] Penalize new tokens based on frequency
# @param options [Hash] Additional options to pass to the API
#
# @return [ChatCompletionResponse] The completion response object
#
# @raise [InvalidRequestError] When request parameters are invalid
# @raise [AuthenticationError] When authentication fails
# @raise [RateLimitError] When rate limit is exceeded
# @raise [ServerError] When server error occurs
# @raise [TimeoutError] When request times out
#
# @example Simple request
# response = completions(
# messages: "Hello, world!",
# model: "glm-5",
# max_tokens: 100
# )
#
# @example With array of messages
# messages = [
# { role: "system", content: "You are a helpful assistant." },
# { role: "user", content: "What is 2+2?" }
# ]
# response = completions(messages: messages)
#
# @example With custom parameters
# response = completions(
# messages: "Write a poem",
# model: "glm-5",
# temperature: 0.8,
# max_tokens: 200,
# top_p: 0.9
# )
#
# @see https://platform.z.ai/docs/api-reference/chat/create
def completions(messages:, model: "glm-5", **options)
# Implementation...
end
# Stream chat completions in real-time
#
# When called with a block, yields each chunk as it arrives.
# When called without a block, returns an Enumerator for lazy processing.
#
# @param messages [String, Array<Hash, ChatMessage>] The conversation messages
# @param model [String] The model to use (default: "glm-5")
# @param temperature [Float] Sampling temperature (0.0-1.0)
# @param max_tokens [Integer] Maximum tokens in response
# @param top_p [Float] Nucleus sampling parameter
# @param stop [String, Array<String>] Stop sequences
# @param options [Hash] Additional options
# @yield [chunk] Each streaming chunk as it arrives
# @yieldparam chunk [Hash] The streaming chunk data
#
# @return [Enumerator, nil] Enumerator when called without block
#
# @example Block-style streaming
# stream(messages: "Tell me a story") do |chunk|
# print chunk.dig(:choices, 0, :delta, :content)
# end
#
# @example Enumerator-style streaming
# enum = stream(messages: "Count to 10")
# enum.each { |chunk| process_chunk(chunk) }
#
# @example Collecting all chunks
# chunks = []
# stream(messages: "Explain Ruby") { |chunk| chunks << chunk }
# full_response = chunks.map { |c| c.dig(:choices, 0, :delta, :content) }.join
#
# @see https://platform.z.ai/docs/api-reference/chat/create-stream
def stream(messages:, model: "glm-5", **options, &block)
# Implementation...
end
end
end
end# Getting Started with Z.ai Ruby SDK
This guide will help you get up and running with the Z.ai Ruby SDK quickly.
## Prerequisites
- Ruby 2.7+ (Ruby 3.0+ recommended)
- A Z.ai API key (get one at https://platform.z.ai)
## Installation
### Option 1: Bundler (Recommended)
Add to your Gemfile:
```ruby
gem 'zai-ruby-sdk'Then run:
bundle installgem install zai-ruby-sdk# Option A: Direct initialization
client = Zai.overseas_client(api_key: "your-api-key-here")
# Option B: Environment variable
# Set in your shell:
# export ZAI_API_KEY="your-api-key-here"
client = Zai.overseas_clientrequire 'zai'
client = Zai.overseas_client(api_key: ENV["ZAI_API_KEY"])
response = client.chat.completions(
messages: "Hello, Z.ai! Tell me something interesting about Ruby.",
model: "glm-5"
)
puts response.first_messageSave the code to hello_zai.rb and run:
ruby hello_zai.rbThe response object contains:
response.id # Unique identifier for the completion
response.object # Type of object (usually "chat.completion")
response.created # Unix timestamp of creation
response.model # Model used for completion
response.choices # Array of completion choices
response.usage # Token usage information
# Get the main response text
response.first_message- Explore Chat Completions
- Learn about Streaming
- Check out Examples
- Read the API Reference
#### 2.1.2 Chat Completions Guide (`guides/chat-completions.md`)
```markdown
# Chat Completions Guide
This guide covers using the chat completions API to generate text responses from Z.ai's language models.
## Basics
### Simple Completion
```ruby
response = client.chat.completions(
messages: "What is Ruby?",
model: "glm-5"
)
puts response.first_message
Maintain context by providing a conversation history:
messages = [
{ role: "system", content: "You are a helpful Ruby expert." },
{ role: "user", content: "What's the difference between a class and a module?" },
{ role: "assistant", content: "In Ruby..." },
{ role: "user", content: "Can you show me an example?" }
]
response = client.chat.completions(messages: messages)
puts response.first_messageControl randomness in the response (0.0 to 1.0):
# More deterministic
response = client.chat.completions(
messages: "Explain Ruby's modules",
temperature: 0.2
)
# More creative
response = client.chat.completions(
messages: "Write a creative story about Ruby",
temperature: 0.8
)Limit response length:
response = client.chat.completions(
messages: "Tell me about Ruby's history",
max_tokens: 100
)Alternative to temperature for nucleus sampling:
response = client.chat.completions(
messages: "Explain metaprogramming in Ruby",
top_p: 0.9
)Get responses as they're generated:
client.chat.stream(messages: "Write a Ruby poem") do |chunk|
content = chunk.dig(:choices, 0, :delta, :content)
print content if content
end
# Or with Enumerator
enum = client.chat.stream(messages: "Count to 10")
enum.each { |chunk| print chunk.dig(:choices, 0, :delta, :content) }begin
response = client.chat.completions(messages: "Hello")
rescue Zai::AuthenticationError => e
puts "Check your API key: #{e.message}"
rescue Zai::RateLimitError => e
puts "Rate limit exceeded. Try again later."
rescue Zai::APIError => e
puts "API error: #{e.message}"
end- Include system messages for better context
- Use appropriate temperature based on your use case
- Handle errors gracefully
- Use streaming for long responses
- Monitor token usage with response.usage
messages = [
{ role: "system", content: "You are a Ruby expert who writes clean, idiomatic code." },
{ role: "user", content: "Write a Ruby method to validate email addresses." }
]
response = client.chat.completions(
messages: messages,
temperature: 0.3
)
puts response.first_messagemessages = [
{ role: "system", content: "You are a technical writer." },
{ role: "user", content: "Write documentation for this Ruby method: def calculate_total(items) items.sum(&:price) end" }
]
response = client.chat.completions(
messages: messages,
temperature: 0.2
)
### 2.2 API Reference Documentation
#### 2.2.1 Client Class Documentation
```ruby
module Zai
# Main client class for interacting with Z.ai's API
#
# The Client is the primary interface to the Z.ai SDK. It manages
# configuration, authentication, and provides access to all API resources.
#
# @example Creating a client
# client = Zai.client(
# api_key: "your-key",
# base_url: "https://api.z.ai/api/paas/v4"
# )
#
# @example Using factory methods
# overseas = Zai.overseas_client(api_key: "key")
# china = Zai.china_client(api_key: "key")
#
# @see https://platform.z.ai/docs
class Client
# Initialize a new client with configuration options
#
# @param api_key [String] Your Z.ai API key
# @param base_url [String] API base URL
# @param timeout [Integer] Request timeout in seconds (default: 30)
# @param max_retries [Integer] Maximum retry attempts (default: 3)
# @param disable_token_cache [Boolean] Disable JWT token caching (default: true)
# @param source_channel [String] Source channel identifier (default: "ruby-sdk")
# @param cache [Object] Custom cache implementation (optional)
#
# @return [Client] A new client instance
#
# @example Basic initialization
# client = Zai::Client.new(api_key: "your-key")
#
# @example With custom configuration
# client = Zai::Client.new(
# api_key: "key",
# base_url: "https://api.z.ai/api/paas/v4",
# timeout: 60,
# max_retries: 5
# )
def initialize(**options)
# Implementation...
end
# Access the chat API resource
#
# @return [API::Chat] Chat API interface
#
# @example Simple chat completion
# response = client.chat.completions(
# messages: "Hello, world!"
# )
def chat
# Implementation...
end
# Access the embeddings API resource
#
# @return [API::Embeddings] Embeddings API interface
#
# @example Create text embeddings
# response = client.embeddings.embeddings(
# input: "Hello, world!"
# )
def embeddings
# Implementation...
end
# Access the images API resource
#
# @return [API::Images] Images API interface
#
# @example Generate an image
# response = client.images.generations(
# prompt: "A ruby gem in space",
# n: 1,
# size: "1024x1024"
# )
def images
# Implementation...
end
# Access the videos API resource
#
# @return [API::Videos] Videos API interface
def videos
# Implementation...
end
# Access the audio API resource
#
# @return [API::Audio] Audio API interface
def audio
# Implementation...
end
# Access the files API resource
#
# @return [API::Files] Files API interface
def files
# Implementation...
end
# Access the assistants API resource
#
# @return [API::Assistants] Assistants API interface
def assistants
# Implementation...
end
end
end
# Contributing to Z.ai Ruby SDK
Thank you for your interest in contributing to the Z.ai Ruby SDK! This guide will help you get started.
## Development Setup
### Prerequisites
- Ruby 2.7+ (Ruby 3.0+ recommended for development)
- Git
- A text editor or IDE
### Fork and Clone
1. Fork the repository on GitHub
2. Clone your fork locally:
```bash
git clone https://github.com/your-username/z-ai-ruby-sdk.git
cd z-ai-ruby-sdk- Add the upstream remote:
git remote add upstream https://github.com/z-ai-ruby/sdk.git
bundle installbundle exec rake specbundle exec rubocopgit checkout -b feature/your-feature-name- Write clear, idiomatic Ruby code
- Follow the existing code style
- Add tests for new functionality
- Update documentation
bundle exec rake specMake sure all tests pass before submitting a pull request.
bundle exec rubocop
bundle exec rake typecheck # If RBS type checking is set upFollow our commit message convention (see below).
git commit -m "feat: add new feature description"git push origin feature/your-feature-nameThen create a pull request on GitHub.
We follow the Ruby community style guidelines enforced by RuboCop:
bundle exec rubocop -a # Auto-fix offenses- Use snake_case for methods and variables
- Use PascalCase for classes and modules
- Use SCREAMING_SNAKE_CASE for constants
- Document all public methods with YARD
- Include examples for complex methods
- Keep documentation up to date with code changes
- Write tests for all new functionality
- Use descriptive test descriptions
- Mock external dependencies
- Cover edge cases and error conditions
We use conventional commits:
feat:for new featuresfix:for bug fixesdocs:for documentation changesstyle:for code style changesrefactor:for code refactoringtest:for adding or updating testschore:for maintenance tasks
Examples:
feat: add streaming support for chat completions
fix: handle JWT token expiration gracefully
docs: update README with installation instructions
test: add integration tests for embeddings API
Unit tests are located in spec/zai/. Run them with:
bundle exec rspec spec/zai/Integration tests are in spec/integration/. These tests make real API calls and require an API key:
ZAI_API_KEY=your-key bundle exec rspec spec/integration/We use VCR to record and replay HTTP interactions. When adding new tests:
- Run the test once to record the cassette
- Review the recorded cassette for sensitive data
- Commit the cassette with your test changes
API documentation is generated from YARD comments. To generate locally:
bundle exec yard docTo serve documentation:
bundle exec yard serverAdd examples to the examples/ directory:
- Keep examples simple and focused
- Include error handling
- Use environment variables for API keys
- Update version number in
lib/zai/version.rb - Update
CHANGELOG.md - Create a pull request for review
- Merge after approval
- Tag the release:
git tag v1.2.3 - Push tag:
git push origin v1.2.3
- Check existing issues and discussions
- Create a new issue for questions or bugs
- Join our community discussions
By contributing, you agree that your contributions will be licensed under the MIT License.
### 2.4 Integration Guides
#### 2.4.1 Rails Integration Guide (`guides/rails-integration.md`)
```markdown
# Rails Integration Guide
This guide shows how to integrate the Z.ai Ruby SDK with Ruby on Rails applications.
## Installation
Add to your Gemfile:
```ruby
gem 'zai-ruby-sdk'
Then run:
bundle installCreate config/initializers/zai.rb:
# config/initializers/zai.rb
Rails.application.configure do
config.zai = ActiveSupport::OrderedOptions.new
# Configure your Z.ai client
config.zai.api_key = ENV.fetch('ZAI_API_KEY')
config.zai.base_url = ENV.fetch('ZAI_BASE_URL', 'https://api.z.ai/api/paas/v4')
config.zai.timeout = 30
config.zai.max_retries = 3
end
# Create a global client
ZAI_CLIENT = Zai.client(
api_key: Rails.application.config.zai.api_key,
base_url: Rails.application.config.zai.base_url,
timeout: Rails.application.config.zai.timeout,
max_retries: Rails.application.config.zai.max_retries
)Add to .env (use dotenv-rails in development):
ZAI_API_KEY=your-api-key
ZAI_BASE_URL=https://api.z.ai/api/paas/v4class ChatController < ApplicationController
def create
response = ZAI_CLIENT.chat.completions(
messages: chat_params[:message],
model: "glm-5"
)
render json: { response: response.first_message }
rescue Zai::APIError => e
render json: { error: e.message }, status: :unprocessable_entity
end
private
def chat_params
params.require(:chat).permit(:message)
end
endclass Post < ApplicationRecord
def generate_summary
response = ZAI_CLIENT.chat.completions(
messages: "Summarize this post: #{content}",
model: "glm-5",
max_tokens: 100
)
update!(summary: response.first_message)
end
endclass ContentGeneratorService
def initialize(topic:)
@topic = topic
end
def generate_blog_post
response = ZAI_CLIENT.chat.completions(
messages: [
{ role: "system", content: "You are a blog post writer." },
{ role: "user", content: "Write a blog post about #{@topic}" }
],
model: "glm-5",
temperature: 0.7,
max_tokens: 1000
)
response.first_message
end
private
attr_reader :topic
endclass GenerateContentJob < ApplicationJob
queue_as :default
def perform(post_id)
post = Post.find(post_id)
response = ZAI_CLIENT.chat.completions(
messages: "Generate content for: #{post.title}",
model: "glm-5"
)
post.update!(content: response.first_message)
rescue Zai::APIError => e
Rails.logger.error "Failed to generate content: #{e.message}"
raise
end
endclass StreamController < ApplicationController
def chat
response.headers['Content-Type'] = 'text/event-stream'
response.headers['Cache-Control'] = 'no-cache'
self.response_body = Enumerator.new do |stream|
ZAI_CLIENT.chat.stream(messages: params[:message]) do |chunk|
content = chunk.dig(:choices, 0, :delta, :content)
stream << "data: #{content}\n\n" if content
end
end
end
endclass ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room_id]}"
end
def speak(data)
Thread.new do
ZAI_CLIENT.chat.stream(messages: data['message']) do |chunk|
content = chunk.dig(:choices, 0, :delta, :content)
ActionCable.server.broadcast(
"chat_#{params[:room_id]}",
{ content: content }
) if content
end
end
end
endmodule ZaiErrorHandler
def self.call(exception)
case exception
when Zai::AuthenticationError
Rails.logger.error "Z.ai authentication failed: #{exception.message}"
# Handle authentication errors
when Zai::RateLimitError
Rails.logger.warn "Z.ai rate limit exceeded: #{exception.message}"
# Handle rate limiting
when Zai::APIError
Rails.logger.error "Z.ai API error: #{exception.message}"
# Handle general API errors
end
end
endclass ApplicationController < ActionController::Base
rescue_from Zai::APIError, with: :handle_zai_error
private
def handle_zai_error(exception)
ZaiErrorHandler.call(exception)
render json: { error: 'An error occurred with the AI service' }, status: :service_unavailable
end
end# spec/controllers/chat_controller_spec.rb
require 'rails_helper'
RSpec.describe ChatController, type: :controller do
let(:mock_response) { double("Response", first_message: "Hello!") }
before do
allow(ZAI_CLIENT).to receive_message_chain(:chat, :completions).and_return(mock_response)
end
describe "POST #create" do
it "returns a chat response" do
post :create, params: { chat: { message: "Hello" } }
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body)['response']).to eq("Hello!")
end
end
end# spec/services/content_generator_service_spec.rb
require 'rails_helper'
RSpec.describe ContentGeneratorService, type: :service, vcr: true do
describe "#generate_blog_post" do
it "generates blog post content" do
service = described_class.new(topic: "Ruby programming")
content = service.generate_blog_post
expect(content).to be_a(String)
expect(content).not_to be_empty
end
end
endclass CachedChatService
def initialize(topic:)
@topic = topic
end
def generate_response
Rails.cache.fetch("chat_response_#{Digest::MD5.hexdigest(@topic)}", expires_in: 1.hour) do
response = ZAI_CLIENT.chat.completions(
messages: @topic,
model: "glm-5"
)
response.first_message
end
end
private
attr_reader :topic
endclass AiResponseJob < ApplicationJob
queue_as :ai_requests
def perform(prompt, callback_url)
response = ZAI_CLIENT.chat.completions(messages: prompt)
# Send response via webhook
HTTP.post(callback_url, json: { response: response.first_message })
rescue Zai::APIError => e
# Handle error and possibly retry
end
endNever commit API keys to your repository. Use:
- Environment variables
- Rails encrypted credentials
- Secret management services
# Using encrypted credentials
api_key = Rails.application.credentials.zai[:api_key]
# Using environment variables
api_key = ENV.fetch('ZAI_API_KEY')Always validate and sanitize user input before sending to the API:
class ValidatedChatService
MAX_MESSAGE_LENGTH = 4000
def initialize(message:)
@message = message
end
def generate_response
validate_message!
ZAI_CLIENT.chat.completions(messages: @message)
end
private
def validate_message!
if @message.length > MAX_MESSAGE_LENGTH
raise ArgumentError, "Message too long"
end
# Add more validation as needed
end
attr_reader :message
endheroku config:set ZAI_API_KEY=your-api-key
heroku config:set ZAI_BASE_URL=https://api.z.ai/api/paas/v4FROM ruby:3.2-slim
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install --without development test
COPY . .
ENV ZAI_API_KEY="" # Set at runtime
ENV ZAI_BASE_URL="https://api.z.ai/api/paas/v4"
CMD ["rails", "server", "-b", "0.0.0.0"]
#### 2.4.2 Sinatra Integration Guide (`guides/sinatra-integration.md`)
```markdown
# Sinatra Integration Guide
This guide shows how to integrate the Z.ai Ruby SDK with Sinatra applications.
## Installation
Add to your Gemfile:
```ruby
gem 'sinatra'
gem 'zai-ruby-sdk'
# app.rb
require 'sinatra'
require 'zai'
# Configure Zai client
configure do
set :zai_client, Zai.client(
api_key: ENV.fetch('ZAI_API_KEY'),
base_url: ENV.fetch('ZAI_BASE_URL', 'https://api.z.ai/api/paas/v4')
)
end
# Routes
get '/' do
"Z.ai + Sinatra Integration"
end
post '/chat' do
message = params[:message]
response = settings.zai_client.chat.completions(
messages: message,
model: "glm-5"
)
{ response: response.first_message }.to_json
enderror Zai::APIError do
status 502
content_type :json
{ error: 'AI service error', message: env['sinatra.error'].message }.to_json
end
error Zai::AuthenticationError do
status 401
content_type :json
{ error: 'Authentication failed' }.to_json
endget '/stream' do
content_type 'text/event-stream'
stream(:keep_open) do |out|
settings.zai_client.chat.stream(messages: params[:message]) do |chunk|
content = chunk.dig(:choices, 0, :delta, :content)
out << "data: #{content}\n\n" if content
end
end
end# extensions/zai_extension.rb
module Sinatra
module ZaiExtension
def zai_client
@zai_client ||= Zai.client(
api_key: ENV.fetch('ZAI_API_KEY'),
base_url: ENV.fetch('ZAI_BASE_URL', 'https://api.z.ai/api/paas/v4')
)
end
end
register ZaiExtension
end
# app.rb
require 'sinatra'
require_relative 'extensions/zai_extension'
get '/chat' do
response = zai_client.chat.completions(
messages: params[:message],
model: "glm-5"
)
erb :chat_response, locals: { response: response }
end# spec/app_spec.rb
require 'spec_helper'
require 'sinatra'
require_relative '../app'
describe Sinatra::Application do
let(:mock_response) { double("Response", first_message: "Hello!") }
before do
allow_any_instance_of(Sinatra::Application).to receive(:zai_client).and_return(
double("Client", chat: double("Chat", completions: mock_response))
)
end
describe 'POST /chat' do
it 'returns a chat response' do
post '/chat', { message: 'Hello' }, { 'CONTENT_TYPE' => 'application/json' }
expect(last_response).to be_ok
expect(JSON.parse(last_response.body)['response']).to eq('Hello!')
end
end
end# config.ru
require './app'
run Sinatra::Applicationexport ZAI_API_KEY="your-api-key"
export ZAI_BASE_URL="https://api.z.ai/api/paas/v4"# app.rb
require 'sinatra'
require 'zai'
# Ensure Sinatra runs in production mode
set :environment, :production
# Configure client
set :zai_client, Zai.client(
api_key: ENV.fetch('ZAI_API_KEY'),
base_url: ENV.fetch('ZAI_BASE_URL', 'https://api.z.ai/api/paas/v4')
)
## 3. Documentation Tools
### 3.1 Recommended Tool Stack
#### Primary Tools
1. **YARD** - For API documentation generation
- Install: `gem install yard`
- Generate: `yardoc`
- Serve: `yard server`
- Deploy to rubydoc.info via GitHub integration
2. **GitHub Pages** - For static documentation hosting
- Jekyll for site generation
- Custom theme matching Z.ai brand
- Multi-language support
3. **ReDoc** - For OpenAPI/Swagger documentation
- If we generate OpenAPI specs from Ruby code
#### Secondary Tools
1. **RDoc** - For Ruby core documentation (backup)
2. **Metrictank** - For documentation metrics
3. **Grimoire** - For documentation improvement suggestions
### 3.2 YARD Configuration
#### `.yardopts` File
--no-private --embed-mixins --embed-inheritance --markup markdown --markup-provider redcarpet --exclude 'spec|test|examples' --load lib/zai.rb
#### `.yard/yard_template.rb` for Custom Template
```ruby
def htmlify(text)
# Custom HTML processing
end
def format_type(type)
# Custom type formatting
end
# .github/workflows/docs.yml
name: Documentation
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
jobs:
build-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install dependencies
run: |
gem install yard redcarpet
bundle install
- name: Generate documentation
run: |
yardoc --output-dir docs/api
- name: Generate guides
run: |
mkdir -p docs/guides
cp guides/*.md docs/guides/
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
- name: Update RubyDoc.info
if: startsWith(github.ref, 'refs/tags/')
run: |
# Trigger rubydoc.info update via webhook
curl -X POST -d "token=${{ secrets.RUBYDOC_WEBHOOK_TOKEN }}" \
https://rubydoc.info/github/z-ai-ruby/sdk/webhook-
RubyDoc.info - Primary API documentation
- Auto-updated on releases
- Indexed by search engines
- Versioned documentation
-
GitHub Pages - Comprehensive documentation site
- Guides and tutorials
- Examples and code snippets
- Multiple language versions
-
Platform.z.ai - Official documentation
- Cross-language reference
- Platform-specific guides
- Integration with developer portal
- Use GitHub's CDN for static assets
- Implement caching headers
- Optimize images and diagrams
- Minify CSS and JavaScript
#!/usr/bin/env ruby
# frozen_string_literal: true
# Quick start example for Z.ai Ruby SDK
# This example demonstrates basic chat completion functionality
require 'zai'
# Initialize the client
# You can get an API key from https://platform.z.ai
client = Zai.overseas_client(
api_key: ENV.fetch('ZAI_API_KEY') {
puts "Please set ZAI_API_KEY environment variable"
exit 1
}
)
puts "Z.ai Ruby SDK Quick Start"
puts "=" * 30
# Example 1: Simple completion
puts "\n1. Simple Completion:"
puts "Question: What is Ruby?"
response = client.chat.completions(
messages: "What is Ruby in 50 words or less?",
model: "glm-5",
max_tokens: 100
)
puts "Answer: #{response.first_message}"
# Example 2: Conversation with context
puts "\n2. Conversation with Context:"
conversation = [
{ role: "system", content: "You are a helpful Ruby expert." },
{ role: "user", content: "What's the difference between a class and a module?" },
{ role: "assistant", content: "In Ruby, classes are blueprints for objects that can be instantiated, while modules are collections of methods and constants that can be mixed into classes." },
{ role: "user", content: "Can you show me an example of each?" }
]
response = client.chat.completions(
messages: conversation,
model: "glm-5"
)
puts "Answer: #{response.first_message}"
# Example 3: Streamed response
puts "\n3. Streamed Response:"
puts "Question: Count from 1 to 5"
print "Answer: "
client.chat.stream(
messages: "Count from 1 to 5, one number at a time",
model: "glm-5"
) do |chunk|
content = chunk.dig(:choices, 0, :delta, :content)
print content if content
end
puts "\n"
# Example 4: Error handling
puts "\n4. Error Handling:"
begin
# This will fail with an invalid API key
bad_client = Zai.overseas_client(api_key: "invalid-key")
bad_client.chat.completions(messages: "Hello")
rescue Zai::AuthenticationError => e
puts "Authentication failed as expected: #{e.message}"
end
# Example 5: Token usage
puts "\n5. Token Usage:"
response = client.chat.completions(
messages: "Explain Ruby's metaprogramming capabilities",
model: "glm-5"
)
puts "Prompt tokens: #{response.usage.prompt_tokens}"
puts "Completion tokens: #{response.usage.completion_tokens}"
puts "Total tokens: #{response.usage.total_tokens}"
puts "\n" + "=" * 30
puts "Quick start complete! Check the documentation for more examples."#!/usr/bin/env ruby
# frozen_string_literal: true
require 'zai'
require 'erb'
require 'fileutils'
# Content Generator Example
# Demonstrates a practical use case: generating blog post content
class BlogPostGenerator
def initialize(api_key:)
@client = Zai.overseas_client(api_key: api_key)
end
def generate_post(topic:, length: 'medium', tone: 'informative')
# Step 1: Generate outline
outline = generate_outline(topic)
puts "Generated outline:\n#{outline}\n\n"
# Step 2: Generate introduction
introduction = generate_section(topic, "introduction", tone)
# Step 3: Generate main sections
sections = outline.split("\n").reject(&:empty?).map do |section_title|
title = section_title.strip
content = generate_section(topic, title, tone)
{ title: title, content: content }
end
# Step 4: Generate conclusion
conclusion = generate_section(topic, "conclusion", tone)
# Step 5: Compile into blog post
compile_blog_post(
topic: topic,
introduction: introduction,
sections: sections,
conclusion: conclusion
)
end
private
def generate_outline(topic)
response = @client.chat.completions(
messages: [
{
role: "system",
content: "You are a content strategist. Create a structured outline for a blog post. Use a numbered list format."
},
{
role: "user",
content: "Create an outline for a blog post about '#{topic}'. Include introduction, 3-4 main sections, and conclusion."
}
],
model: "glm-5",
temperature: 0.3
)
response.first_message
end
def generate_section(topic, section_type, tone)
prompts = {
"introduction" => "Write an engaging introduction for a blog post about '#{topic}'. The tone should be #{tone}.",
"conclusion" => "Write a compelling conclusion for a blog post about '#{topic}'. Summarize key points and provide a call to action."
}
prompt = prompts[section_type] || "Write a detailed section about '#{section_type}' in a blog post about '#{topic}'. The tone should be #{tone}."
response = @client.chat.completions(
messages: [
{
role: "system",
content: "You are an expert blogger and writer. Write engaging, well-structured content."
},
{ role: "user", content: prompt }
],
model: "glm-5",
temperature: 0.7,
max_tokens: 300
)
response.first_message
end
def compile_blog_post(topic:, introduction:, sections:, conclusion:)
template = ERB.new(<<~MARKDOWN)
# <%= topic %>
## Introduction
<%= introduction %>
<% sections.each do |section| %>
## <%= section[:title] %>
<%= section[:content] %>
<% end %>
## Conclusion
<%= conclusion %>
---
*This blog post was generated using Z.ai's Ruby SDK*
MARKDOWN
template.result_with_hash(
topic: topic,
introduction: introduction,
sections: sections,
conclusion: conclusion
)
end
attr_reader :client
end
# Usage example
if __FILE__ == $0
api_key = ENV.fetch('ZAI_API_KEY') do
puts "Please set ZAI_API_KEY environment variable"
exit 1
end
generator = BlogPostGenerator.new(api_key: api_key)
# Generate a blog post
topic = ARGV[0] || "The Future of AI in Software Development"
post_content = generator.generate_post(topic: topic, length: 'medium', tone: 'informative')
# Save to file
filename = "blog_post_#{topic.downcase.gsub(/\s+/, '_')}.md"
File.write(filename, post_content)
puts "\nBlog post saved to: #{filename}"
end# Beginner Tutorial: Building Your First AI-Powered Ruby Application
Welcome to the Z.ai Ruby SDK beginner tutorial! In this guide, you'll build a simple command-line application that uses AI to generate text.
## What We'll Build
We'll create a Ruby script that:
1. Accepts user input from the command line
2. Sends the input to Z.ai's chat model
3. Displays the AI's response
4. Handles common errors gracefully
## Prerequisites
Before starting, make sure you have:
- Ruby 2.7 or newer installed
- A Z.ai API key (get one at https://platform.z.ai)
- Basic familiarity with Ruby
## Step 1: Project Setup
First, create a new directory for your project:
```bash
mkdir my_ai_app
cd my_ai_appCreate a Gemfile to manage dependencies:
# Gemfile
source 'https://rubygems.org'
gem 'zai-ruby-sdk'Install the dependencies:
bundle installCreate a file called app.rb:
#!/usr/bin/env ruby
require 'zai'
require 'dotenv/load'
# Set up the client with your API key
api_key = ENV.fetch('ZAI_API_KEY') do
puts "Please set ZAI_API_KEY in your environment or .env file"
exit 1
end
client = Zai.overseas_client(api_key: api_key)
# Get user input
puts "What would you like to ask the AI?"
user_input = gets.chomp
# Send to AI and get response
puts "\nThinking..."
response = client.chat.completions(
messages: user_input,
model: "glm-5"
)
puts "\nAI Response:"
puts response.first_messageFirst, create a .env file with your API key:
ZAI_API_KEY=your-api-key-goes-hereNow run your application:
ruby app.rbTry asking questions like:
- "What is Ruby programming?"
- "Explain object-oriented programming"
- "Tell me a joke"
Let's make our application more robust by handling potential errors:
#!/usr/bin/env ruby
require 'zai'
require 'dotenv/load'
class AIAssistant
def initialize
@client = Zai.overseas_client(
api_key: ENV.fetch('ZAI_API_KEY') do
puts "Please set ZAI_API_KEY in your environment or .env file"
exit 1
end
)
end
def ask(question)
puts "\nThinking..."
response = @client.chat.completions(
messages: question,
model: "glm-5"
)
response.first_message
rescue Zai::AuthenticationError => e
puts "Authentication failed: Please check your API key"
puts "Error details: #{e.message}"
rescue Zai::RateLimitError => e
puts "Rate limit exceeded. Please try again later."
rescue Zai::APIError => e
puts "An error occurred: #{e.message}"
end
private
attr_reader :client
end
# Main application loop
assistant = AIAssistant.new
puts "AI Assistant (type 'quit' to exit)"
puts "=" * 40
loop do
print "\nYou: "
user_input = gets.chomp
break if user_input.downcase == 'quit'
next if user_input.empty?
puts "AI: #{assistant.ask(user_input)}"
end
puts "\nGoodbye!"Our application currently has no memory of previous messages. Let's fix that:
class AIAssistant
def initialize
@client = Zai.overseas_client(
api_key: ENV.fetch('ZAI_API_KEY') do
puts "Please set ZAI_API_KEY in your environment or .env file"
exit 1
end
)
@conversation = []
end
def ask(question)
# Add user message to conversation
@conversation << { role: "user", content: question }
# Keep conversation to last 10 messages to manage token usage
@conversation = @conversation.last(10) if @conversation.size > 10
puts "\nThinking..."
response = @client.chat.completions(
messages: @conversation,
model: "glm-5"
)
answer = response.first_message
# Add AI response to conversation
@conversation << { role: "assistant", content: answer }
answer
rescue Zai::AuthenticationError => e
puts "Authentication failed: Please check your API key"
puts "Error details: #{e.message}"
rescue Zai::RateLimitError => e
puts "Rate limit exceeded. Please try again later."
rescue Zai::APIError => e
puts "An error occurred: #{e.message}"
end
private
attr_reader :client, :conversation
endLet's add a few more features to make our application more interesting:
class AIAssistant
def initialize
@client = Zai.overseas_client(
api_key: ENV.fetch('ZAI_API_KEY') do
puts "Please set ZAI_API_KEY in your environment or .env file"
exit 1
end
)
@conversation = []
@mode = :general # Can be :general, :creative, :precise
end
def ask(question)
# Check for commands
return handle_command(question) if question.start_with?('/')
# Add user message to conversation
@conversation << { role: "user", content: question }
# Manage conversation size
@conversation = @conversation.last(10) if @conversation.size > 10
puts "\nThinking..."
response = @client.chat.completions(
messages: build_prompt,
model: "glm-5",
temperature: temperature_for_mode(@mode)
)
answer = response.first_message
@conversation << { role: "assistant", content: answer }
answer
rescue Zai::APIError => e
puts "Error: #{e.message}"
end
def set_mode(new_mode)
modes = [:general, :creative, :precise]
if modes.include?(new_mode.to_sym)
@mode = new_mode.to_sym
puts "Mode set to #{@mode}"
else
puts "Available modes: #{modes.join(', ')}"
end
end
private
def handle_command(command)
case command
when '/help'
show_help
when '/clear'
@conversation.clear
puts "Conversation cleared."
when '/mode'
puts "Current mode: #{@mode}"
when /^\/mode (\w+)$/
set_mode($1)
when '/quit'
puts "Goodbye!"
exit 0
else
puts "Unknown command. Type /help for available commands."
end
end
def show_help
puts <<~HELP
Available commands:
/help - Show this help message
/clear - Clear conversation history
/mode - Show current mode
/mode <mode> - Set mode (general, creative, precise)
/quit - Exit the application
Modes:
general - Balanced responses (temperature: 0.7)
creative - More creative responses (temperature: 0.9)
precise - More focused responses (temperature: 0.3)
HELP
end
def build_prompt
system_message = case @mode
when :creative
"You are a creative AI assistant. Be imaginative and explore ideas freely."
when :precise
"You are a precise AI assistant. Provide accurate, focused answers."
else
"You are a helpful AI assistant."
end
[{ role: "system", content: system_message }] + @conversation
end
def temperature_for_mode(mode)
case mode
when :creative then 0.9
when :precise then 0.3
else 0.7
end
end
attr_reader :client, :conversation, :mode
end
# Main application
assistant = AIAssistant.new
puts "AI Assistant v1.0"
puts "Type /help for commands"
puts "=" * 40
loop do
print "\nYou: "
user_input = gets.chomp
response = assistant.ask(user_input)
puts "AI: #{response}" if response
endIn this tutorial, you've learned how to:
- Set up a Ruby project with the Z.ai SDK
- Initialize and configure the client
- Make API calls to generate text
- Handle errors gracefully
- Maintain conversation context
- Add interactive features
- Explore the Chat Completions Guide for more advanced options
- Check out the Examples directory for more use cases
- Learn about streaming responses for real-time applications
- Read the API Reference for complete documentation
The complete code for this tutorial is available in the examples directory.
## 5. Documentation Maintenance
### 5.1 Documentation Updates with Releases
#### Release Documentation Checklist
```markdown
## Release Documentation Checklist
### Pre-Release (1 week before)
- [ ] Review all API documentation for accuracy
- [ ] Update version numbers in all documentation files
- [ ] Write release notes for CHANGELOG.md
- [ ] Verify all examples still work
- [ ] Check for broken links in documentation
### Release Day
- [ ] Update CHANGELOG.md with release notes
- [ ] Update version in `lib/zai/version.rb`
- [ ] Generate and deploy API documentation
- [ ] Create GitHub release with documentation
- [ ] Update project README if needed
- [ ] Update RubyGems description
### Post-Release (within 1 week)
- [ ] Monitor documentation for reported issues
- [ ] Review analytics on documentation usage
- [ ] Plan documentation improvements for next release
- [ ] Update any outdated guides
- [ ] Thank contributors in release notes
## Documentation Changes
### Type of Change
- [ ] API documentation update
- [ ] Guide/tutorial update
- [ ] Example code update
- [ ] README update
- [ ] Other: ___________
### Description
Briefly describe what documentation changes were made and why.
### Areas Updated
- [ ] API Reference
- [ ] Getting Started Guide
- [ ] Examples
- [ ] Integration Guides
- [ ] Migration Guides
- [ ] Troubleshooting
### Testing
- [ ] Verified all code examples work
- [ ] Tested all linked resources
- [ ] Reviewed for clarity and accuracy
- [ ] Checked for consistent formatting
### Review Checklist
- [ ] Documentation follows style guide
- [ ] All links are valid
- [ ] Code examples are formatted correctly
- [ ] No typos or grammatical errors
- [ ] Version information is accurate- Complete documentation in English
- Internationalization-ready structure
- Clear, simple language to ease translation
- Chinese (Simplified)
- Japanese
- Spanish
- French
- German
- Korean
- Portuguese (Brazil)
-
Preparation
- Extract all translatable content
- Create translation glossary
- Establish style guides per language
-
Translation
- Use professional translators familiar with Ruby
- Technical review by native Ruby developers
- Community review and feedback
-
Maintenance
- Track changes in source documentation
- Incremental translation updates
- Community contribution system
main # Production documentation
├── develop # Staging documentation
├── docs/feature-name # Feature documentation
├── docs/fix-typo # Quick documentation fixes
└── release/v1.2.0 # Release documentation updates
docs: update API reference for new endpoints
docs: add Rails integration guide
docs: fix broken link in getting started
docs: translate README to Chinese
docs: refactor examples directory structure
# .github/workflows/docs-checks.yml
name: Documentation Checks
on:
push:
branches: [ main, develop ]
paths:
- 'docs/**'
- 'guides/**'
- 'examples/**'
- '**.md'
pull_request:
branches: [ main, develop ]
paths:
- 'docs/**'
- 'guides/**'
- 'examples/**'
- '**.md'
jobs:
verify-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check links
uses: lycheeverse/lychee-action@v1.8.0
with:
args: --verbose --no-progress '**/*.md'
fail: true
spell-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Spell check
uses: streetsidesoftware/cspell-action@v2
with:
files: '**/*.md'
config: '.cspell.json'
validate-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install dependencies
run: |
gem install zai-ruby-sdk
bundle install
- name: Validate Ruby examples
env:
ZAI_API_KEY: ${{ secrets.TEST_API_KEY }}
run: |
for example in examples/**/*.rb; do
echo "Validating $example"
ruby -c "$example" || exit 1
done
yard-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- name: Install dependencies
run: |
gem install yard
bundle install
- name: Generate YARD docs
run: |
yardoc --no-private --list-undoc
- name: Check for undocumented public methods
run: |
if yard stats --list-undoc | grep -q "@undoc"; then
echo "Error: Found undocumented public methods"
exit 1
fi# spec/documentation_examples_spec.rb
require 'spec_helper'
require 'tempfile'
RSpec.describe "Documentation Examples" do
let(:test_api_key) { "test-key" }
# Test code snippets from README
describe "README examples" do
it "quick start example works" do
code = File.read('README.md')[/```ruby\n(.*?)\n```m, 1]
# Create a temporary file with the example
Tempfile.open(['example', '.rb']) do |file|
file.write("require 'zai'\n")
file.write(code.gsub('Zai.overseas_client(api_key: "your-api-key")',
"Zai.overseas_client(api_key: '#{test_api_key}')"))
file.flush
# Verify syntax
expect(system("ruby -c #{file.path}")).to be_truthy
end
end
end
# Test example files
describe "Example files" do
Dir.glob('examples/**/*.rb').each do |example_file|
it "#{example_file} has valid syntax" do
expect(system("ruby -c #{example_file}")).to be_truthy
end
it "#{example_file} runs without errors" do
# This would require mocking API responses
# For now, just check that it loads without syntax errors
load example_file
end
end
end
# Test guide code snippets
describe "Guide examples" do
Dir.glob('guides/**/*.md').each do |guide_file|
describe File.basename(guide_file) do
let(:content) { File.read(guide_file) }
it "has valid Ruby code blocks" do
code_blocks = content.scan(/```ruby\n(.*?)\n```m)
code_blocks.each do |code|
Tempfile.open(['guide_example', '.rb']) do |file|
file.write(code[0])
file.flush
expect(system("ruby -c #{file.path}")).to be_truthy
end
end
end
end
end
end
endRuby 3.2+ includes the YJIT (Yet Another Just-In-Time) compiler which provides significant performance improvements:
# Enable YJIT optimizations in your application
# This is automatically done by the SDK on Ruby 3.2+
if defined?(RubyVM::YJIT)
RubyVM::YJIT.enable
end
# Performance benchmark example
require 'benchmark'
require 'zai'
client = Zai.overseas_client(api_key: ENV["ZAI_API_KEY"])
# Compare performance with and without YJIT
Benchmark.bm(20) do |x|
x.report("With YJIT:") do
100.times { client.chat.completions(messages: "Hello") }
end
x.report("Without YJIT:") do
RubyVM::YJIT.disable if defined?(RubyVM::YJIT)
100.times { client.chat.completions(messages: "Hello") }
end
endRuby 3.2+ includes memory management improvements:
# The SDK automatically configures GC for Ruby 3.2+
# You can further optimize memory usage:
# Enable compaction (Ruby 3.2+)
GC.compact
# Configure GC tuning for the SDK
GC.opts[:minor_gc_tuning] = {
:full_mark_threshold => 1.0,
:step_size => 1.1
}Ruby 3.2+ introduces data classes for immutable structs:
# The SDK uses data classes when available on Ruby 3.2+
# This provides better performance for model creation:
# Before (Ruby < 3.2)
class OldMessage
attr_reader :role, :content
def initialize(role:, content:)
@role = role
@content = content
end
end
# After (Ruby 3.2+)
data class NewMessage
attr_reader :role, :content
def initialize(role:, content:)
@role = role
@content = content
end
end
# Performance comparison
Benchmark.bm(20) do |x|
x.report("Old message class:") do
10000.times { OldMessage.new(role: "user", content: "test") }
end
x.report("Data class (Ruby 3.2+):") do
10000.times { NewMessage.new(role: "user", content: "test") }
end
endJRuby 10.0.4.0+ enables true parallel processing:
# The SDK provides JRuby-specific concurrent client
require 'zai/jruby'
client = Zai::JRuby::ConcurrentClient.new(api_key: ENV["ZAI_API_KEY"])
# Process multiple requests in parallel
requests = [
{ messages: "What is Ruby?" },
{ messages: "What is JRuby?" },
{ messages: "What is YJIT?" }
]
# Parallel processing
responses = client.parallel_completions(requests)
# This utilizes multiple CPU cores available on JRubyJRuby can leverage Java libraries for enhanced performance:
# Use Java HTTP client for better performance
if defined?(JRUBY_VERSION)
client = Zai::JRuby::JavaIntegration.create_http_client("https://api.z.ai")
# This uses Java's HTTP/2 client with native optimizations
response = client.post("/chat/completions", {
model: "glm-5",
messages: [{ role: "user", content: "Hello" }]
})
enddocs/
├── api/ # Generated YARD documentation
│ ├── index.html
│ └── frames.html
├── guides/ # User guides and tutorials
│ ├── getting-started.md
│ ├── chat-completions.md
│ ├── streaming.md
│ ├── embeddings.md
│ ├── images.md
│ ├── error-handling.md
│ ├── authentication.md
│ ├── migration.md
│ └── troubleshooting.md
├── integration/ # Integration guides
│ ├── rails.md
│ ├── sinatra.md
│ └── background-jobs.md
├── tutorials/ # Step-by-step tutorials
│ ├── beginner.md
│ ├── intermediate.md
│ └── advanced.md
├── examples/ # Code examples
│ ├── quick_start.rb
│ ├── chat_bot.rb
│ ├── content_generator.rb
│ └── rails_integration/
├── assets/ # Static assets
│ ├── css/
│ ├── js/
│ └── images/
├── translations/ # Multi-language support
│ ├── zh-CN/
│ ├── ja/
│ └── es/
├── README.md # Main documentation index
├── CHANGELOG.md # Version history
├── CONTRIBUTING.md # Contributing guidelines
└── _config.yml # Jekyll configuration
# 入门指南
本指南将帮助您快速开始使用 Z.ai Ruby SDK。
## 前提条件
- Ruby 2.7+(推荐 Ruby 3.0+)
- Z.ai API 密钥(在 https://platform.z.ai 获取)
## 安装
### 选项 1:使用 Bundler(推荐)
在 Gemfile 中添加:
```ruby
gem 'zai-ruby-sdk'然后运行:
bundle installgem install zai-ruby-sdk# 选项 A:直接初始化
client = Zai.overseas_client(api_key: "your-api-key-here")
# 选项 B:环境变量
# 在 shell 中设置:
# export ZAI_API_KEY="your-api-key-here"
client = Zai.overseas_clientrequire 'zai'
client = Zai.overseas_client(api_key: ENV["ZAI_API_KEY"])
response = client.chat.completions(
messages: "您好,Z.ai!请告诉我一些关于 Ruby 的趣事。",
model: "glm-5"
)
puts response.first_message将代码保存到 hello_zai.rb 并运行:
ruby hello_zai.rbresponse 对象包含:
response.id # 完成的唯一标识符
response.object # 对象类型(通常是 "chat.completion")
response.created # 创建的 Unix 时间戳
response.model # 使用的模型
response.choices # 完成选择的数组
response.usage # 令牌使用信息
# 获取主要响应文本
response.first_message
### 7.3 Documentation Tooling Recommendations
#### Gemfile for Documentation
```ruby
# Gemfile
source 'https://rubygems.org'
# Main dependencies
gem 'zai-ruby-sdk'
# Documentation dependencies
group :docs do
gem 'yard', '~> 0.9'
gem 'redcarpet', '~> 3.5'
gem 'jekyll', '~> 4.3'
gem 'jekyll-sitemap'
gem 'jekyll-seo-tag'
gem 'jekyll-feed'
gem 'jekyll-redirect-from'
# Documentation verification
gem 'lychee', '~> 0.14' # Link checker
gem 'cspell' # Spell checker
# Internationalization
gem 'i18n'
gem 'jekyll-multiple-languages-plugin'
end
# Development dependencies
group :development do
gem 'guard'
gem 'guard-yard'
gem 'guard-livereload'
end
# Rakefile
require 'yard'
namespace :docs do
desc "Generate YARD documentation"
task :generate do
YARD::CLI.run(['--output-dir', 'docs/api'])
end
desc "Serve documentation locally"
task :serve do
require 'webrick'
port = ENV['PORT'] || 8808
# Start YARD server in background
yard_thread = Thread.new do
YARD::CLI.run(['--server', '--port', port.to_s])
end
# Start Jekyll for guides
jekyll_thread = Thread.new do
system("bundle exec jekyll serve --port #{port + 1} --watch")
end
puts "Documentation serving on:"
puts " API: http://localhost:#{port}"
puts " Guides: http://localhost:#{port + 1}"
yard_thread.join
jekyll_thread.join
end
desc "Check documentation links"
task :check_links do
puts "Checking links..."
system("lychee '**/*.md' --verbose --no-progress")
end
desc "Check spelling"
task :spell do
puts "Checking spelling..."
system("cspell '**/*.md' --config .cspell.json")
end
desc "Validate all examples"
task :validate_examples do
require 'spec_helper'
puts "Validating Ruby syntax for all examples..."
Dir.glob('examples/**/*.rb').each do |file|
puts " Checking #{file}"
unless system("ruby -c #{file}")
puts " ERROR: Invalid syntax in #{file}"
exit 1
end
end
puts "All examples have valid syntax!"
end
desc "Run all documentation checks"
task :check => [:check_links, :spell, :validate_examples]
desc "Build complete documentation site"
task :build => :generate do
# Copy guides to docs directory
system("cp -r guides docs/")
system("cp -r examples docs/")
system("cp README.md docs/index.md")
system("cp CHANGELOG.md docs/changelog.md")
puts "Documentation built successfully!"
end
end
# Default documentation task
desc "Generate and serve documentation"
task :docs => ['docs:serve']
# Add documentation checks to CI task
if Rake::Task.task_defined?(:ci)
Rake::Task[:ci].enhance ['docs:check']
end# documentation_maintenance.yaml
monthly_tasks:
- task: "Review all API documentation for accuracy"
owner: "technical_writer"
due: "1st of each month"
- task: "Update examples with latest best practices"
owner: "developer_advocate"
due: "15th of each month"
- task: "Check for broken links across all documentation"
owner: "documentation_team"
due: "3rd of each month"
- task: "Review and update getting started guide"
owner: "technical_writer"
due: "Last Friday of each month"
quarterly_tasks:
- task: "Comprehensive documentation review"
owner: "documentation_team"
due: "End of quarter"
- task: "Analyze documentation usage metrics and plan improvements"
owner: "product_manager"
due: "Quarterly planning"
- task: "Update integration guides for framework versions"
owner: "developer_advocate"
due: "After major framework releases"
release_tasks:
- task: "Update version numbers in all documentation"
owner: "release_manager"
trigger: "pre-release"
- task: "Write release notes for CHANGELOG.md"
owner: "technical_writer"
trigger: "pre-release"
- task: "Generate and deploy API documentation"
owner: "documentation_team"
trigger: "release day"
- task: "Update README with new features"
owner: "technical_writer"
trigger: "release day"
- task: "Create GitHub release with documentation"
owner: "release_manager"
trigger: "release day"This comprehensive documentation plan provides a structured approach to creating, maintaining, and evolving the documentation for the Z.ai Ruby SDK. The plan emphasizes:
- User-Centric Approach: Documentation organized by user needs and skill levels
- Practical Examples: Real-world code examples that developers can adapt
- Ruby Community Standards: Following Ruby documentation conventions and best practices
- Maintenance Strategy: Clear processes for keeping documentation current
- Internationalization: Planning for multi-language support
- Automation: Leveraging tools to ensure documentation quality and accuracy
The documentation team can implement this plan directly, using the provided templates, examples, and workflows to create a comprehensive, professional documentation site that serves Ruby developers effectively.