z-ai-sdk-java/ARCHITECTURE.md
tomsun28 0b09e46478
init: initial the repo (#1)
* init project

* init project

* init project

* init project

* init project

* init project

* update action

* update action

* update action

* update action

* java format

* java format

* update code and format

* update code and format

* update code and format

* update code and format

* update code and format

* update code and format

* update version

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish

* fix publish
2025-07-12 14:30:39 +08:00

23 KiB

Z.ai SDK Java - Architecture Documentation

Overview

The Z.ai SDK Java provides a service-oriented architecture that offers clean separation of concerns, comprehensive configuration management, and support for both synchronous and streaming operations. The SDK is built around a client-service pattern with reactive programming support.

Architecture Components

1. Core Client Architecture

ZaiClient

The main client class that serves as the entry point for all AI services:

public class ZaiClient extends AbstractClientBaseService {
    // Service instances
    private ChatService chatService;
    private AgentService agentService;
    private EmbeddingService embeddingService;
    // ... other services
    
    // Constructor
    public ZaiClient(ZaiConfig config) {
        // Initialize HTTP client and Retrofit
    }
    
    // Service accessors
    public synchronized ChatService chat() { /* ... */ }
    public synchronized AgentService agents() { /* ... */ }
    // ... other service accessors
}

Base Request and Response Models

ClientRequest Interface: Base interface for all service requests

public interface ClientRequest<T> {
    // Marker interface for type safety
}

ClientResponse Interface: Base interface for all service responses

public interface ClientResponse<T> {
    T getData();
    void setData(T data);
    void setCode(int code);
    void setMsg(String msg);
    void setSuccess(boolean success);
    void setError(ChatError error);
}

FlowableClientResponse Interface: Extended interface for streaming responses

public interface FlowableClientResponse<T> extends ClientResponse<T> {
    void setFlowable(Flowable<T> stream);
}

2. Configuration Management

ZaiConfig

Main configuration class that contains all SDK settings:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ZaiConfig {
    private String baseUrl;
    private String apiSecretKey;
    private String apiKey;
    private String apiSecret;
    private int expireMillis = 30 * 60 * 1000; // 30 minutes
    private String alg = "HS256";
    private boolean disableTokenCache;
    
    // Connection pool settings
    private int connectionPoolMaxIdleConnections = 5;
    private long connectionPoolKeepAliveDuration = 1;
    private TimeUnit connectionPoolTimeUnit = TimeUnit.SECONDS;
    
    // Timeout settings
    private int requestTimeOut;
    private int connectTimeout;
    private int readTimeout;
    private int writeTimeout;
    private TimeUnit timeOutTimeUnit;
    
    private String source_channel;
}

Configuration Features

  1. Authentication Settings

    • API secret key in format {apiKey}.{apiSecret}
    • JWT token expiration and algorithm configuration
    • Token caching control
  2. Network Configuration

    • Base URL for API endpoints
    • Connection pool settings (max idle connections, keep-alive duration)
    • Timeout configurations (request, connect, read, write)
  3. Token Management

    • JWT token generation and caching
    • Configurable expiration times
    • Option to disable token caching for direct API key usage

3. Service Implementations

ChatService

Provides chat completion functionality with support for synchronous, asynchronous, and streaming operations:

public interface ChatService {
    /**
     * Creates a chat completion, either streaming or non-streaming based on the request configuration.
     */
    ChatCompletionResponse createChatCompletion(ChatCompletionCreateParams request);
    
    /**
     * Creates an asynchronous chat completion.
     */
    ChatCompletionResponse asyncChatCompletion(ChatCompletionCreateParams request);
    
    /**
     * Retrieves the result of an asynchronous model operation.
     */
    QueryModelResultResponse retrieveAsyncResult(AsyncResultRetrieveParams request);
}

Service Implementation Pattern

All services follow a consistent implementation pattern:

public class ChatServiceImpl implements ChatService {
    private final ZaiClient zAiClient;
    private final ChatApi chatApi;
    
    public ChatServiceImpl(ZaiClient zAiClient) {
        this.zAiClient = zAiClient;
        this.chatApi = this.zAiClient.retrofit().create(ChatApi.class);
    }
    
    @Override
    public ChatCompletionResponse createChatCompletion(ChatCompletionCreateParams request) {
        // Parameter validation
        String paramMsg = validateParams(request);
        if (StringUtils.isNotEmpty(paramMsg)) {
            return new ChatCompletionResponse(-100, String.format("invalid param: %s", paramMsg));
        }
        
        // Route to streaming or synchronous execution
        if (request.getStream()) {
            return streamChatCompletion(request);
        } else {
            return syncChatCompletion(request);
        }
    }
}

Usage Examples

Basic Configuration

// Simple configuration with API secret key
ZaiConfig config = new ZaiConfig("your.api.key.your.api.secret");
ZaiClient client = new ZaiClient(config);

// Or using separate API key and secret
ZaiConfig config = new ZaiConfig("your.api.key", "your.api.secret");
ZaiClient client = new ZaiClient(config);

Builder Pattern Configuration

// Using the Builder pattern for advanced configuration
ZaiClient client = new ZaiClient.Builder("your.api.key.your.api.secret")
    .enableTokenCache()
    .networkConfig(
        300,  // request timeout
        100,  // connect timeout
        100,  // read timeout
        100,  // write timeout
        TimeUnit.SECONDS
    )
    .connectionPool(
        10,   // max idle connections
        5,    // keep alive duration
        TimeUnit.MINUTES
    )
    .tokenExpire(3600000) // 1 hour in milliseconds
    .build();

Custom Configuration with ZaiConfig

ZaiConfig config = ZaiConfig.builder()
    .apiSecretKey("your.api.key.your.api.secret")
    .baseUrl("https://custom.api.endpoint")
    .requestTimeOut(60)
    .connectTimeout(30)
    .readTimeout(30)
    .writeTimeout(30)
    .timeOutTimeUnit(TimeUnit.SECONDS)
    .disableTokenCache(false)
    .expireMillis(7200000) // 2 hours
    .connectionPoolMaxIdleConnections(10)
    .connectionPoolKeepAliveDuration(5)
    .connectionPoolTimeUnit(TimeUnit.MINUTES)
    .build();

ZaiClient client = new ZaiClient(config);

Service Usage

// Get service instance
ChatService chatService = client.chat();

// Create chat request
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
    .model("glm-4")
    .messages(Arrays.asList(
        ChatMessage.builder()
            .role(ChatMessage.Role.USER)
            .content("Hello, world!")
            .build()
    ))
    .stream(false) // Set to true for streaming
    .temperature(0.7f)
    .maxTokens(1024)
    .build();

// Execute request
try {
    ChatCompletionResponse response = chatService.createChatCompletion(request);
    
    if (response.isSuccess()) {
        ModelData data = response.getData();
        if (data != null && data.getChoices() != null && !data.getChoices().isEmpty()) {
            String content = data.getChoices().get(0).getMessage().getContent();
            System.out.println("Response: " + content);
        }
    } else {
        System.err.println("Error: " + response.getMsg());
        if (response.getError() != null) {
            System.err.println("Error details: " + response.getError().getMessage());
        }
    }
} catch (Exception e) {
    System.err.println("Request failed: " + e.getMessage());
}

Streaming Usage

// Create streaming request
ChatCompletionCreateParams streamRequest = ChatCompletionCreateParams.builder()
    .model("glm-4")
    .messages(Arrays.asList(
        ChatMessage.builder()
            .role(ChatMessage.Role.USER)
            .content("Tell me a story")
            .build()
    ))
    .stream(true) // Enable streaming
    .temperature(0.7f)
    .maxTokens(1024)
    .build();

// Execute streaming request
ChatCompletionResponse response = chatService.createChatCompletion(streamRequest);

if (response.isSuccess() && response.getFlowable() != null) {
    response.getFlowable().subscribe(
        data -> {
            // Handle streaming chunk
            if (data.getChoices() != null && !data.getChoices().isEmpty()) {
                String content = data.getChoices().get(0).getDelta().getContent();
                if (content != null) {
                    System.out.print(content);
                }
            }
        },
        error -> System.err.println("\nStream error: " + error.getMessage()),
        () -> System.out.println("\nStream completed")
    );
} else {
    System.err.println("Failed to start streaming: " + response.getMsg());
}

Asynchronous Usage

// Create async request
ChatCompletionCreateParams asyncRequest = ChatCompletionCreateParams.builder()
    .model("glm-4")
    .messages(Arrays.asList(
        ChatMessage.builder()
            .role(ChatMessage.Role.USER)
            .content("Generate a long document")
            .build()
    ))
    .build();

// Execute async request
ChatCompletionResponse asyncResponse = chatService.asyncChatCompletion(asyncRequest);

if (asyncResponse.isSuccess()) {
    String taskId = asyncResponse.getData().getTaskId();
    System.out.println("Async task started with ID: " + taskId);
    
    // Poll for results
    AsyncResultRetrieveParams retrieveParams = new AsyncResultRetrieveParams();
    retrieveParams.setId(taskId);
    
    QueryModelResultResponse result = chatService.retrieveAsyncResult(retrieveParams);
    // Handle result...
}

Available Services

The ZaiClient provides access to multiple AI services:

ZaiClient client = new ZaiClient(config);

// Chat completion service
ChatService chatService = client.chat();

// Agent service for agent-based completions
AgentService agentService = client.agents();

// Embedding service for text embeddings
EmbeddingService embeddingService = client.embeddings();

// File management service
FileService fileService = client.files();

// Audio processing service
AudioService audioService = client.audio();

// Image generation service
ImageService imageService = client.images();

// Batch processing service
BatchService batchService = client.batches();

// Fine-tuning service
FineTuningService fineTuningService = client.fineTuning();

// Web search service
WebSearchService webSearchService = client.webSearch();

// Video processing service
VideosService videosService = client.videos();

// Knowledge base service
KnowledgeService knowledgeService = client.knowledge();

// Document management service
DocumentService documentService = client.documents();

// Assistant service
AssistantService assistantService = client.assistants();

Request and Response Models

Common Request Structure

All requests extend CommonRequest which provides common fields:

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class CommonRequest {
    @JsonProperty("request_id")
    private String requestId;
    
    @JsonProperty("user_id")
    private String userId;
    
    // Additional common fields...
}

Chat Request Example

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class ChatCompletionCreateParams extends CommonRequest implements ClientRequest<ChatCompletionCreateParams> {
    private String model;
    private List<ChatMessage> messages;
    private Boolean stream;
    private Float temperature;
    @JsonProperty("max_tokens")
    private Integer maxTokens;
    private List<String> stop;
    private List<ChatTool> tools;
    // ... other fields
}

Response Structure

All responses implement ClientResponse or FlowableClientResponse:

@Data
public class ChatCompletionResponse implements FlowableClientResponse<ModelData> {
    private int code;
    private String msg;
    private boolean success;
    private ModelData data;
    private Flowable<ModelData> flowable; // For streaming responses
    private ChatError error;
}

Error Handling

Response Error Handling

ChatCompletionResponse response = chatService.createChatCompletion(request);

// Check if the request was successful
if (!response.isSuccess()) {
    int errorCode = response.getCode();
    String errorMessage = response.getMsg();
    
    System.err.println("Request failed with code: " + errorCode);
    System.err.println("Error message: " + errorMessage);
    
    // Check for detailed error information
    if (response.getError() != null) {
        ChatError error = response.getError();
        System.err.println("Error code: " + error.getCode());
        System.err.println("Error details: " + error.getMessage());
        
        // Handle specific error types
        switch (errorCode) {
            case 400:
                System.err.println("Bad request - check your parameters");
                break;
            case 401:
                System.err.println("Authentication failed - check your API key");
                break;
            case 429:
                System.err.println("Rate limit exceeded - please retry later");
                break;
            case 500:
                System.err.println("Server error - please try again");
                break;
            default:
                System.err.println("Unexpected error occurred");
        }
    }
    return;
}

// Process successful response
ModelData data = response.getData();
if (data != null) {
    // Handle successful response data
    System.out.println("Request completed successfully");
}

Exception Handling

try {
    ChatCompletionResponse response = chatService.createChatCompletion(request);
    // Process response...
} catch (ZAiHttpException e) {
    // Handle HTTP-specific errors
    System.err.println("HTTP Error: " + e.getMessage());
    System.err.println("Status Code: " + e.statusCode);
    System.err.println("Error Code: " + e.code);
} catch (Exception e) {
    // Handle other exceptions
    System.err.println("Unexpected error: " + e.getMessage());
    e.printStackTrace();
}

Extension Points

Custom Service Implementation

public class CustomService implements AIService<CustomRequest, CustomResponse> {
    @Override
    public CustomResponse execute(CustomRequest request) throws Exception {
        // Implementation
    }
    
    @Override
    public CompletableFuture<CustomResponse> executeAsync(CustomRequest request) {
        // Implementation
    }
    
    @Override
    public Flowable<CustomResponse> executeStream(CustomRequest request) throws Exception {
        // Implementation
    }
    
    @Override
    public void validateRequest(CustomRequest request) throws IllegalArgumentException {
        // Validation logic
    }
    
    @Override
    public String getServiceType() {
        return "CUSTOM_SERVICE";
    }
}

// Register custom service
client.registerService("CUSTOM_SERVICE", new CustomService());

Custom Configuration

// Extend configuration for custom needs
ZaiConfiguration customConfig = ZaiConfigurationBuilder.newBuilder()
    .apiSecretKey("your.api.key")
    .baseUrl("https://custom.endpoint")
    // Add custom settings
    .build();

// Add custom metadata to configuration
customConfig.getAuth().addMetadata("customAuth", "value");
customConfig.getNetwork().addMetadata("customNetwork", "value");

Best Practices

Configuration Management

// Use builder pattern for configuration
ZaiConfig config = ZaiConfig.builder()
    .apiSecretKey("your.api.key.your.api.secret")
    .baseUrl("https://open.bigmodel.cn/")
    .requestTimeOut(60)
    .connectTimeout(30)
    .readTimeout(30)
    .writeTimeout(30)
    .timeOutTimeUnit(TimeUnit.SECONDS)
    .disableTokenCache(false)
    .expireMillis(3600000) // 1 hour
    .connectionPoolMaxIdleConnections(10)
    .connectionPoolKeepAliveDuration(5)
    .connectionPoolTimeUnit(TimeUnit.MINUTES)
    .build();

ZaiClient client = new ZaiClient(config);

Request Building

// Use builder pattern for creating requests
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
    .model("glm-4")
    .messages(Arrays.asList(
        ChatMessage.builder()
            .role(ChatMessage.Role.USER)
            .content("Hello, world!")
            .build()
    ))
    .temperature(0.7f)
    .maxTokens(1000)
    .stream(false)
    .build();

Error Handling

// Comprehensive error handling
ChatCompletionResponse response = chatService.createChatCompletion(request);

if (!response.isSuccess()) {
    System.err.println("Request failed: " + response.getMsg());
    if (response.getError() != null) {
        System.err.println("Error details: " + response.getError().getMessage());
    }
    return;
}

// Process successful response
ModelData data = response.getData();
if (data != null && data.getChoices() != null && !data.getChoices().isEmpty()) {
    String content = data.getChoices().get(0).getMessage().getContent();
    System.out.println("Response: " + content);
}

Streaming Best Practices

// Handle streaming responses properly
ChatCompletionCreateParams streamRequest = request.toBuilder()
    .stream(true)
    .build();

ChatCompletionResponse response = chatService.createChatCompletion(streamRequest);

if (response.isSuccess() && response.getFlowable() != null) {
    response.getFlowable()
        .observeOn(Schedulers.io())
        .subscribe(
            data -> {
                // Process each streaming chunk
                if (data.getChoices() != null && !data.getChoices().isEmpty()) {
                    String content = data.getChoices().get(0).getDelta().getContent();
                    if (content != null) {
                        System.out.print(content);
                    }
                }
            },
            error -> {
                System.err.println("Streaming error: " + error.getMessage());
            },
            () -> {
                System.out.println("\nStreaming completed");
            }
        );
}

Resource Management

// Properly manage client lifecycle
try {
    ZaiClient client = new ZaiClient(config);
    ChatService chatService = client.chat();
    
    // Use the service...
    ChatCompletionResponse response = chatService.createChatCompletion(request);
    
} catch (Exception e) {
    System.err.println("Error: " + e.getMessage());
} finally {
    // Clean up resources if needed
}

Asynchronous Processing

// Use async operations for long-running tasks
ChatCompletionResponse asyncResponse = chatService.asyncChatCompletion(request);

if (asyncResponse.isSuccess()) {
    String taskId = asyncResponse.getData().getTaskId();
    
    // Poll for results
    AsyncResultRetrieveParams retrieveParams = new AsyncResultRetrieveParams();
    retrieveParams.setId(taskId);
    
    // Implement polling logic with backoff
    CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(1000); // Wait before polling
            return chatService.retrieveAsyncResult(retrieveParams);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }).thenAccept(result -> {
        // Handle result
        if (result.isSuccess()) {
            System.out.println("Async task completed");
        }
    });
}

Security Best Practices

  1. API Key Management: Store API keys securely, never hardcode them
  2. Token Caching: Enable token caching to reduce authentication overhead
  3. Request Validation: Always validate input parameters
  4. Error Logging: Log errors but never log sensitive information
  5. Timeout Configuration: Set appropriate timeouts to prevent hanging requests
  6. Connection Pooling: Configure connection pools for optimal performance
  7. Rate Limiting: Implement client-side rate limiting to respect API limits

Migration Guide

Upgrading to Latest Version

This guide helps you migrate from older versions of the Z-AI SDK to the current architecture.

Key Changes in Current Version

  1. Unified Client Architecture: All services are now accessed through ZaiClient
  2. Improved Configuration: ZaiConfig with builder pattern for better flexibility
  3. Standardized Request/Response: All requests implement ClientRequest, responses implement ClientResponse
  4. Enhanced Streaming: Better support for streaming responses with FlowableClientResponse
  5. Comprehensive Service Coverage: Support for Chat, Agents, Embeddings, Files, Audio, Images, and more

Configuration Migration

// If you were using basic configuration
// Old approach (if applicable)
String apiKey = "your-api-key";
String apiSecret = "your-api-secret";

// New approach
ZaiConfig config = ZaiConfig.builder()
    .apiKey(apiKey)
    .apiSecret(apiSecret)
    .baseUrl("https://open.bigmodel.cn/")
    .enableTokenCache(true)
    .tokenExpiredSeconds(3600)
    .build();

ZaiClient client = new ZaiClient(config);

Service Usage Migration

// Modern service usage
ChatService chatService = client.chat();
EmbeddingService embeddingService = client.embeddings();
FileService fileService = client.files();
AudioService audioService = client.audio();
ImageService imageService = client.images();
// ... and more services

Request Building Migration

// Use builder pattern for all requests
ChatCompletionCreateParams chatRequest = ChatCompletionCreateParams.builder()
    .model("glm-4")
    .messages(Arrays.asList(
        ChatMessage.builder()
            .role(ChatMessage.Role.USER)
            .content("Hello, world!")
            .build()
    ))
    .temperature(0.7f)
    .maxTokens(1000)
    .build();

Response Handling Migration

// Standardized response handling
ChatCompletionResponse response = chatService.createChatCompletion(chatRequest);

if (response.isSuccess()) {
    ModelData data = response.getData();
    // Process successful response
} else {
    System.err.println("Error: " + response.getMsg());
    if (response.getError() != null) {
        System.err.println("Details: " + response.getError().getMessage());
    }
}

Streaming Migration

// Enhanced streaming support
ChatCompletionCreateParams streamRequest = chatRequest.toBuilder()
    .stream(true)
    .build();

ChatCompletionResponse streamResponse = chatService.createChatCompletion(streamRequest);

if (streamResponse.isSuccess() && streamResponse.getFlowable() != null) {
    streamResponse.getFlowable()
        .subscribe(
            data -> {
                // Process streaming data
            },
            error -> {
                // Handle streaming errors
            },
            () -> {
                // Streaming completed
            }
        );
}

Best Practices for Migration

  1. Update Dependencies: Ensure you're using the latest version of the SDK
  2. Review Configuration: Update your configuration to use ZaiConfig.builder()
  3. Update Service Access: Use ZaiClient to access all services
  4. Standardize Error Handling: Use the new response structure for error handling
  5. Test Thoroughly: Test all functionality after migration
  6. Update Documentation: Update your internal documentation to reflect the new patterns

This architecture provides a solid foundation for future enhancements while maintaining backward compatibility where possible.