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
This commit is contained in:
tomsun28 2025-07-12 14:30:39 +08:00 committed by GitHub
parent 94af104305
commit 0b09e46478
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
366 changed files with 24828 additions and 23 deletions

32
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: 🐞 Bug Report
description: File a bug report
title: "[Bug]: "
type: "Bug"
body:
- type: markdown
attributes:
value: |
Thanks for stopping by to let us know something could be better!
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us what you expected to happen and how to reproduce the issue.
placeholder: Tell us what you see!
value: "A bug happened!"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/THUDM/z-ai-sdk-java/blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

View file

@ -0,0 +1,40 @@
name: 💡 Feature Request
description: Suggest an idea for this repository
title: "[Feat]: "
type: "Feature"
body:
- type: markdown
attributes:
value: |
Thanks for stopping by to let us know something could be better!
- type: textarea
id: problem
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is.
placeholder: Ex. I'm always frustrated when [...]
- type: textarea
id: describe
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/THUDM/z-ai-sdk-java/blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,11 @@
# Description
Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
- [ ] Follow the [`CONTRIBUTING` Guide](https://github.com/THUDM/z-ai-sdk-java/blob/main/CONTRIBUTING.md).
- [ ] Make your Pull Request title in the <https://www.conventionalcommits.org/> specification.
- [ ] Ensure the tests pass (Run `mvn clean test` from the repository root)
- [ ] Appropriate docs were updated (if necessary)
Fixes #<issue_number_goes_here>

27
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Build and Test
on:
pull_request:
branches:
- main
push:
branches:
- 'action*'
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 8
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
cache: maven
- name: Validate Java Format
run: mvn spring-javaformat:validate
- name: Build with Maven
run: mvn -B package

27
.github/workflows/lint-pr.yaml vendored Normal file
View file

@ -0,0 +1,27 @@
name: "Lint PR"
on:
pull_request_target:
types:
- opened
- edited
- reopened
jobs:
lint-pr:
name: Validate PR title
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
subjectPattern: ^[a-zA-Z0-9!-_@#=*`.|+,\s]+$
# If `subjectPattern` is configured, you can use this property to override
# the default error message that is shown when the pattern doesn't match.
# The variables `subject` and `title` can be used within the message.
subjectPatternError: |
The subject "{subject}" found in the pr title "{title}" didn't match the
configured pattern. Can only chars, numbers and symbols !-_@#=*`|+.

32
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Publish package to the Maven Central Repository
on:
release:
types: [created]
workflow_dispatch:
jobs:
pre-build:
uses:
./.github/workflows/build.yml
publish-release:
needs:
- pre-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Maven Central Repository
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-passphrase: MAVEN_GPG_PASSPHRASE
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
- name: Publish package to Maven Central
run: mvn --batch-mode clean deploy -P release
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

59
.gitignore vendored
View file

@ -1,24 +1,45 @@
# Compiled class file
*.class
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
### mvn release:prepare temp files
/*/release.properties
/*/pom.xml.releaseBackup
/*/pom.xml.versionsBackup
/release.properties
/pom.xml.releaseBackup
/pom.xml.versionsBackup
.flattened-pom.xml
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### VS Code ###
.vscode/
logs/data.log
logs/error.log
logs/
app.pid
/data/

1
.springjavaformatconfig Normal file
View file

@ -0,0 +1 @@
java-baseline=8

828
ARCHITECTURE.md Normal file
View file

@ -0,0 +1,828 @@
# 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:
```java
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
```java
public interface ClientRequest<T> {
// Marker interface for type safety
}
```
**ClientResponse Interface**: Base interface for all service responses
```java
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
```java
public interface FlowableClientResponse<T> extends ClientResponse<T> {
void setFlowable(Flowable<T> stream);
}
```
### 2. Configuration Management
#### ZaiConfig
Main configuration class that contains all SDK settings:
```java
@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:
```java
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:
```java
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
```java
// 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
```java
// 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
```java
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
```java
// 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
```java
// 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
```java
// 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:
```java
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:
```java
@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
```java
@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`:
```java
@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
```java
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
```java
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
```java
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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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
```java
// 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.

91
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,91 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of
experience, education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when the Project
Steward has a reasonable belief that an individual's behavior may have a
negative impact on the project or its community.
## Conflict Resolution
We do not believe that all conflict is bad; healthy debate and disagreement
often yield positive results. However, it is never okay to be disrespectful or
to engage in behavior that violates the projects code of conduct.
If you see someone violating the code of conduct, you are encouraged to address
the behavior directly with those involved. Many issues can be resolved quickly
and easily, and this gives people more control over the outcome of their
dispute. If you are unable to resolve the matter for any reason, or if the
behavior is threatening or harassing, report it. We are dedicated to providing
an environment where participants feel welcome and safe.
Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the
Project Steward(s) for *[PROJECT NAME]*. It is the Project Stewards duty to
receive and address reported violations of the code of conduct. They will then
work with a committee consisting of representatives from the Open Source
Programs Office and the Z.ai Open Source Strategy team.
We will investigate every complaint, but you may not receive a direct response.
We will use our discretion in determining when and how to follow up on reported
incidents, which may range from not taking action to permanent expulsion from
the project and project-sponsored spaces. We will notify the accused of the
report and provide them an opportunity to discuss it before any action is taken.
The identity of the reporter will be omitted from the details of the report
supplied to the accused. In potentially harmful situations, such as ongoing
harassment or threats to anyone's safety, we may take action without notice.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

54
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,54 @@
# How to contribute
We'd love to accept your patches and contributions to this project.
## Contribution process
### Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
### Contributor Guide
You may follow these steps to contribute:
1. **Fork the official repository.** This will create a copy of the official repository in your own account.
2. **Sync the branches.** This will ensure that your copy of the repository is up-to-date with the latest changes from the official repository.
3. **Work on your forked repository's feature branch.** This is where you will make your changes to the code.
4. **Commit your updates on your forked repository's feature branch.** This will save your changes to your copy of the repository.
5. **Submit a pull request to the official repository's main branch.** This will request that your changes be merged into the official repository.
6. **Resolve any linting errors.** This will ensure that your changes are formatted correctly.
Here are some additional things to keep in mind during the process:
- **Test your changes.** Before you submit a pull request, make sure that your changes work as expected.
- **Be patient.** It may take some time for your pull request to be reviewed and merged.
### Environment Setup
For running unit tests, set up your environment variables with your API credentials:
```bash
export ZAI_BASE_URL=https://api.z.ai/api/paas/v4/ # Default ZAI API endpoint
export ZAI_API_KEY=your_api_key_here # Replace with your actual API key
```
> ⚠️ **Note**: Running tests will consume a small amount of tokens from your API account.
### Dependencies
This SDK uses the following core dependencies:
| Library | Version |
|---------|----------|
| OkHttp | 3.14.9 |
| Java JWT | 4.2.2 |
| Jackson | 2.11.3 |
| Retrofit2 | 2.9.0 |
Have Fun!
---

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Z.ai & THUKEG
Copyright (c) Z.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

370
README.md
View file

@ -1,2 +1,368 @@
# z-ai-sdk-java
Java SDK for z.ai
# Z.ai Open Platform Java SDK
[![Maven Central](https://img.shields.io/maven-central/v/ai.z/z-ai-sdk.svg)](https://search.maven.org/artifact/ai.z/z-ai-sdk)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Java](https://img.shields.io/badge/java-1.8%2B-orange.svg)](https://www.oracle.com/java/)
[中文文档](README_CN.md) | English
**Not yet released.**
The official Java SDK for Z.ai and ZHIPU AI platforms, providing a unified interface to access powerful AI capabilities including chat completion, embeddings, image generation, audio processing, and more.
## ✨ Features
- 🚀 **Type-safe API**: All interfaces are fully type-encapsulated, no need to consult API documentation
- 🔧 **Easy Integration**: Simple and intuitive API design for quick integration
- ⚡ **High Performance**: Built with modern Java libraries for optimal performance
- 🛡️ **Secure**: Built-in authentication and token management
- 📦 **Lightweight**: Minimal dependencies for easy project integration
## 📦 Installation
### Requirements
- Java 1.8 or higher
- Maven or Gradle
- Not supported on Android platform
### Maven
Add the following dependency to your `pom.xml`:
```xml
<dependency>
<groupId>ai.z</groupId>
<artifactId>z-ai-sdk</artifactId>
<version>0.0.1</version>
</dependency>
```
### Gradle
Add the following dependency to your `build.gradle` (for Groovy DSL):
```groovy
dependencies {
implementation 'ai.z:z-ai-sdk:0.0.1'
}
```
Or `build.gradle.kts` (for Kotlin DSL):
```kotlin
dependencies {
implementation("ai.z:z-ai-sdk:0.0.1")
}
```
### 📋 Dependencies
This SDK uses the following core dependencies:
| Library | Version |
|---------|----------|
| OkHttp | 4.12.0 |
| Java JWT | 4.4.0 |
| Jackson | 2.17.2 |
| Retrofit2 | 2.11.0 |
| RxJava | 3.1.8 |
| SLF4J | 2.0.16 |
## 🚀 Quick Start
### Basic Usage
1. **Create a ZaiClient** with your API credentials
2. **Access services** through the client
3. **Call API methods** with typed parameters
```java
import ai.z.openapi.ZaiClient;
import ai.z.openapi.service.model.*;
import ai.z.openapi.core.Constants;
// Create client with API key, recommend export the ENV api-key
// export ZAI_API_KEY=your.api.key
ZaiClient client = ZaiClient.builder().build();
// Or set the api-key by code
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.build();
// Or create client for specific platform
ZaiClient zhipuClient = ZaiClient.ofZHIPU("your.api.key.your.api.secret").build();
```
### Client Configuration
The SDK provides a flexible builder pattern for customizing your client:
```java
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.baseUrl("https://api.z.ai/api/paas/v4/")
.enableTokenCache()
.tokenExpire(3600000) // 1 hour
.connectionPool(10, 5, TimeUnit.MINUTES)
.build();
```
## 💡 Examples
### Chat Completion
```java
import ai.z.openapi.ZaiClient;
import ai.z.openapi.service.model.*;
import ai.z.openapi.core.Constants;
import java.util.Arrays;
// Create client
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.build();
// Create chat request
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("Hello, how are you?")
.build()
))
.stream(false)
.temperature(0.7f)
.maxTokens(1024)
.build();
// Execute request
ChatCompletionResponse response = client.chat().createChatCompletion(request);
if (response.isSuccess()) {
String content = response.getData().getChoices().get(0).getMessage().getContent();
System.out.println("Response: " + content);
} else {
System.err.println("Error: " + response.getMsg());
}
```
### Streaming Chat
```java
// Create streaming request
ChatCompletionCreateParams streamRequest = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("Tell me a story")
.build()
))
.stream(true) // Enable streaming
.build();
// Execute streaming request
ChatCompletionResponse response = client.chat().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")
);
}
```
### Function Calling
```java
// Define function
ChatTool weatherTool = ChatTool.builder()
.type(ChatToolType.FUNCTION.value())
.function(ChatFunction.builder()
.name("get_weather")
.description("Get current weather for a location")
.parameters(ChatFunctionParameters.builder()
.type("object")
.properties(Map.of(
"location", Map.of(
"type", "string",
"description", "City name"
)
))
.required(Arrays.asList("location"))
.build())
.build())
.build();
// Create request with function
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("What's the weather like in Beijing?")
.build()
))
.tools(Arrays.asList(weatherTool))
.toolChoice("auto")
.build();
ChatCompletionResponse response = client.chat().createChatCompletion(request);
```
### Embeddings
```java
import ai.z.openapi.service.embedding.*;
// Create embedding request
EmbeddingCreateParams request = EmbeddingCreateParams.builder()
.model(Constants.ModelEmbedding3)
.input(Arrays.asList("Hello world", "How are you?"))
.build();
// Execute request
EmbeddingResponse response = client.embeddings().create(request);
if (response.isSuccess()) {
response.getData().getData().forEach(embedding -> {
System.out.println("Embedding: " + embedding.getEmbedding());
});
}
```
### Image Generation
```java
import ai.z.openapi.service.image.*;
// Create image generation request
CreateImageRequest request = CreateImageRequest.builder()
.model(Constants.ModelCogView3Plus)
.prompt("A beautiful sunset over mountains")
.size("1024x1024")
.quality("standard")
.n(1)
.build();
// Execute request
ImageResponse response = client.images().generate(request);
if (response.isSuccess()) {
response.getData().getData().forEach(image -> {
System.out.println("Image URL: " + image.getUrl());
});
}
```
### Spring Boot Integration
```java
@RestController
public class AIController {
private final ZaiClient zaiClient;
public AIController() {
this.zaiClient = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.enableTokenCache()
.build();
}
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody ChatRequest request) {
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content(request.getMessage())
.build()
))
.build();
ChatCompletionResponse response = zaiClient.chat().createChatCompletion(params);
if (response.isSuccess()) {
String content = response.getData().getChoices().get(0).getMessage().getContent();
return ResponseEntity.ok(content);
} else {
return ResponseEntity.badRequest().body(response.getMsg());
}
}
}
```
## 🔧 Available Services
The ZaiClient provides access to comprehensive AI services:
| Service | Description | Key Features |
|---------|-------------|-------------|
| **Chat** | Text generation and conversation | Streaming, function calling, async support |
| **Embeddings** | Text embeddings generation | Multiple embedding models |
| **Images** | Image generation and processing | CogView models, various sizes |
| **Audio** | Speech synthesis and recognition | Text-to-speech, speech-to-text |
| **Files** | File management and processing | Upload, download, batch processing |
| **Assistants** | AI assistant management | Create, configure, and manage assistants |
| **Agents** | Agent-based completions | Specialized agent interactions |
| **Knowledge** | Knowledge base operations | Document indexing and retrieval |
| **Fine-tuning** | Model customization | Train custom models |
| **Batch** | Batch processing | Bulk operations |
| **Web Search** | Web search integration | Real-time web information |
| **Videos** | Video processing | Video analysis and generation |
## 🎯 Supported Models
### Text Generation
- `glm-4-plus` - Enhanced GLM-4 with improved capabilities
- `glm-4` - Standard GLM-4 model
- `glm-4-air` - Lightweight version for speed
- `glm-4-flash` - Ultra-fast response model
- `glm-4-long` - Optimized for long-context conversations
- `glm-4-voice` - Specialized for voice interactions
### Vision Models
- `glm-4v-plus` - Enhanced vision model
- `glm-4v` - Standard vision model
### Image Generation
- `cogview-3-plus` - Enhanced image generation
- `cogview-3` - Standard image generation
### Embeddings
- `embedding-3` - Latest embedding model
- `embedding-2` - Previous generation embedding
### Specialized
- `charglm-3` - Character interaction model
- `cogtts` - Text-to-speech model
## 📈 Release Notes
For detailed release notes and version history, please see [Release-Note.md](Release-Note.md).
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🤝 Contributing
We welcome contributions! Please feel free to submit a Pull Request.
## 📞 Support
For questions and support:
- Visit [Z.ai Platform](https://z.ai/)
- Visit [ZHIPU AI Open Platform](http://open.bigmodel.cn/)
- Check our [Architecture Documentation](ARCHITECTURE.md)

396
README_CN.md Normal file
View file

@ -0,0 +1,396 @@
# Z.ai Open Platform Java SDK
[![Maven Central](https://img.shields.io/maven-central/v/ai.z/z-ai-sdk.svg)](https://search.maven.org/artifact/ai.z/z-ai-sdk)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Java](https://img.shields.io/badge/java-1.8%2B-orange.svg)](https://www.oracle.com/java/)
[English Readme](README.md)
Z.ai AI 平台官方 Java SDK提供统一接口访问强大的AI能力包括对话补全、向量嵌入、图像生成、音频处理等功能。
## ✨ 特性
- 🚀 **类型安全**: 所有接口完全类型封装无需查阅API文档即可完成接入
- 🔧 **简单易用**: 简洁直观的API设计快速上手
- ⚡ **高性能**: 基于现代Java库构建性能优异
- 🛡️ **安全可靠**: 内置身份验证和令牌管理
- 📦 **轻量级**: 最小化依赖,易于项目集成
## 📦 安装
### 环境要求
- Java 1.8 或更高版本
- Maven 或 Gradle
- 尚不支持在 Android 平台运行
### Maven 依赖
在您的 `pom.xml` 中添加以下依赖:
```xml
<dependency>
<groupId>ai.z</groupId>
<artifactId>z-ai-sdk</artifactId>
<version>0.0.1</version>
</dependency>
```
### Gradle 依赖
在您的 `build.gradle` 中添加以下依赖(适用于 Groovy DSL
```groovy
dependencies {
implementation 'ai.z:z-ai-sdk:0.0.1'
}
```
`build.gradle.kts`(适用于 Kotlin DSL
```kotlin
dependencies {
implementation("ai.z:z-ai-sdk:0.0.1")
}
```
### 📋 核心依赖
本SDK使用以下核心依赖库
| 依赖库 | 版本 |
|--------|------|
| OkHttp | 4.12.0 |
| Java JWT | 4.4.0 |
| Jackson | 2.17.2 |
| Retrofit2 | 2.11.0 |
| RxJava | 3.1.8 |
| SLF4J | 2.0.16 |
## 🚀 快速开始
### 基本用法
1. **使用API凭证创建ZaiClient**
2. **通过客户端访问服务**
3. **使用类型化参数调用API方法**
```java
import ai.z.openapi.ZaiClient;
import ai.z.openapi.service.model.*;
import ai.z.openapi.core.Constants;
// 创建客户端 推荐使用环境变量设置API凭证
// export ZAI_API_KEY=your.api.key
ZaiClient client = ZaiClient.builder().build();
// 或代码设置凭证
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.build();
// 或为特定平台创建客户端
ZaiClient zhipuClient = ZaiClient.ofZHIPU("your.api.key.your.api.secret").build();
```
### 客户端配置
SDK提供了灵活的构建器模式来自定义您的客户端
```java
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.baseUrl("https://api.z.ai/api/paas/v4/")
.enableTokenCache()
.tokenExpire(3600000) // 1小时
.connectionPool(10, 5, TimeUnit.MINUTES)
.build();
```
## 💡 使用示例
### 对话补全
```java
import ai.z.openapi.ZaiClient;
import ai.z.openapi.service.model.*;
import ai.z.openapi.core.Constants;
import java.util.Arrays;
// 创建客户端
ZaiClient client = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.build();
// 创建对话请求
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("你好,你怎么样?")
.build()
))
.stream(false)
.temperature(0.7f)
.maxTokens(1024)
.build();
// 执行请求
ChatCompletionResponse response = client.chat().createChatCompletion(request);
if (response.isSuccess()) {
String content = response.getData().getChoices().get(0).getMessage().getContent();
System.out.println("回复: " + content);
} else {
System.err.println("错误: " + response.getMsg());
}
```
### 流式对话
```java
// 创建流式请求
ChatCompletionCreateParams streamRequest = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("给我讲个故事")
.build()
))
.stream(true) // 启用流式
.build();
// 执行流式请求
ChatCompletionResponse response = client.chat().createChatCompletion(streamRequest);
if (response.isSuccess() && response.getFlowable() != null) {
response.getFlowable().subscribe(
data -> {
// 处理流式数据块
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("\n流式错误: " + error.getMessage()),
() -> System.out.println("\n流式完成")
);
}
```
### 函数调用
```java
// 定义函数
ChatTool weatherTool = ChatTool.builder()
.type(ChatToolType.FUNCTION.value())
.function(ChatFunction.builder()
.name("get_weather")
.description("获取指定地点的当前天气")
.parameters(ChatFunctionParameters.builder()
.type("object")
.properties(Map.of(
"location", Map.of(
"type", "string",
"description", "城市名称"
)
))
.required(Arrays.asList("location"))
.build())
.build())
.build();
// 创建带函数的请求
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content("北京的天气怎么样?")
.build()
))
.tools(Arrays.asList(weatherTool))
.toolChoice("auto")
.build();
ChatCompletionResponse response = client.chat().createChatCompletion(request);
```
### 向量嵌入
```java
import ai.z.openapi.service.embedding.*;
// 创建嵌入请求
EmbeddingCreateParams request = EmbeddingCreateParams.builder()
.model(Constants.ModelEmbedding3)
.input(Arrays.asList("你好世界", "你好吗?"))
.build();
// 执行请求
EmbeddingResponse response = client.embeddings().create(request);
if (response.isSuccess()) {
response.getData().getData().forEach(embedding -> {
System.out.println("嵌入向量: " + embedding.getEmbedding());
});
}
```
### 图像生成
```java
import ai.z.openapi.service.image.*;
// 创建图像生成请求
CreateImageRequest request = CreateImageRequest.builder()
.model(Constants.ModelCogView3Plus)
.prompt("山间美丽的日落")
.size("1024x1024")
.quality("standard")
.n(1)
.build();
// 执行请求
ImageResponse response = client.images().generate(request);
if (response.isSuccess()) {
response.getData().getData().forEach(image -> {
System.out.println("图像URL: " + image.getUrl());
});
}
```
### Spring Boot 集成
```java
@RestController
public class AIController {
private final ZaiClient zaiClient;
public AIController() {
this.zaiClient = ZaiClient.builder()
.apiKey("your.api.key.your.api.secret")
.enableTokenCache()
.build();
}
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody ChatRequest request) {
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.model(Constants.ModelChatGLM4)
.messages(Arrays.asList(
ChatMessage.builder()
.role(ChatMessageRole.USER.value())
.content(request.getMessage())
.build()
))
.build();
ChatCompletionResponse response = zaiClient.chat().createChatCompletion(params);
if (response.isSuccess()) {
String content = response.getData().getChoices().get(0).getMessage().getContent();
return ResponseEntity.ok(content);
} else {
return ResponseEntity.badRequest().body(response.getMsg());
}
}
}
```
## 🔧 可用服务
### Chat 服务
- 对话补全(同步/异步)
- 流式对话
- 函数调用
- 多模态对话(文本+图像)
### Embeddings 服务
- 文本向量化
- 批量嵌入
- 多种嵌入模型
### Images 服务
- 文本到图像生成
- 图像编辑
- 图像变体
### Audio 服务
- 语音转文本
- 文本转语音
- 语音翻译
### Files 服务
- 文件上传
- 文件管理
- 文件检索
### Assistants 服务
- AI助手创建
- 助手管理
- 对话线程
### Agents 服务
- 智能代理
- 工作流管理
- 任务执行
### Knowledge 服务
- 知识库管理
- 文档处理
- 知识检索
### Batch 服务
- 批量处理
- 异步任务
- 结果管理
## 🤖 支持的模型
### 文本生成模型
- `glm-4-plus` - 最新的GLM-4 Plus模型
- `glm-4-0520` - GLM-4标准版
- `glm-4-long` - 长文本处理版本
- `glm-4-airx` - 轻量级版本
- `glm-4-air` - 快速响应版本
- `glm-4-flashx` - 超快响应版本
- `glm-4-flash` - 闪电版本
### 视觉模型
- `glm-4v-plus` - 多模态理解模型
- `glm-4v` - 视觉理解模型
### 图像生成模型
- `cogview-3-plus` - 高质量图像生成
- `cogview-3` - 标准图像生成
### 嵌入模型
- `embedding-3` - 最新嵌入模型
- `embedding-2` - 标准嵌入模型
### 专业模型
- `charglm-3` - 角色扮演模型
- `emohaa` - 情感分析模型
## 📈 版本更新
详细的版本更新记录和历史信息,请查看 [Release-Note.md](Release-Note.md)。
## 📄 许可证
本项目基于 MIT 许可证开源 - 详情请查看 [LICENSE](LICENSE) 文件。
## 🤝 贡献
欢迎贡献代码!请随时提交 Pull Request。
## 📞 支持
如有问题和技术支持:
- Visit [Z.ai Platform](https://z.ai/)
- Visit [ZHIPU AI Open Platform](http://open.bigmodel.cn/)
- Check our [Architecture Documentation](ARCHITECTURE.md)

4
Release-Note.md Normal file
View file

@ -0,0 +1,4 @@
# Release Notes
### 0.0.1
- ✅ Initial the Z.ai sdk refer from ZHIPU sdk

237
core/pom.xml Normal file
View file

@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ai.z.openapi</groupId>
<artifactId>z-ai-sdk-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>z-ai-sdk</artifactId>
<packaging>jar</packaging>
<name>Java sdk for Z.ai open api</name>
<description>Java SDK for Z.ai Open Platform API</description>
<url>https://z.ai</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://www.opensource.org/licenses/mit-license.php</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>Z.ai</id>
<name>Z.ai</name>
<email>user_feedback@z.ai</email>
<url>https://z.ai/model-api</url>
<organization>Z.ai</organization>
<organizationUrl>https://z.ai</organizationUrl>
<roles>
<role>architect</role>
<role>developer</role>
</roles>
<timezone>Asia/Shanghai</timezone>
</developer>
</developers>
<scm>
<connection>scm:git:https://github.com/THUDM/z-ai-sdk-java.git</connection>
<developerConnection>scm:git:https://github.com/THUDM/z-ai-sdk-java.git</developerConnection>
<tag>HEAD</tag>
<url>https://github.com/THUDM/z-ai-sdk-java</url>
</scm>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.0.11</slf4j.version>
<okhttp.version>3.14.9</okhttp.version>
<jackson.version>2.11.3</jackson.version>
<retrofit2.version>2.9.0</retrofit2.version>
<rxjava.version>3.1.8</rxjava.version>
<jwt.version>4.2.2</jwt.version>
<lombok.version>1.18.32</lombok.version>
<tika.version>2.9.0</tika.version>
<junit.test.version>5.10.2</junit.test.version>
<testcontainer.version>1.19.8</testcontainer.version>
<mockserver.version>5.15.0</mockserver.version>
</properties>
<dependencies>
<!-- slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<!-- http -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-sse</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>${rxjava.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt.version}</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--file tika-->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
<!-- Test JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.test.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.test.version}</version>
<scope>test</scope>
</dependency>
<!-- Test Containers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainer.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>${testcontainer.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${testcontainer.version}</version>
<scope>test</scope>
</dependency>
<!-- Test MockServer -->
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>${mockserver.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mockserver.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,646 @@
package ai.z.openapi;
import ai.z.openapi.service.AbstractClientBaseService;
import ai.z.openapi.service.model.ChatError;
import ai.z.openapi.service.model.ZAiHttpException;
import ai.z.openapi.service.chat.ChatService;
import ai.z.openapi.service.chat.ChatServiceImpl;
import ai.z.openapi.service.agents.AgentService;
import ai.z.openapi.service.agents.AgentServiceImpl;
import ai.z.openapi.service.embedding.EmbeddingService;
import ai.z.openapi.service.embedding.EmbeddingServiceImpl;
import ai.z.openapi.service.file.FileService;
import ai.z.openapi.service.file.FileServiceImpl;
import ai.z.openapi.service.audio.AudioService;
import ai.z.openapi.service.audio.AudioServiceImpl;
import ai.z.openapi.service.image.ImageService;
import ai.z.openapi.service.image.ImageServiceImpl;
import ai.z.openapi.service.batches.BatchService;
import ai.z.openapi.service.batches.BatchServiceImpl;
import ai.z.openapi.service.fine_turning.FineTuningService;
import ai.z.openapi.service.fine_turning.FineTuningServiceImpl;
import ai.z.openapi.service.web_search.WebSearchService;
import ai.z.openapi.service.web_search.WebSearchServiceImpl;
import ai.z.openapi.service.videos.VideosService;
import ai.z.openapi.service.videos.VideosServiceImpl;
import ai.z.openapi.service.knowledge.KnowledgeService;
import ai.z.openapi.service.knowledge.KnowledgeServiceImpl;
import ai.z.openapi.service.document.DocumentService;
import ai.z.openapi.service.document.DocumentServiceImpl;
import ai.z.openapi.service.assistant.AssistantService;
import ai.z.openapi.service.assistant.AssistantServiceImpl;
import ai.z.openapi.core.config.ZaiConfig;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.core.model.FlowableClientResponse;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.OkHttps;
import ai.z.openapi.utils.RequestSupplier;
import ai.z.openapi.utils.StringUtils;
import io.reactivex.Flowable;
import io.reactivex.Single;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.TimeUnit;
import static ai.z.openapi.core.Constants.ZHIPU_AI_BASE_URL;
import static ai.z.openapi.core.Constants.Z_AI_BASE_URL;
/**
* ZaiClient is the main entry point for interacting with the Z.ai API. This client
* provides access to various AI services including chat, embeddings, file operations,
* audio processing, image generation, and more.
*
* <p>
* The client supports both Z.ai and ZHIPU AI endpoints and provides thread-safe lazy
* initialization of services.
* </p>
*
* <p>
* Example usage:
* </p>
* <pre>{@code
* ZaiClient client = new ZaiClient.Builder("your-api-key")
* .networkConfig(30, 10, 30, 30, TimeUnit.SECONDS)
* .build();
*
* ChatService chatService = client.chat();
* // Use the chat service...
*
* client.close(); // Don't forget to close when done
* }</pre>
*
*/
public class ZaiClient extends AbstractClientBaseService {
/** Logger instance for this class */
private static final Logger logger = LoggerFactory.getLogger(ZaiClient.class);
/** HTTP client for making network requests */
private final OkHttpClient httpClient;
/** Retrofit instance for API communication */
private final Retrofit retrofit;
// Service instances - lazily initialized for thread safety and performance
/** Chat service for conversational AI operations */
private ChatService chatService;
/** Agent service for AI agent management */
private AgentService agentService;
/** Embedding service for text embeddings */
private EmbeddingService embeddingService;
/** File service for file operations */
private FileService fileService;
/** Audio service for audio processing */
private AudioService audioService;
/** Image service for image generation and processing */
private ImageService imageService;
/** Batch service for batch processing operations */
private BatchService batchService;
/** Fine-tuning service for model customization */
private FineTuningService fineTuningService;
/** Web search service for internet search capabilities */
private WebSearchService webSearchService;
/** Videos service for video processing */
private VideosService videosService;
/** Knowledge service for knowledge base operations */
private KnowledgeService knowledgeService;
/** Document service for document processing */
private DocumentService documentService;
/** Assistant service for AI assistant functionality */
private AssistantService assistantService;
/**
* Constructs a new ZaiClient with the specified configuration. By default, this
* client uses the Z.ai OpenAPI endpoint.
* @param config the configuration object containing API keys, timeouts, and other
* settings
* @throws IllegalArgumentException if config is null or invalid
*/
public ZaiClient(ZaiConfig config) {
this.httpClient = OkHttps.create(config);
this.retrofit = new Retrofit.Builder()
.baseUrl(StringUtils.isEmpty(config.getBaseUrl()) ? Z_AI_BASE_URL : config.getBaseUrl())
.client(httpClient)
.addConverterFactory(JacksonConverterFactory.create(mapper))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
// ==================== Service Accessor Methods ====================
// All service methods use lazy initialization with synchronization for thread safety
/**
* Returns the chat service for conversational AI operations. This service handles
* chat completions, streaming conversations, and related functionality.
* @return the ChatService instance (lazily initialized)
*/
public synchronized ChatService chat() {
if (chatService == null) {
this.chatService = new ChatServiceImpl(this);
}
return chatService;
}
/**
* Returns the agent service for AI agent management. This service handles agent
* creation, configuration, and execution.
* @return the AgentService instance (lazily initialized)
*/
public synchronized AgentService agents() {
if (agentService == null) {
this.agentService = new AgentServiceImpl(this);
}
return agentService;
}
/**
* Returns the embedding service for text embeddings. This service converts text into
* numerical vector representations.
* @return the EmbeddingService instance (lazily initialized)
*/
public synchronized EmbeddingService embeddings() {
if (embeddingService == null) {
this.embeddingService = new EmbeddingServiceImpl(this);
}
return embeddingService;
}
/**
* Returns the file service for file operations. This service handles file uploads,
* downloads, and management.
* @return the FileService instance (lazily initialized)
*/
public synchronized FileService files() {
if (fileService == null) {
this.fileService = new FileServiceImpl(this);
}
return fileService;
}
/**
* Returns the audio service for audio processing. This service handles
* speech-to-text, text-to-speech, and audio analysis.
* @return the AudioService instance (lazily initialized)
*/
public synchronized AudioService audio() {
if (audioService == null) {
this.audioService = new AudioServiceImpl(this);
}
return audioService;
}
/**
* Returns the image service for image generation and processing. This service handles
* image creation, editing, and analysis.
* @return the ImageService instance (lazily initialized)
*/
public synchronized ImageService images() {
if (imageService == null) {
this.imageService = new ImageServiceImpl(this);
}
return imageService;
}
/**
* Returns the batch service for batch processing operations. This service handles
* large-scale batch processing of requests.
* @return the BatchService instance (lazily initialized)
*/
public synchronized BatchService batches() {
if (batchService == null) {
this.batchService = new BatchServiceImpl(this);
}
return batchService;
}
/**
* Returns the fine-tuning service for model customization. This service handles
* training custom models on user data.
* @return the FineTuningService instance (lazily initialized)
*/
public synchronized FineTuningService fineTuning() {
if (fineTuningService == null) {
this.fineTuningService = new FineTuningServiceImpl(this);
}
return fineTuningService;
}
/**
* Returns the web search service for internet search capabilities. This service
* provides AI-powered web search functionality.
* @return the WebSearchService instance (lazily initialized)
*/
public synchronized WebSearchService webSearch() {
if (webSearchService == null) {
this.webSearchService = new WebSearchServiceImpl(this);
}
return webSearchService;
}
/**
* Returns the videos service for video processing. This service handles video
* analysis, generation, and manipulation.
* @return the VideosService instance (lazily initialized)
*/
public synchronized VideosService videos() {
if (videosService == null) {
this.videosService = new VideosServiceImpl(this);
}
return videosService;
}
/**
* Returns the knowledge service for knowledge base operations. This service manages
* knowledge bases and retrieval operations.
* @return the KnowledgeService instance (lazily initialized)
*/
public synchronized KnowledgeService knowledge() {
if (knowledgeService == null) {
this.knowledgeService = new KnowledgeServiceImpl(this);
}
return knowledgeService;
}
/**
* Returns the document service for document processing. This service handles document
* parsing, analysis, and manipulation.
* @return the DocumentService instance (lazily initialized)
*/
public synchronized DocumentService documents() {
if (documentService == null) {
this.documentService = new DocumentServiceImpl(this);
}
return documentService;
}
/**
* Returns the assistant service for AI assistant functionality. This service provides
* advanced AI assistant capabilities.
* @return the AssistantService instance (lazily initialized)
*/
public synchronized AssistantService assistants() {
if (assistantService == null) {
this.assistantService = new AssistantServiceImpl(this);
}
return assistantService;
}
// ==================== Utility Methods ====================
/**
* Returns the underlying Retrofit instance used for API communication. This method is
* primarily intended for advanced users who need direct access to the Retrofit client
* for custom API calls.
* @return the Retrofit instance
*/
public Retrofit retrofit() {
return retrofit;
}
/**
* Closes the ZAi client and releases all associated resources. This method shuts down
* the HTTP client's connection pool and executor service. After calling this method,
* the client should not be used for further requests.
*
* <p>
* <strong>Important:</strong> Always call this method when you're done with the
* client to prevent resource leaks.
* </p>
*/
public void close() {
if (httpClient != null) {
httpClient.dispatcher().executorService().shutdown();
}
}
// ==================== Core Request Execution Methods ====================
/**
* Executes a synchronous API request and returns the response. This method handles
* the complete request lifecycle including error handling and response wrapping.
* @param <Data> the type of data expected in the response
* @param <Param> the type of parameters for the request
* @param <TReq> the type of client request
* @param <TResp> the type of client response
* @param request the client request containing parameters
* @param requestSupplier the supplier that creates the actual API call
* @param tRespClass the class of the response type
* @return the wrapped response containing either success data or error information
*/
@Override
public <Data, Param, TReq extends ClientRequest<Param>, TResp extends ClientResponse<Data>> TResp executeRequest(
TReq request, RequestSupplier<Param, Data> requestSupplier, Class<TResp> tRespClass) {
Single<Data> apiCall = requestSupplier.get((Param) request);
TResp tResp = convertToClientResponse(tRespClass);
try {
// Execute the API call synchronously
Data response = execute(apiCall);
tResp.setCode(200);
tResp.setMsg("Call successful");
tResp.setData(response);
tResp.setSuccess(true);
}
catch (ZAiHttpException e) {
logger.error("API request failed with business error", e);
tResp.setCode(e.statusCode);
tResp.setMsg("Business error");
tResp.setSuccess(false);
ChatError chatError = new ChatError();
chatError.setCode(Integer.parseInt(e.code));
chatError.setMessage(e.getMessage());
tResp.setError(chatError);
}
return tResp;
}
/**
* Executes a streaming API request and returns a response containing a Flowable
* stream. This method is used for requests that return data as a continuous stream,
* such as chat completions with streaming enabled.
* @param <Data> the type of data expected in each stream element
* @param <Param> the type of parameters for the request
* @param <TReq> the type of client request
* @param <TResp> the type of flowable client response
* @param request the client request containing parameters
* @param requestSupplier the supplier that creates the actual streaming API call
* @param tRespClass the class of the response type
* @param tDataClass the class of the data type for stream elements
* @return the wrapped response containing either a success stream or error
* information
*/
@Override
@SuppressWarnings("unchecked")
public <Data, Param, TReq extends ClientRequest<Param>, TResp extends FlowableClientResponse<Data>> TResp streamRequest(
TReq request, FlowableRequestSupplier<Param, retrofit2.Call<ResponseBody>> requestSupplier,
Class<TResp> tRespClass, Class<Data> tDataClass) {
retrofit2.Call<ResponseBody> apiCall = requestSupplier.get((Param) request);
TResp tResp = convertToClientResponse(tRespClass);
try {
// Create a streaming response using the provided API call
Flowable<Data> stream = stream(apiCall, tDataClass);
tResp.setCode(200);
tResp.setMsg("Stream initialized successfully");
tResp.setSuccess(true);
tResp.setFlowable(stream);
}
catch (ZAiHttpException e) {
logger.error("Streaming API request failed with business error", e);
tResp.setCode(e.statusCode);
tResp.setMsg("Business error");
tResp.setSuccess(false);
ChatError chatError = new ChatError();
chatError.setCode(Integer.parseInt(e.code));
chatError.setMessage(e.getMessage());
tResp.setError(chatError);
}
return tResp;
}
/**
* Creates a new instance of the specified response class using reflection. This
* helper method is used to instantiate response objects dynamically.
* @param <Data> the type of data contained in the response
* @param <TResp> the type of client response
* @param tRespClass the class of the response type to instantiate
* @return a new instance of the response class
* @throws RuntimeException if the response object cannot be created
*/
private <Data, TResp extends ClientResponse<Data>> TResp convertToClientResponse(Class<TResp> tRespClass) {
try {
return tRespClass.getDeclaredConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new RuntimeException("Failed to create response object of type: " + tRespClass.getSimpleName(), e);
}
}
// ==================== Builder Pattern Implementation ====================
/**
* Builder class for creating ZaiClient instances with custom configurations. This
* builder provides a fluent API for setting up the client with various options
* including authentication, network settings, and connection pooling.
*
* <p>
* Example usage:
* </p>
* <pre>{@code
* ZaiClient client = new ZaiClient.Builder("your-api-key")
* .networkConfig(30, 10, 30, 30, TimeUnit.SECONDS)
* .connectionPool(10, 5, TimeUnit.MINUTES)
* .enableTokenCache()
* .build();
* }</pre>
*/
public static final class Builder {
/** Configuration object that accumulates all builder settings */
private final ZaiConfig config = new ZaiConfig();
public Builder() {
}
/**
* Creates a new builder with the specified API key.
* @param apiKey the API key for authentication
* @throws IllegalArgumentException if apiKey is null or empty
*/
public Builder(String apiKey) {
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalArgumentException("API key cannot be null or empty");
}
config.setApiKey(apiKey);
}
/**
* Creates a new builder with a custom base URL and API secret key.
* @param baseUrl the custom base URL for the API endpoint
* @param apiKey the API secret key for authentication
* @throws IllegalArgumentException if any parameter is null or empty
*/
public Builder(String baseUrl, String apiKey) {
if (baseUrl == null || baseUrl.trim().isEmpty()) {
throw new IllegalArgumentException("Base URL cannot be null or empty");
}
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalArgumentException("API secret key cannot be null or empty");
}
config.setBaseUrl(baseUrl);
config.setApiKey(apiKey);
}
/**
* Config the api service base url
* @param baseUrl base url
* @return this Builder instance for method chaining
*/
public Builder baseUrl(String baseUrl) {
if (baseUrl == null || baseUrl.trim().isEmpty()) {
throw new IllegalArgumentException("Base URL cannot be null or empty");
}
config.setBaseUrl(baseUrl);
return this;
}
/**
* Config the apikey
* @param apiKey api key
* @return this Builder instance for method chaining
*/
public Builder apiKey(String apiKey) {
if (apiKey == null || apiKey.trim().isEmpty()) {
throw new IllegalArgumentException("API secret key cannot be null or empty");
}
config.setApiKey(apiKey);
return this;
}
/**
* Use the ZHIPU AI base url
* @return this Builder instance for method chaining
*/
public Builder ofZHIPU() {
config.setBaseUrl(ZHIPU_AI_BASE_URL);
return this;
}
/**
* Use the Z AI base url
* @return this Builder instance for method chaining
*/
public Builder ofZAI() {
config.setBaseUrl(Z_AI_BASE_URL);
return this;
}
/**
* Disables token caching, forcing the client to use API keys for direct requests.
* This is the default behavior.
* @return this Builder instance for method chaining
*/
public Builder disableTokenCache() {
config.setDisableTokenCache(true);
return this;
}
/**
* Enables token caching, allowing the client to use access tokens for requests.
* This can improve performance by reducing authentication overhead.
* @return this Builder instance for method chaining
*/
public Builder enableTokenCache() {
config.setDisableTokenCache(false);
return this;
}
/**
* Configures the HTTP connection pool settings.
* @param maxIdleConnections maximum number of idle connections to keep in the
* pool
* @param keepAliveDuration how long to keep idle connections alive
* @param timeUnit the time unit for the keep alive duration
* @return this Builder instance for method chaining
*/
public Builder connectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
config.setConnectionPoolMaxIdleConnections(maxIdleConnections);
config.setConnectionPoolKeepAliveDuration(keepAliveDuration);
config.setConnectionPoolTimeUnit(timeUnit);
return this;
}
/**
* Sets the token expiration time in milliseconds.
* @param expireMillis the token expiration time in milliseconds
* @return this Builder instance for method chaining
*/
public Builder tokenExpire(int expireMillis) {
config.setExpireMillis(expireMillis);
return this;
}
/**
* Configures network request timeout settings.
* @param requestTimeOut the overall request timeout (see
* {@link OkHttpClient.Builder#callTimeout(long, TimeUnit)})
* @param connectTimeout the connection timeout (see
* {@link OkHttpClient.Builder#connectTimeout(long, TimeUnit)})
* @param readTimeout the read timeout (see
* {@link OkHttpClient.Builder#readTimeout(long, TimeUnit)})
* @param writeTimeout the write timeout (see
* {@link OkHttpClient.Builder#writeTimeout(long, TimeUnit)})
* @param timeUnit the time unit for all timeout values
* @return this Builder instance for method chaining
*/
public Builder networkConfig(int requestTimeOut, int connectTimeout, int readTimeout, int writeTimeout,
TimeUnit timeUnit) {
config.setRequestTimeOut(requestTimeOut);
config.setConnectTimeout(connectTimeout);
config.setReadTimeout(readTimeout);
config.setWriteTimeout(writeTimeout);
config.setTimeOutTimeUnit(timeUnit);
return this;
}
/**
* Builds and returns a new ZaiClient instance with the configured settings.
* @return a new ZaiClient instance
* @throws IllegalStateException if the configuration is invalid
*/
public ZaiClient build() {
return new ZaiClient(config);
}
}
// ==================== Static Factory Methods ====================
/**
* Creates a new Builder instance for constructing ZaiClient.
* @return a new Builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Creates a ZaiClient configured for ZHIPU AI platform with the specified API key.
* @param apiKey the API key for authentication
* @return a new ZaiClient instance configured for ZHIPU AI
* @throws IllegalArgumentException if apiKey is null or empty
*/
public static Builder ofZHIPU(String apiKey) {
return new Builder().apiKey(apiKey).ofZHIPU();
}
/**
* Creates a ZaiClient configured for ZHIPU AI platform with the specified API key.
* @return a new ZaiClient instance configured for ZHIPU AI
* @throws IllegalArgumentException if apiKey is null or empty
*/
public static Builder ofZHIPU() {
return new Builder().ofZHIPU();
}
}

View file

@ -0,0 +1,25 @@
package ai.z.openapi.api.agents;
import ai.z.openapi.service.agents.AgentAsyncResultRetrieveParams;
import ai.z.openapi.service.agents.AgentsCompletionRequest;
import ai.z.openapi.service.model.ModelData;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Streaming;
public interface AgentsApi {
@Streaming
@POST("v1/agents")
Call<ResponseBody> agentsCompletionStream(@Body AgentsCompletionRequest request);
@POST("v1/agents")
Single<ModelData> agentsCompletionSync(@Body AgentsCompletionRequest request);
@POST("v1/agents/async-result")
Single<ModelData> queryAgentsAsyncResult(@Body AgentAsyncResultRetrieveParams request);
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.api.assistant;
import ai.z.openapi.service.assistant.AssistantCompletion;
import ai.z.openapi.service.assistant.AssistantParameters;
import ai.z.openapi.service.assistant.conversation.ConversationUsageListStatus;
import ai.z.openapi.service.assistant.query_support.AssistantSupportStatus;
import ai.z.openapi.service.assistant.conversation.ConversationParameters;
import ai.z.openapi.service.assistant.query_support.QuerySupportParams;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Streaming;
public interface AssistantApi {
@Streaming
@POST("assistant")
Call<ResponseBody> assistantCompletionStream(@Body AssistantParameters request);
@POST("assistant")
Single<AssistantCompletion> assistantCompletion(@Body AssistantParameters request);
@POST("assistant/list")
Single<AssistantSupportStatus> querySupport(@Body QuerySupportParams request);
@POST("assistant/conversation/list")
Single<ConversationUsageListStatus> queryConversationUsage(@Body ConversationParameters request);
}

View file

@ -0,0 +1,50 @@
package ai.z.openapi.api.audio;
import ai.z.openapi.service.audio.AudioSpeechRequest;
import ai.z.openapi.service.model.ModelData;
import io.reactivex.Single;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Streaming;
import java.util.Map;
public interface AudioApi {
/**
* TTS interface (Text to speech)
* @param request
* @return
*/
@POST("audio/speech")
Single<ResponseBody> audioSpeech(@Body AudioSpeechRequest request);
/**
* Voice cloning interface
* @param request
* @return
*/
@Multipart
@POST("audio/customization")
Single<ResponseBody> audioCustomization(@PartMap Map<String, RequestBody> request,
@Part MultipartBody.Part voiceData);
@Streaming
@POST("audio/transcriptions")
@Multipart
Call<ResponseBody> audioTranscriptionsStream(@PartMap Map<String, RequestBody> request,
@Part MultipartBody.Part file);
@POST("audio/transcriptions")
@Multipart
Single<ModelData> audioTranscriptions(@PartMap Map<String, RequestBody> request, @Part MultipartBody.Part file);
}

View file

@ -0,0 +1,27 @@
package ai.z.openapi.api.batches;
import ai.z.openapi.service.batches.Batch;
import ai.z.openapi.service.batches.BatchCreateParams;
import ai.z.openapi.service.batches.BatchPage;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface BatchesApi {
@POST("batches")
Single<Batch> batchesCreate(@Body BatchCreateParams batchCreateParams);
@GET("batches/{batch_id}")
Single<Batch> batchesRetrieve(@Path("batch_id") String batchId);
@GET("batches")
Single<BatchPage> batchesList(@Query("after") String after, @Query("limit") Integer limit);
@POST("batches/{batch_id}/cancel")
Single<Batch> batchesCancel(@Path("batch_id") String batchId);
}

View file

@ -0,0 +1,29 @@
package ai.z.openapi.api.chat;
import ai.z.openapi.service.model.ChatCompletionCreateParams;
import ai.z.openapi.service.model.ModelData;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Streaming;
public interface ChatApi {
@Streaming
@POST("chat/completions")
Call<ResponseBody> createChatCompletionStream(@Body ChatCompletionCreateParams request);
@POST("async/chat/completions")
Single<ModelData> createChatCompletionAsync(@Body ChatCompletionCreateParams request);
@POST("chat/completions")
Single<ModelData> createChatCompletion(@Body ChatCompletionCreateParams request);
@GET("async-result/{id}")
Single<ModelData> queryAsyncResult(@Path("id") String id);
}

View file

@ -0,0 +1,14 @@
package ai.z.openapi.api.embedding;
import ai.z.openapi.service.embedding.EmbeddingCreateParams;
import ai.z.openapi.service.embedding.EmbeddingResult;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface EmbeddingApi {
@POST("embeddings")
Single<EmbeddingResult> createEmbeddings(@Body EmbeddingCreateParams request);
}

View file

@ -0,0 +1,37 @@
package ai.z.openapi.api.file;
import ai.z.openapi.service.file.File;
import ai.z.openapi.service.file.FileDeleted;
import ai.z.openapi.service.file.QueryFileResult;
import io.reactivex.Single;
import okhttp3.MultipartBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
import retrofit2.http.Streaming;
public interface FileApi {
@POST("files")
Single<File> uploadFile(@Body MultipartBody multipartBody);
@GET("files/{file_id}")
Single<File> retrieveFile(@Path("file_id") String fileId);
@DELETE("files/{file_id}")
Single<FileDeleted> deletedFile(@Path("file_id") String fileId);
@GET("files")
Single<QueryFileResult> queryFileList(@Query("after") String after, @Query("purpose") String purpose,
@Query("order") String order, @Query("limit") Integer limit);
@Streaming
@GET("files/{file_id}/content")
Call<ResponseBody> fileContent(@Path("file_id") String fileId);
}

View file

@ -0,0 +1,42 @@
package ai.z.openapi.api.fine_tuning;
import ai.z.openapi.service.fine_turning.FineTunedModelsStatus;
import ai.z.openapi.service.fine_turning.FineTuningEvent;
import ai.z.openapi.service.fine_turning.FineTuningJob;
import ai.z.openapi.service.fine_turning.FineTuningJobRequest;
import ai.z.openapi.service.fine_turning.PersonalFineTuningJob;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface FineTuningApi {
@POST("fine_tuning/jobs")
Single<FineTuningJob> createFineTuningJob(@Body FineTuningJobRequest request);
@GET("fine_tuning/jobs/{fine_tuning_job_id}/events")
Single<FineTuningEvent> listFineTuningJobEvents(@Path("fine_tuning_job_id") String fineTuningJobId,
@Query("limit") Integer limit, @Query("after") String after);
@GET("fine_tuning/jobs/{fine_tuning_job_id}")
Single<FineTuningJob> retrieveFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId,
@Query("limit") Integer limit, @Query("after") String after);
@GET("fine_tuning/jobs")
Single<PersonalFineTuningJob> queryPersonalFineTuningJobs(@Query("limit") Integer limit,
@Query("after") String after);
@POST("fine_tuning/jobs/{fine_tuning_job_id}/cancel")
Single<FineTuningJob> cancelFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId);
@DELETE("fine_tuning/jobs/{fine_tuning_job_id}")
Single<FineTuningJob> deleteFineTuningJob(@Path("fine_tuning_job_id") String fineTuningJobId);
@DELETE("fine_tuning/fine_tuned_models/{fine_tuned_model}")
Single<FineTunedModelsStatus> deleteFineTuningModel(@Path("fine_tuned_model") String fineTunedModel);
}

View file

@ -0,0 +1,14 @@
package ai.z.openapi.api.images;
import ai.z.openapi.service.image.CreateImageRequest;
import ai.z.openapi.service.image.ImageResult;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface ImagesApi {
@POST("images/generations")
Single<ImageResult> createImage(@Body CreateImageRequest request);
}

View file

@ -0,0 +1,36 @@
package ai.z.openapi.api.knowledge;
import ai.z.openapi.service.knowledge.KnowledgeBaseParams;
import ai.z.openapi.service.knowledge.KnowledgeInfo;
import ai.z.openapi.service.knowledge.KnowledgePage;
import ai.z.openapi.service.knowledge.KnowledgeUsed;
import io.reactivex.Single;
import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface KnowledgeApi {
@POST("knowledge")
Single<KnowledgeInfo> knowledgeCreate(@Body KnowledgeBaseParams knowledgeBaseParams);
@PUT("knowledge/{knowledge_id}")
Single<Response<Void>> knowledgeModify(@Path("knowledge_id") String knowledge_id,
@Body KnowledgeBaseParams knowledgeBaseParams);
@GET("knowledge")
Single<KnowledgePage> knowledgeQuery(@Query("page") Integer page, @Query("size") Integer size);
@DELETE("knowledge/{knowledge_id}")
Single<Response<Void>> knowledgeDelete(@Path("knowledge_id") String knowledge_id);
@GET("knowledge/capacity")
Single<KnowledgeUsed> knowledgeUsed();
}

View file

@ -0,0 +1,37 @@
package ai.z.openapi.api.knowledge.document;
import ai.z.openapi.service.knowledge.document.DocumentData;
import ai.z.openapi.service.knowledge.document.DocumentEditParams;
import ai.z.openapi.service.knowledge.document.DocumentObject;
import ai.z.openapi.service.knowledge.document.DocumentPage;
import io.reactivex.Single;
import okhttp3.MultipartBody;
import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface DocumentApi {
@POST("files")
Single<DocumentObject> createDocument(@Body MultipartBody document);
@PUT("document/{document_id}")
Single<Response<Void>> modifyDocument(@Path("document_id") String documentId,
@Body DocumentEditParams documentEditParams);
@DELETE("document/{document_id}")
Single<Response<Void>> deleteDocument(@Path("document_id") String documentId);
@GET("files")
Single<DocumentPage> queryDocumentList(@Query("knowledge_id") String knowledgeId, @Query("purpose") String purpose,
@Query("page") Integer page, @Query("limit") Integer limit, @Query("order") String order);
@GET("document/{document_id}")
Single<DocumentData> retrieveDocument(@Path("document_id") String documentId);
}

View file

@ -0,0 +1,21 @@
package ai.z.openapi.api.tools;
import ai.z.openapi.service.tools.WebSearchParamsRequest;
import ai.z.openapi.service.tools.WebSearchPro;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.Streaming;
public interface ToolsApi {
@Streaming
@POST("tools")
Call<ResponseBody> webSearchStreaming(@Body WebSearchParamsRequest request);
@POST("tools")
Single<WebSearchPro> webSearch(@Body WebSearchParamsRequest request);
}

View file

@ -0,0 +1,19 @@
package ai.z.openapi.api.videos;
import ai.z.openapi.service.videos.VideoCreateParams;
import ai.z.openapi.service.videos.VideoObject;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface VideosApi {
@POST("videos/generations")
Single<VideoObject> videoGenerations(@Body VideoCreateParams request);
@GET("async-result/{id}")
Single<VideoObject> videoGenerationsResult(@Path("id") String id);
}

View file

@ -0,0 +1,14 @@
package ai.z.openapi.api.web_search;
import ai.z.openapi.service.web_search.WebSearchDTO;
import ai.z.openapi.service.web_search.WebSearchRequest;
import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface WebSearchApi {
@POST("web_search")
Single<WebSearchDTO> webSearch(@Body WebSearchRequest request);
}

View file

@ -0,0 +1,155 @@
package ai.z.openapi.core;
/**
* Constants class containing all the configuration values and model identifiers used
* throughout the Z.AI OpenAPI SDK.
*
* This class provides centralized access to: - API base URLs - Model identifiers for
* different AI capabilities - Invocation method constants
*
* @author Z.AI SDK Team
* @since 1.0.0
*/
public final class Constants {
// Private constructor to prevent instantiation
private Constants() {
throw new UnsupportedOperationException("Constants class cannot be instantiated");
}
// =============================================================================
// API Configuration
// =============================================================================
/**
* Base URL for the ZHIPU AI OpenAPI service. All API requests will be made to
* endpoints under this base URL.
*/
public static final String ZHIPU_AI_BASE_URL = "https://open.bigmodel.cn/api/paas/v4/";
/**
* Base URL for the Z.AI OpenAPI service. All API requests will be made to endpoints
* under this base URL.
*/
public static final String Z_AI_BASE_URL = "https://api.z.ai/api/paas/v4/";
// =============================================================================
// Text Generation Models
// =============================================================================
/**
* GLM-4 Plus model - Enhanced version with improved capabilities.
*/
public static final String ModelChatGLM4Plus = "glm-4-plus";
/**
* GLM-4 Air model - Lightweight version optimized for speed.
*/
public static final String ModelChatGLM4Air = "glm-4-air";
/**
* GLM-4 Flash model - Ultra-fast response model.
*/
public static final String ModelChatGLM4Flash = "glm-4-flash";
/**
* GLM-4 standard model - Balanced performance and capability.
*/
public static final String ModelChatGLM4 = "glm-4";
/**
* GLM-4 model version 0520 - Specific version release.
*/
public static final String ModelChatGLM40520 = "glm-4-0520";
/**
* GLM-4 AirX model - Extended Air model with additional features.
*/
public static final String ModelChatGLM4Airx = "glm-4-airx";
/**
* GLM-4 Long model - Optimized for long-context conversations.
*/
public static final String ModelChatGLMLong = "glm-4-long";
/**
* GLM-4 Voice model - Specialized for voice-related tasks.
*/
public static final String ModelChatGLM4Voice = "glm-4-voice";
// =============================================================================
// Vision Models (Image Understanding)
// =============================================================================
/**
* GLM-4V Plus model - Enhanced vision model for image understanding.
*/
public static final String ModelChatGLM4VPlus = "glm-4v-plus";
/**
* GLM-4V standard model - Standard vision model for image analysis.
*/
public static final String ModelChatGLM4V = "glm-4v";
// =============================================================================
// Image Generation Models
// =============================================================================
/**
* CogView-3 Plus model - Enhanced image generation capabilities.
*/
public static final String ModelCogView3Plus = "cogview-3-plus";
/**
* CogView-3 standard model - Standard image generation model.
*/
public static final String ModelCogView = "cogview-3";
// =============================================================================
// Embedding Models
// =============================================================================
/**
* Embedding model version 2 - Text embedding generation.
*/
public static final String ModelEmbedding2 = "embedding-2";
/**
* Embedding model version 3 - Latest text embedding generation.
*/
public static final String ModelEmbedding3 = "embedding-3";
// =============================================================================
// Specialized Models
// =============================================================================
/**
* CharGLM-3 model - Anthropomorphic character interaction model.
*/
public static final String ModelCharGLM3 = "charglm-3";
/**
* CogTTS model - Text-to-Speech synthesis model.
*/
public static final String ModelTTS = "cogtts";
// =============================================================================
// API Invocation Methods
// =============================================================================
/**
* Asynchronous invocation method - For non-blocking API calls.
*/
public static final String INVOKE_METHOD_ASYNC = "async-invoke";
/**
* Server-Sent Events invocation method - For streaming responses.
*/
public static final String INVOKE_METHOD_SSE = "sse-invoke";
/**
* Standard synchronous invocation method - For blocking API calls.
*/
public static final String INVOKE_METHOD = "invoke";
}

View file

@ -0,0 +1,27 @@
package ai.z.openapi.core.cache;
import java.util.concurrent.TimeUnit;
/**
* Token cache interface with default LocalCache implementation. Can be replaced with
* distributed cache (e.g., Redis) as needed.
*/
public interface ICache {
/**
* Retrieves cached value by key.
* @param key the cache key
* @return cached value or empty string if not found or expired
*/
String get(String key);
/**
* Sets cache value with expiration time.
* @param key the cache key
* @param value the value to cache
* @param expire expiration duration
* @param timeUnit time unit for expiration
*/
void set(String key, String value, int expire, TimeUnit timeUnit);
}

View file

@ -0,0 +1,81 @@
package ai.z.openapi.core.cache;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Thread-safe local cache implementation using ConcurrentHashMap. Provides basic caching
* functionality with expiration support.
*/
public class LocalCache implements ICache {
private static final Logger log = LoggerFactory.getLogger(LocalCache.class);
private static final ConcurrentMap<String, Value> CACHE = new ConcurrentHashMap<>(8);
/**
* Private constructor to prevent direct instantiation.
*/
private LocalCache() {
}
/**
* Gets singleton instance of LocalCache.
* @return LocalCache instance
*/
public static LocalCache getInstance() {
return Inner.LOCAL_CACHE;
}
@Override
public String get(String key) {
Value v = LocalCache.CACHE.get(key);
if (v == null || new Date().after(v.end)) {
return "";
}
log.debug("Retrieved key: {}, time left: {}s", key, (v.end.getTime() - new Date().getTime()) / 1000);
return v.value;
}
@Override
public void set(String key, String value, int expire, TimeUnit timeUnit) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, (int) timeUnit.toSeconds(expire));
Value v = new Value(value, calendar.getTime());
log.debug("Cached key: {}, expire time: {}", key, calendar.getTime());
LocalCache.CACHE.put(key, v);
}
/**
* Internal value wrapper with expiration time.
*/
private static class Value {
final String value;
final Date end;
public Value(String value, Date time) {
this.value = value;
this.end = time;
}
}
/**
* Singleton holder pattern for thread-safe lazy initialization.
*/
private static class Inner {
private static final LocalCache LOCAL_CACHE = new LocalCache();
}
}

View file

@ -0,0 +1,400 @@
package ai.z.openapi.core.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.concurrent.TimeUnit;
import static ai.z.openapi.core.Constants.Z_AI_BASE_URL;
/**
* Configuration class for ZAI SDK containing API credentials, JWT settings, HTTP client
* configurations, and cache settings. Supports reading configuration values from
* environment variables with memory values taking priority.
*/
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ZaiConfig {
// Environment variable names
private static final String ENV_BASE_URL = "ZAI_BASE_URL";
private static final String ENV_API_KEY = "ZAI_API_KEY";
private static final String ENV_EXPIRE_MILLIS = "ZAI_EXPIRE_MILLIS";
private static final String ENV_ALG = "ZAI_ALG";
private static final String ENV_DISABLE_TOKEN_CACHE = "ZAI_DISABLE_TOKEN_CACHE";
private static final String ENV_CONNECTION_POOL_MAX_IDLE = "ZAI_CONNECTION_POOL_MAX_IDLE";
private static final String ENV_CONNECTION_POOL_KEEP_ALIVE = "ZAI_CONNECTION_POOL_KEEP_ALIVE";
private static final String ENV_REQUEST_TIMEOUT = "ZAI_REQUEST_TIMEOUT";
private static final String ENV_CONNECT_TIMEOUT = "ZAI_CONNECT_TIMEOUT";
private static final String ENV_READ_TIMEOUT = "ZAI_READ_TIMEOUT";
private static final String ENV_WRITE_TIMEOUT = "ZAI_WRITE_TIMEOUT";
/**
* Base URL for API endpoints.
*/
private String baseUrl;
/**
* Combined API secret key in format: {apiId}.{apiSecret}
*/
private String apiKey;
/**
* API id component.
*/
private String apiId;
/**
* API secret component.
*/
private String apiSecret;
/**
* JWT token expiration time in milliseconds (default: 30 minutes).
*/
private int expireMillis = 30 * 60 * 1000;
/**
* JWT encryption algorithm (default: HS256).
*/
private String alg = "HS256";
/**
* Flag to disable token caching.
*/
private boolean disableTokenCache;
/**
* Maximum number of idle connections in the connection pool.
*/
private int connectionPoolMaxIdleConnections = 5;
/**
* Keep alive duration for connections in the pool (in seconds).
*/
private long connectionPoolKeepAliveDuration = 1;
/**
* Time unit for connection pool keep alive duration.
*/
private TimeUnit connectionPoolTimeUnit = TimeUnit.SECONDS;
/**
* Request timeout in specified time unit.
*/
private int requestTimeOut = 300;
/**
* Connection timeout in specified time unit.
*/
private int connectTimeout = 100;
/**
* Read timeout in specified time unit.
*/
private int readTimeout = 100;
/**
* Write timeout in specified time unit.
*/
private int writeTimeout = 100;
/**
* Time unit for timeout configurations.
*/
private TimeUnit timeOutTimeUnit = TimeUnit.SECONDS;
/**
* Source channel identifier for request tracking.
*/
private String source_channel = "java-sdk";
/**
* Constructor with combined API secret key.
* @param apiKey combined secret key in format {apiKey}.{apiSecret}
* @throws RuntimeException if apiSecretKey format is invalid
*/
public ZaiConfig(String apiKey) {
this.apiKey = apiKey;
String[] arrStr = apiKey.split("\\.");
if (arrStr.length != 2) {
throw new RuntimeException("invalid apiSecretKey");
}
this.apiId = arrStr[0];
this.apiSecret = arrStr[1];
}
/**
* Sets API key and parses it into id and secret components.
* @param apiKey combined secret key in format {apiId}.{apiSecret}
* @throws RuntimeException if apiSecretKey format is invalid
*/
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
String[] arrStr = apiKey.split("\\.");
if (arrStr.length != 2) {
throw new RuntimeException("invalid api Key");
}
this.apiId = arrStr[0];
this.apiSecret = arrStr[1];
}
/**
* Gets base URL with system property and environment variable fallback.
*/
public String getBaseUrl() {
if (baseUrl != null) {
return baseUrl;
}
String propValue = System.getProperty(ENV_BASE_URL);
propValue = propValue != null ? propValue : System.getenv(ENV_BASE_URL);
return propValue != null ? propValue : Z_AI_BASE_URL;
}
/**
* Gets API secret key with system property and environment variable fallback.
*/
public String getApiKey() {
if (apiKey != null && !apiKey.isEmpty()) {
return apiKey;
}
String propValue = System.getProperty(ENV_API_KEY);
String value = propValue != null ? propValue : System.getenv(ENV_API_KEY);
if (value != null && !value.isEmpty()) {
// Parse value and set components
this.apiKey = value;
String[] arrStr = value.split("\\.");
if (arrStr.length == 2) {
this.apiId = arrStr[0];
this.apiSecret = arrStr[1];
}
return value;
}
return apiKey;
}
/**
* Gets API key with system property and environment variable fallback.
*/
public String getApiId() {
if (apiId != null && !apiId.isEmpty()) {
return apiId;
}
getApiKey();
return apiId;
}
/**
* Gets API secret with system property and environment variable fallback.
*/
public String getApiSecret() {
if (apiSecret != null && !apiSecret.isEmpty()) {
return apiSecret;
}
getApiKey();
return apiSecret;
}
/**
* Gets expire millis with system property and environment variable fallback.
*/
public int getExpireMillis() {
if (expireMillis != 30 * 60 * 1000) { // If not default value
return expireMillis;
}
String propValue = System.getProperty(ENV_EXPIRE_MILLIS);
String value = propValue != null ? propValue : System.getenv(ENV_EXPIRE_MILLIS);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return expireMillis;
}
/**
* Gets algorithm with system property and environment variable fallback.
*/
public String getAlg() {
if (!"HS256".equals(alg)) {
return alg;
}
String propValue = System.getProperty(ENV_ALG);
String value = propValue != null ? propValue : System.getenv(ENV_ALG);
return value != null ? value : alg;
}
/**
* Gets disable token cache flag with system property and environment variable
* fallback.
*/
public boolean isDisableTokenCache() {
if (disableTokenCache) {
return true;
}
String propValue = System.getProperty(ENV_DISABLE_TOKEN_CACHE);
String value = propValue != null ? propValue : System.getenv(ENV_DISABLE_TOKEN_CACHE);
return Boolean.parseBoolean(value);
}
/**
* Gets connection pool max idle connections with system property and environment
* variable fallback.
*/
public int getConnectionPoolMaxIdleConnections() {
if (connectionPoolMaxIdleConnections != 5) { // If not default value
return connectionPoolMaxIdleConnections;
}
String propValue = System.getProperty(ENV_CONNECTION_POOL_MAX_IDLE);
String value = propValue != null ? propValue : System.getenv(ENV_CONNECTION_POOL_MAX_IDLE);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return connectionPoolMaxIdleConnections;
}
/**
* Gets connection pool keep alive duration with system property and environment
* variable fallback.
*/
public long getConnectionPoolKeepAliveDuration() {
if (connectionPoolKeepAliveDuration != 1) { // If not default value
return connectionPoolKeepAliveDuration;
}
String propValue = System.getProperty(ENV_CONNECTION_POOL_KEEP_ALIVE);
String value = propValue != null ? propValue : System.getenv(ENV_CONNECTION_POOL_KEEP_ALIVE);
if (value != null) {
try {
return Long.parseLong(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return connectionPoolKeepAliveDuration;
}
/**
* Gets connection pool time unit (always returns the set value or default).
*/
public TimeUnit getConnectionPoolTimeUnit() {
return connectionPoolTimeUnit != null ? connectionPoolTimeUnit : TimeUnit.SECONDS;
}
/**
* Gets request timeout with system property and environment variable fallback.
*/
public int getRequestTimeOut() {
if (requestTimeOut != 300) {
return requestTimeOut;
}
String propValue = System.getProperty(ENV_REQUEST_TIMEOUT);
String value = propValue != null ? propValue : System.getenv(ENV_REQUEST_TIMEOUT);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return requestTimeOut;
}
/**
* Gets connect timeout with system property and environment variable fallback.
*/
public int getConnectTimeout() {
if (connectTimeout != 100) {
return connectTimeout;
}
String propValue = System.getProperty(ENV_CONNECT_TIMEOUT);
String value = propValue != null ? propValue : System.getenv(ENV_CONNECT_TIMEOUT);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return connectTimeout;
}
/**
* Gets read timeout with system property and environment variable fallback.
*/
public int getReadTimeout() {
if (readTimeout != 100) {
return readTimeout;
}
String propValue = System.getProperty(ENV_READ_TIMEOUT);
String value = propValue != null ? propValue : System.getenv(ENV_READ_TIMEOUT);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return readTimeout;
}
/**
* Gets write timeout with system property and environment variable fallback.
*/
public int getWriteTimeout() {
if (writeTimeout != 100) {
return writeTimeout;
}
String propValue = System.getProperty(ENV_WRITE_TIMEOUT);
String value = propValue != null ? propValue : System.getenv(ENV_WRITE_TIMEOUT);
if (value != null) {
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
// Return default value if parsing fails
}
}
return writeTimeout;
}
/**
* Gets timeout time unit (always returns the set value or null).
*/
public TimeUnit getTimeOutTimeUnit() {
return timeOutTimeUnit;
}
/**
* Gets source channel with system property.
*/
public String getSource_channel() {
return source_channel;
}
}

View file

@ -0,0 +1,5 @@
package ai.z.openapi.core.model;
public interface ClientRequest<T> {
}

View file

@ -0,0 +1,19 @@
package ai.z.openapi.core.model;
import ai.z.openapi.service.model.ChatError;
public interface ClientResponse<T> {
T getData();
void setData(T data);
void setCode(int code);
void setMsg(String msg);
void setSuccess(boolean b);
void setError(ChatError chatError);
}

View file

@ -0,0 +1,19 @@
package ai.z.openapi.core.model;
import io.reactivex.Flowable;
/**
* Client response interface with reactive stream support. Extends ClientResponse to
* provide Flowable stream functionality.
*
* @param <T> response data type
*/
public interface FlowableClientResponse<T> extends ClientResponse<T> {
/**
* Sets the reactive stream for this response.
* @param stream Flowable stream containing response data
*/
void setFlowable(Flowable<T> stream);
}

View file

@ -0,0 +1,156 @@
package ai.z.openapi.core.response;
import java.io.Closeable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Objects;
import okhttp3.ResponseBody;
import okio.BufferedSource;
public class HttpxBinaryResponseContent implements Closeable {
private final retrofit2.Response<ResponseBody> response;
public HttpxBinaryResponseContent(retrofit2.Response<ResponseBody> response) {
if (response == null || response.body() == null) {
throw new IllegalArgumentException("Response or ResponseBody cannot be null");
}
this.response = response;
}
public byte[] getContent() throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
try (BufferedSource source = response.body().source()) {
return source.readByteArray();
}
}
public String getText() throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
try (BufferedSource source = response.body().source()) {
return source.readUtf8();
}
}
public String getEncoding() {
return response.body() != null && response.body().contentType() != null
? Objects.requireNonNull(Objects.requireNonNull(response.body().contentType()).charset()).toString()
: null;
}
public Iterator<byte[]> iterBytes(int chunkSize) throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
return new Iterator<byte[]>() {
final BufferedSource source = response.body().source();
final byte[] buffer = new byte[chunkSize];
boolean hasMore = true;
@Override
public boolean hasNext() {
try {
if (source.exhausted()) {
hasMore = false;
}
}
catch (IOException e) {
hasMore = false;
}
return hasMore;
}
@Override
public byte[] next() {
try {
source.read(buffer);
}
catch (IOException e) {
// Handle the exception
}
return buffer;
}
};
}
public Iterator<String> iterText(int chunkSize) throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
return new Iterator<String>() {
final BufferedSource source = response.body().source();
final byte[] buffer = new byte[chunkSize];
boolean hasMore = true;
@Override
public boolean hasNext() {
try {
if (source.exhausted()) {
hasMore = false;
}
}
catch (IOException e) {
hasMore = false;
}
return hasMore;
}
@Override
public String next() {
try {
int bytesRead = source.read(buffer);
return new String(buffer, 0, bytesRead);
}
catch (IOException e) {
// Handle the exception
}
return "";
}
};
}
public void writeToFile(String file) throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
try (BufferedSource source = response.body().source(); FileOutputStream fos = new FileOutputStream(file)) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = source.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
public void streamToFile(String file, int chunkSize) throws IOException {
if (response.body() == null) {
throw new IOException("ResponseBody is null");
}
try (BufferedSource source = response.body().source(); FileOutputStream fos = new FileOutputStream(file)) {
byte[] buffer = new byte[chunkSize];
int bytesRead;
while ((bytesRead = source.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
@Override
public void close() throws IOException {
if (response.body() != null) {
response.body().close();
}
}
}

View file

@ -0,0 +1,47 @@
package ai.z.openapi.core.token;
import ai.z.openapi.core.config.ZaiConfig;
import ai.z.openapi.utils.StringUtils;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.Objects;
/**
* OkHttp Interceptor that adds an authorization token header
*/
public class AuthenticationInterceptor implements Interceptor {
private final ZaiConfig config;
public AuthenticationInterceptor(ZaiConfig config) {
Objects.requireNonNull(config.getApiKey(), "Z.ai token required");
this.config = config;
}
@Override
public Response intercept(Chain chain) throws IOException {
String accessToken;
if (this.config.isDisableTokenCache()) {
accessToken = this.config.getApiKey();
}
else {
TokenManager tokenManager = GlobalTokenManager.getTokenManagerV4();
accessToken = tokenManager.getToken(this.config);
}
String source_channel = "java-sdk";
if (StringUtils.isNotEmpty(config.getSource_channel())) {
source_channel = config.getSource_channel();
}
Request request = chain.request()
.newBuilder()
.header("Authorization", "Bearer " + accessToken)
.header("x-source-channel", source_channel)
.header("Accept-Language", "en-US,en")
.build();
return chain.proceed(request);
}
}

View file

@ -0,0 +1,32 @@
package ai.z.openapi.core.token;
import ai.z.openapi.core.cache.LocalCache;
/**
* Global token manager providing singleton access to TokenManager instance. Uses
* LocalCache as default cache implementation.
*/
public class GlobalTokenManager {
/**
* Global TokenManager instance with LocalCache.
*/
private static volatile TokenManager globalTokenManager = new TokenManager(LocalCache.getInstance());
/**
* Gets the global TokenManager instance.
* @return TokenManager instance
*/
public static TokenManager getTokenManagerV4() {
return globalTokenManager;
}
/**
* Sets custom TokenManager implementation.
* @param tokenManager custom TokenManager instance
*/
public static void setTokenManager(TokenManager tokenManager) {
globalTokenManager = tokenManager;
}
}

View file

@ -0,0 +1,100 @@
package ai.z.openapi.core.token;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import ai.z.openapi.core.config.ZaiConfig;
import ai.z.openapi.core.cache.ICache;
import ai.z.openapi.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* JWT token manager for handling token generation and caching.
*/
public class TokenManager {
private static final Logger logger = LoggerFactory.getLogger(TokenManager.class);
private final ICache cache;
private static final String TOKEN_KEY_PREFIX = "zai_oapi_token";
/**
* Additional delay time (5 minutes) to prevent token expiration issues.
*/
private static final Long DELAY_EXPIRE_TIME = 5 * 60 * 1000L;
/**
* Constructs TokenManager with specified cache implementation.
* @param cache cache implementation for token storage
*/
public TokenManager(ICache cache) {
this.cache = cache;
}
/**
* Gets valid JWT token, either from cache or generates new one.
* @param config ZAI configuration containing API credentials
* @return valid JWT token
*/
public String getToken(ZaiConfig config) {
String tokenCacheKey = genTokenCacheKey(config.getApiId());
String cacheToken = cache.get(tokenCacheKey);
if (StringUtils.isNotEmpty(cacheToken)) {
return cacheToken;
}
String newToken = createJwt(config);
cache.set(tokenCacheKey, newToken, config.getExpireMillis(), TimeUnit.MILLISECONDS);
return newToken;
}
/**
* Creates JWT token using HMAC256 algorithm.
* @param config ZAI configuration
* @return JWT token string or null if creation fails
*/
private static String createJwt(ZaiConfig config) {
Algorithm alg;
String algId = config.getAlg();
if ("HS256".equals(algId)) {
try {
alg = Algorithm.HMAC256(config.getApiSecret().getBytes(StandardCharsets.UTF_8));
}
catch (Exception e) {
logger.error("Failed to create HMAC256 algorithm", e);
return null;
}
}
else {
// Currently only HS256 is supported
logger.error("Algorithm: {} not supported", algId);
return null;
}
Map<String, Object> payload = new HashMap<>();
// here the api_key is the apiId
payload.put("api_key", config.getApiId());
payload.put("exp", System.currentTimeMillis() + config.getExpireMillis() + DELAY_EXPIRE_TIME);
payload.put("timestamp", Calendar.getInstance().getTimeInMillis());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "HS256");
headerClaims.put("sign_type", "SIGN");
return JWT.create().withPayload(payload).withHeader(headerClaims).sign(alg);
}
/**
* Generates cache key for token storage.
* @param apiKey API key
* @return formatted cache key
*/
private String genTokenCacheKey(String apiKey) {
return String.format("%s-%s", TOKEN_KEY_PREFIX, apiKey);
}
}

View file

@ -0,0 +1,147 @@
package ai.z.openapi.service;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.core.model.FlowableClientResponse;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.model.ResponseBodyCallback;
import ai.z.openapi.service.model.SSE;
import ai.z.openapi.service.model.ZAiError;
import ai.z.openapi.service.model.ZAiHttpException;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.Single;
import okhttp3.ResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import retrofit2.Call;
import retrofit2.HttpException;
import retrofit2.Response;
import java.io.IOException;
/**
* Abstract base service class that provides common functionality for API client
* implementations. This class handles request execution, response processing, and error
* handling for both synchronous and streaming API calls.
*/
public abstract class AbstractClientBaseService {
protected final static Logger logger = LoggerFactory.getLogger(AbstractClientBaseService.class);
protected static final ObjectMapper mapper = MessageDeserializeFactory.defaultObjectMapper();
/**
* Executes a synchronous API request.
* @param <Data> the type of data returned by the API
* @param <Param> the type of parameters sent to the API
* @param <TReq> the type of the request object
* @param <TResp> the type of the response object
* @param request the request object containing parameters
* @param requestSupplier the supplier that creates the API call
* @param tRespClass the class of the response type
* @return the response object containing the API result
*/
public abstract <Data, Param, TReq extends ClientRequest<Param>, TResp extends ClientResponse<Data>> TResp executeRequest(
TReq request, RequestSupplier<Param, Data> requestSupplier, Class<TResp> tRespClass);
/**
* Executes a streaming API request that returns a continuous stream of data.
* @param <Data> the type of data returned by the API stream
* @param <Param> the type of parameters sent to the API
* @param <TReq> the type of the request object
* @param <TResp> the type of the streaming response object
* @param request the request object containing parameters
* @param requestSupplier the supplier that creates the streaming API call
* @param tRespClass the class of the response type
* @param tDataClass the class of the data type in the stream
* @return the streaming response object containing the API result stream
*/
public abstract <Data, Param, TReq extends ClientRequest<Param>, TResp extends FlowableClientResponse<Data>> TResp streamRequest(
TReq request, FlowableRequestSupplier<Param, Call<ResponseBody>> requestSupplier, Class<TResp> tRespClass,
Class<Data> tDataClass);
/**
* Executes a Single API call synchronously and handles errors.
* @param <T> the type of the response
* @param apiCall the Single API call to execute
* @return the response from the API call
* @throws ZAiHttpException if an HTTP error occurs with a parseable error body
* @throws HttpException if an HTTP error occurs that cannot be parsed
*/
public static <T> T execute(Single<T> apiCall) {
try {
T response = apiCall.blockingGet();
// Check status code if the response is a Response type object
if (response instanceof Response) {
handleResponse((Response<?>) response);
}
return response;
}
catch (HttpException e) {
logger.error("HTTP exception: {}", e.getMessage());
try {
if (e.response() == null || e.response().errorBody() == null) {
throw e;
}
String errorBody = e.response().errorBody().string();
ZAiError error = mapper.readValue(errorBody, ZAiError.class);
throw new ZAiHttpException(error, e, e.code());
}
catch (IOException ex) {
// couldn't parse ZAiError error
throw e;
}
}
}
/**
* Handles HTTP response and throws exception if not successful.
* @param response the HTTP response to handle
* @throws HttpException if the response is not successful
*/
private static void handleResponse(Response<?> response) {
if (!response.isSuccessful()) {
throw new HttpException(response);
}
}
/**
* Creates a streaming Flowable that maps SSE data to the specified class type.
* @param <T> the type to map the SSE data to
* @param apiCall the API call that returns streaming data
* @param cl the class to map the SSE data to
* @return a Flowable of the specified type
*/
public <T> Flowable<T> stream(retrofit2.Call<ResponseBody> apiCall, Class<T> cl) {
return stream(apiCall).map(sse -> mapper.readValue(sse.getData(), cl));
}
/**
* Creates a streaming Flowable of SSE events without emitting done events.
* @param apiCall the API call that returns streaming data
* @return a Flowable of SSE events
*/
public static Flowable<SSE> stream(retrofit2.Call<ResponseBody> apiCall) {
return stream(apiCall, false);
}
/**
* Creates a streaming Flowable of SSE events with optional done event emission.
* @param apiCall the API call that returns streaming data
* @param emitDone whether to emit done events
* @return a Flowable of SSE events
*/
public static Flowable<SSE> stream(retrofit2.Call<ResponseBody> apiCall, boolean emitDone) {
return Flowable.create(emitter -> apiCall.enqueue(new ResponseBodyCallback(emitter, emitDone)),
BackpressureStrategy.BUFFER);
}
}

View file

@ -0,0 +1,38 @@
package ai.z.openapi.service;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.Map;
/**
* Common request base class with extra JSON fields. Other ZAI request classes should
* extend this class to inherit common request parameters.
*/
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Data
public class CommonRequest {
/**
* Request ID provided by the client, must be unique. Used to distinguish each
* request. If not provided by the client, the platform will generate one by default.
*/
@JsonProperty("request_id")
private String requestId;
/**
* A unique identifier representing your end-user, which will help ZAI to monitor and
* detect abuse.
*/
@JsonProperty("user_id")
private String userId;
private Map<String, Object> extraJson;
}

View file

@ -0,0 +1,43 @@
package ai.z.openapi.service.agents;
import ai.z.openapi.core.model.ClientRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.HashMap;
import java.util.Map;
/**
* Parameters for retrieving agent asynchronous task results. This class contains the
* necessary parameters to query the result of an agent's async task.
*/
@EqualsAndHashCode(callSuper = false)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AgentAsyncResultRetrieveParams implements ClientRequest<Map<String, Object>> {
/**
* The ID of the agent async task to retrieve
*/
@JsonProperty("task_id")
private String taskId;
/**
* The agent ID associated with the async task
*/
@JsonProperty("agent_id")
private String agentId;
/**
* Optional request ID for tracking
*/
@JsonProperty("request_id")
private String requestId;
}

View file

@ -0,0 +1,27 @@
package ai.z.openapi.service.agents;
import ai.z.openapi.service.model.ChatCompletionResponse;
import ai.z.openapi.service.model.ModelData;
import io.reactivex.Single;
/**
* Agent completion service interface
*/
public interface AgentService {
/**
* Creates an agent completion, either streaming or non-streaming based on the request
* configuration.
* @param request the agents completion request
* @return ChatCompletionResponse containing the agent completion result
*/
ChatCompletionResponse createAgentCompletion(AgentsCompletionRequest request);
/**
* Retrieves the result of an asynchronous agent operation.
* @param request the query request for the async agent result
* @return Single<ModelData> containing the async agent operation result
*/
Single<ModelData> retrieveAgentAsyncResult(AgentAsyncResultRetrieveParams request);
}

View file

@ -0,0 +1,52 @@
package ai.z.openapi.service.agents;
import ai.z.openapi.ZaiClient;
import ai.z.openapi.api.agents.AgentsApi;
import ai.z.openapi.service.model.ChatCompletionResponse;
import ai.z.openapi.service.model.ModelData;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
import io.reactivex.Single;
import okhttp3.ResponseBody;
/**
* Agent completion service implementation
*/
public class AgentServiceImpl implements AgentService {
private final ZaiClient zAiClient;
private final AgentsApi agentsApi;
public AgentServiceImpl(ZaiClient zAiClient) {
this.zAiClient = zAiClient;
this.agentsApi = zAiClient.retrofit().create(AgentsApi.class);
}
@Override
public ChatCompletionResponse createAgentCompletion(AgentsCompletionRequest request) {
if (request.getStream()) {
return streamAgentCompletion(request);
}
else {
return syncAgentCompletion(request);
}
}
@Override
public Single<ModelData> retrieveAgentAsyncResult(AgentAsyncResultRetrieveParams request) {
return agentsApi.queryAgentsAsyncResult(request);
}
private ChatCompletionResponse streamAgentCompletion(AgentsCompletionRequest request) {
FlowableRequestSupplier<AgentsCompletionRequest, retrofit2.Call<ResponseBody>> supplier = agentsApi::agentsCompletionStream;
;
return this.zAiClient.streamRequest(request, supplier, ChatCompletionResponse.class, ModelData.class);
}
private ChatCompletionResponse syncAgentCompletion(AgentsCompletionRequest request) {
RequestSupplier<AgentsCompletionRequest, ModelData> supplier = agentsApi::agentsCompletionSync;
return this.zAiClient.executeRequest(request, supplier, ChatCompletionResponse.class);
}
}

View file

@ -0,0 +1,60 @@
package ai.z.openapi.service.agents;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import ai.z.openapi.service.model.ChatMessage;
import ai.z.openapi.service.model.SensitiveWordCheckRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Request parameters for agent completion API calls. This class contains all the
* necessary parameters to initiate an agent completion request, including agent ID,
* messages, streaming options, and custom variables.
*/
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AgentsCompletionRequest extends CommonRequest implements ClientRequest<AgentsCompletionRequest> {
/**
* Agent ID
*/
@JsonProperty("agent_id")
private String agent_id;
/**
* Message body
*/
private List<ChatMessage> messages;
/**
* Synchronous call: false, SSE call: true
*/
private Boolean stream;
/**
* Sensitive word detection control
*/
@JsonProperty("sensitive_word_check")
private SensitiveWordCheckRequest sensitiveWordCheck;
/**
* Agent business fields
* @return
*/
@JsonProperty("custom_variables")
private ObjectNode custom_variables;
}

View file

@ -0,0 +1,62 @@
package ai.z.openapi.service.assistant;
import ai.z.openapi.core.model.FlowableClientResponse;
import ai.z.openapi.service.model.ChatError;
import io.reactivex.Flowable;
import lombok.Data;
/**
* Response wrapper for Assistant API calls that supports both synchronous and streaming
* responses. This class implements FlowableClientResponse to handle streaming assistant
* completions.
*/
@Data
public class AssistantApiResponse implements FlowableClientResponse<AssistantCompletion> {
/**
* Response status code.
*/
private int code;
/**
* Response message.
*/
private String msg;
/**
* Indicates whether the request was successful.
*/
private boolean success;
/**
* The assistant completion data for synchronous responses.
*/
private AssistantCompletion data;
/**
* The flowable stream for streaming responses.
*/
private Flowable<AssistantCompletion> flowable;
/**
* Error information if the request failed.
*/
private ChatError error;
/**
* Default constructor.
*/
public AssistantApiResponse() {
}
/**
* Constructor with code and message.
* @param code the response code
* @param msg the response message
*/
public AssistantApiResponse(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

View file

@ -0,0 +1,26 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents an attachment with a file ID.
*/
public class AssistantAttachments {
/**
* The ID of the file attachment.
*/
@JsonProperty("file_id")
private String fileId;
// Getters and Setters
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
}

View file

@ -0,0 +1,138 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ai.z.openapi.service.assistant.message.MessageContent;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.deserialize.assistant.AssistantChoiceDeserializer;
import java.util.Iterator;
import java.util.Map;
/**
* Represents an assistant's choice output in conversation responses. This class contains
* the response index, message content, finish reason, and metadata.
*/
@JsonDeserialize(using = AssistantChoiceDeserializer.class)
public class AssistantChoice extends ObjectNode {
/**
* Result index.
*/
@JsonProperty("index")
private int index;
/**
* Current conversation output message content.
*/
@JsonProperty("delta")
private MessageContent delta;
/**
* Reason for inference completion: - stop: inference naturally ended or triggered
* stop words - sensitive: model inference content was blocked by security review -
* network_error: model inference service exception
*/
@JsonProperty("finish_reason")
private String finishReason;
/**
* Metadata, extension field.
*/
@JsonProperty("metadata")
private Map<String, Object> metadata;
public AssistantChoice() {
super(JsonNodeFactory.instance);
}
public AssistantChoice(ObjectNode objectNode) {
super(JsonNodeFactory.instance);
if (objectNode == null) {
return;
}
ObjectMapper objectMapper = MessageDeserializeFactory.defaultObjectMapper();
if (objectNode.has("index")) {
this.setIndex(objectNode.get("index").asInt());
}
else {
this.setIndex(0);
}
if (objectNode.has("delta")) {
MessageContent delta = objectMapper.convertValue(objectNode.get("delta"), MessageContent.class);
this.setDelta(delta);
}
else {
this.setDelta(null);
}
if (objectNode.has("finish_reason")) {
this.setFinishReason(objectNode.get("finish_reason").asText());
}
else {
this.setFinishReason(null);
}
if (objectNode.has("metadata")) {
Map<String, Object> metadata = objectMapper.convertValue(objectNode.get("metadata"),
new TypeReference<Map<String, Object>>() {
});
this.setMetadata(metadata);
}
else {
this.setMetadata(null);
}
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode field = objectNode.get(fieldName);
this.set(fieldName, field);
}
}
// Getters and Setters
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
this.put("index", index);
}
public MessageContent getDelta() {
return delta;
}
public void setDelta(MessageContent delta) {
this.delta = delta;
this.putPOJO("delta", delta);
}
public String getFinishReason() {
return finishReason;
}
public void setFinishReason(String finishReason) {
this.finishReason = finishReason;
this.put("finish_reason", finishReason);
}
public Map<String, Object> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
this.putPOJO("metadata", metadata);
}
}

View file

@ -0,0 +1,254 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.deserialize.assistant.AssistantCompletionDeserializer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* This class represents the completion data returned by an assistant.
*/
@JsonDeserialize(using = AssistantCompletionDeserializer.class)
public class AssistantCompletion extends ObjectNode {
/**
* Request ID
*/
@JsonProperty("id")
private String id;
/**
* Conversation ID
*/
@JsonProperty("conversation_id")
private String conversationId;
/**
* Assistant ID
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* Request creation time, Unix timestamp
*/
@JsonProperty("created")
private int created;
/**
* Return status, including: `completed` indicates generation finished, `in_progress`
* indicates generating, `failed` indicates generation exception
*/
@JsonProperty("status")
private String status;
/**
* Error information
*/
@JsonProperty("last_error")
private ErrorInfo lastError;
/**
* Incremental return information
*/
@JsonProperty("choices")
private List<AssistantChoice> choices;
/**
* Metadata, extension field
*/
@JsonProperty("metadata")
private Map<String, Object> metadata;
/**
* Token count statistics
*/
@JsonProperty("usage")
private CompletionUsage usage;
public AssistantCompletion() {
super(JsonNodeFactory.instance);
}
public AssistantCompletion(ObjectNode objectNode) {
super(JsonNodeFactory.instance);
ObjectMapper objectMapper = MessageDeserializeFactory.defaultObjectMapper();
if (objectNode.has("id")) {
this.setId(objectNode.get("id").asText());
}
else {
this.setId(null);
}
if (objectNode.has("conversation_id")) {
this.setConversationId(objectNode.get("conversation_id").asText());
}
else {
this.setConversationId(null);
}
if (objectNode.has("assistant_id")) {
this.setAssistantId(objectNode.get("assistant_id").asText());
}
else {
this.setAssistantId(null);
}
if (objectNode.has("created")) {
this.setCreated(objectNode.get("created").asInt());
}
else {
this.setCreated(0);
}
if (objectNode.has("status")) {
this.setStatus(objectNode.get("status").asText());
}
else {
this.setStatus(null);
}
if (objectNode.has("last_error")) {
this.setLastError(objectMapper.convertValue(objectNode.get("last_error"), ErrorInfo.class));
}
else {
this.setLastError(null);
}
if (objectNode.has("choices")) {
List<AssistantChoice> choices1 = objectMapper.convertValue(objectNode.get("choices"),
new com.fasterxml.jackson.core.type.TypeReference<List<AssistantChoice>>() {
});
this.setChoices(choices1);
}
else {
this.setChoices(null);
}
if (objectNode.has("metadata")) {
this.setMetadata(objectMapper.convertValue(objectNode.get("metadata"), Map.class));
}
else {
this.setMetadata(null);
}
if (objectNode.has("usage")) {
this.setUsage(objectMapper.convertValue(objectNode.get("usage"), CompletionUsage.class));
}
else {
this.setUsage(null);
}
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode field = objectNode.get(fieldName);
this.set(fieldName, field);
}
}
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
this.put("id", id);
}
public String getConversationId() {
return conversationId;
}
public void setConversationId(String conversationId) {
this.conversationId = conversationId;
this.put("conversation_id", conversationId);
}
public String getAssistantId() {
return assistantId;
}
public void setAssistantId(String assistantId) {
this.assistantId = assistantId;
this.put("assistant_id", assistantId);
}
public int getCreated() {
return created;
}
public void setCreated(int created) {
this.created = created;
this.put("created", created);
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
this.put("status", status);
}
public ErrorInfo getLastError() {
return lastError;
}
public void setLastError(ErrorInfo lastError) {
this.lastError = lastError;
this.putPOJO("last_error", lastError);
}
public List<AssistantChoice> getChoices() {
return choices;
}
public void setChoices(List<AssistantChoice> choices) {
this.choices = choices;
ArrayNode jsonNodes = this.putArray("choices");
if (choices == null) {
jsonNodes.removeAll();
}
else {
for (AssistantChoice choice : choices) {
jsonNodes.add(choice);
}
}
}
public Map<String, Object> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
this.putPOJO("metadata", metadata);
}
public CompletionUsage getUsage() {
return usage;
}
public void setUsage(CompletionUsage usage) {
this.usage = usage;
this.putPOJO("usage", usage);
}
}

View file

@ -0,0 +1,23 @@
package ai.z.openapi.service.assistant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* Extra parameters for assistant configuration. This class contains additional optional
* parameters that can be used to customize assistant behavior and functionality.
*/
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AssistantExtraParameters {
/**
* Translation agent parameters
*/
private TranslateParameters translate;
}

View file

@ -0,0 +1,71 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class represents the parameters for an assistant, including optional fields.
*/
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AssistantParameters extends CommonRequest implements ClientRequest<AssistantParameters> {
/**
* The ID of the assistant.
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* The conversation ID. If not provided, a new conversation is created.
*/
@JsonProperty("conversation_id")
private String conversationId;
/**
* The name of the model, default is 'GLM-4-Assistant'.
*/
@JsonProperty("model")
private String model;
/**
* Whether to support streaming SSE, should be set to True.
*/
@JsonProperty("stream")
private boolean stream;
/**
* The list of conversation messages.
*/
@JsonProperty("messages")
private List<ConversationMessage> messages;
/**
* The list of file attachments for the conversation, optional.
*/
@JsonProperty("attachments")
private List<AssistantAttachments> attachments;
/**
* Metadata or additional fields, optional.
*/
@JsonProperty("metadata")
private Map<String, Object> metadata;
@JsonProperty("extra_parameters")
private AssistantExtraParameters extraParameters;
}

View file

@ -0,0 +1,43 @@
package ai.z.openapi.service.assistant;
import ai.z.openapi.service.assistant.AssistantParameters;
import ai.z.openapi.service.assistant.AssistantApiResponse;
import ai.z.openapi.service.assistant.conversation.ConversationParameters;
import ai.z.openapi.service.assistant.conversation.ConversationUsageListResponse;
import ai.z.openapi.service.assistant.query_support.AssistantSupportResponse;
import ai.z.openapi.service.assistant.query_support.QuerySupportParams;
/**
* Assistant service interface
*/
public interface AssistantService {
/**
* Creates a streaming assistant completion.
* @param request the assistant completion request
* @return AssistantApiResponse containing the completion result
*/
AssistantApiResponse assistantCompletionStream(AssistantParameters request);
/**
* Creates a non-streaming assistant completion.
* @param request the assistant completion request
* @return AssistantApiResponse containing the completion result
*/
AssistantApiResponse assistantCompletion(AssistantParameters request);
/**
* Queries assistant support status.
* @param request the query support request
* @return AssistantSupportResponse containing the support information
*/
AssistantSupportResponse querySupport(QuerySupportParams request);
/**
* Queries conversation usage information.
* @param request the conversation parameters
* @return ConversationUsageListResponse containing the usage information
*/
ConversationUsageListResponse queryConversationUsage(ConversationParameters request);
}

View file

@ -0,0 +1,53 @@
package ai.z.openapi.service.assistant;
import ai.z.openapi.ZaiClient;
import ai.z.openapi.api.assistant.AssistantApi;
import ai.z.openapi.service.assistant.conversation.ConversationParameters;
import ai.z.openapi.service.assistant.conversation.ConversationUsageListResponse;
import ai.z.openapi.service.assistant.conversation.ConversationUsageListStatus;
import ai.z.openapi.service.assistant.query_support.AssistantSupportResponse;
import ai.z.openapi.service.assistant.query_support.AssistantSupportStatus;
import ai.z.openapi.service.assistant.query_support.QuerySupportParams;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
import okhttp3.ResponseBody;
/**
* Implementation of AssistantService
*/
public class AssistantServiceImpl implements AssistantService {
private final ZaiClient zAiClient;
private final AssistantApi assistantApi;
public AssistantServiceImpl(ZaiClient zAiClient) {
this.zAiClient = zAiClient;
this.assistantApi = zAiClient.retrofit().create(AssistantApi.class);
}
@Override
public AssistantApiResponse assistantCompletionStream(AssistantParameters request) {
FlowableRequestSupplier<AssistantParameters, retrofit2.Call<ResponseBody>> supplier = assistantApi::assistantCompletionStream;
return zAiClient.streamRequest(request, supplier, AssistantApiResponse.class, AssistantCompletion.class);
}
@Override
public AssistantApiResponse assistantCompletion(AssistantParameters request) {
RequestSupplier<AssistantParameters, AssistantCompletion> supplier = assistantApi::assistantCompletion;
return zAiClient.executeRequest(request, supplier, AssistantApiResponse.class);
}
@Override
public AssistantSupportResponse querySupport(QuerySupportParams request) {
RequestSupplier<QuerySupportParams, AssistantSupportStatus> supplier = assistantApi::querySupport;
return zAiClient.executeRequest(request, supplier, AssistantSupportResponse.class);
}
@Override
public ConversationUsageListResponse queryConversationUsage(ConversationParameters request) {
RequestSupplier<ConversationParameters, ConversationUsageListStatus> supplier = assistantApi::queryConversationUsage;
return zAiClient.executeRequest(request, supplier, ConversationUsageListResponse.class);
}
}

View file

@ -0,0 +1,101 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.deserialize.assistant.CompletionUsageDeserializer;
import java.util.Iterator;
/**
* This class represents the usage statistics for a completion.
*/
@JsonDeserialize(using = CompletionUsageDeserializer.class)
public class CompletionUsage extends ObjectNode {
/**
* Number of tokens in the input (prompt).
*/
@JsonProperty("prompt_tokens")
private int promptTokens;
/**
* Number of tokens in the output (completion).
*/
@JsonProperty("completion_tokens")
private int completionTokens;
/**
* Total number of tokens used.
*/
@JsonProperty("total_tokens")
private int totalTokens;
public CompletionUsage() {
super(JsonNodeFactory.instance);
}
public CompletionUsage(ObjectNode objectNode) {
super(JsonNodeFactory.instance);
ObjectMapper objectMapper = MessageDeserializeFactory.defaultObjectMapper();
if (objectNode.get("prompt_tokens") != null) {
this.setPromptTokens(objectNode.get("prompt_tokens").asInt());
}
else {
this.setPromptTokens(0);
}
if (objectNode.get("completion_tokens") != null) {
this.setCompletionTokens(objectNode.get("completion_tokens").asInt());
}
else {
this.setCompletionTokens(0);
}
if (objectNode.get("total_tokens") != null) {
this.setTotalTokens(objectNode.get("total_tokens").asInt());
}
else {
this.setTotalTokens(0);
}
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode field = objectNode.get(fieldName);
this.set(fieldName, field);
}
}
// Getters and Setters
public int getPromptTokens() {
return promptTokens;
}
public void setPromptTokens(int promptTokens) {
this.promptTokens = promptTokens;
this.put("prompt_tokens", promptTokens);
}
public int getCompletionTokens() {
return completionTokens;
}
public void setCompletionTokens(int completionTokens) {
this.completionTokens = completionTokens;
this.put("completion_tokens", completionTokens);
}
public int getTotalTokens() {
return totalTokens;
}
public void setTotalTokens(int totalTokens) {
this.totalTokens = totalTokens;
this.put("total_tokens", totalTokens);
}
}

View file

@ -0,0 +1,34 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.List;
/**
* This class represents a conversation message body.
*/
@EqualsAndHashCode(callSuper = false)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ConversationMessage {
/**
* The role of the user input, e.g., 'user'.
*/
@JsonProperty("role")
private String role;
/**
* The content of the conversation message.
*/
@JsonProperty("content")
private List<MessageTextContent> content;
}

View file

@ -0,0 +1,80 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.deserialize.assistant.ErrorInfoDeserializer;
import java.util.Iterator;
/**
* This class represents error information.
*/
@JsonDeserialize(using = ErrorInfoDeserializer.class)
public class ErrorInfo extends ObjectNode {
/**
* Error code.
*/
@JsonProperty("code")
private String code;
/**
* Error message.
*/
@JsonProperty("message")
private String message;
public ErrorInfo() {
super(JsonNodeFactory.instance);
}
public ErrorInfo(ObjectNode objectNode) {
super(JsonNodeFactory.instance);
ObjectMapper objectMapper = MessageDeserializeFactory.defaultObjectMapper();
if (objectNode.get("code") != null) {
this.setCode(objectNode.get("code").asText());
}
else {
this.setCode(null);
}
if (objectNode.get("message") != null) {
this.setMessage(objectNode.get("message").asText());
}
else {
this.setMessage(null);
}
Iterator<String> fieldNames = objectNode.fieldNames();
while (fieldNames.hasNext()) {
String fieldName = fieldNames.next();
JsonNode field = objectNode.get(fieldName);
this.set(fieldName, field);
}
}
// Getters and Setters
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
this.put("code", code);
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
this.put("message", message);
}
}

View file

@ -0,0 +1,33 @@
package ai.z.openapi.service.assistant;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* This class represents the text content of a message. Currently supports only type =
* text.
*/
@EqualsAndHashCode(callSuper = false)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MessageTextContent {
/**
* The type of the message content, currently only "text" is supported.
*/
@JsonProperty("type")
private String type;
/**
* The text content of the message.
*/
@JsonProperty("text")
private String text;
}

View file

@ -0,0 +1,28 @@
package ai.z.openapi.service.assistant;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* Parameters for translation operations. This class contains the source and target
* language settings for translation requests.
*/
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class TranslateParameters {
/**
* Source language for translation.
*/
private String from;
/**
* Target language for translation.
*/
private String to;
}

View file

@ -0,0 +1,40 @@
package ai.z.openapi.service.assistant.conversation;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* This class represents the parameters for a conversation, including pagination.
*/
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ConversationParameters extends CommonRequest implements ClientRequest<ConversationParameters> {
/**
* The Assistant ID.
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* The current page number for pagination.
*/
@JsonProperty("page")
private int page;
/**
* The number of items per page for pagination.
*/
@JsonProperty("page_size")
private int pageSize;
}

View file

@ -0,0 +1,44 @@
package ai.z.openapi.service.assistant.conversation;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* This class represents the usage data for a specific conversation.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ConversationUsage {
/**
* The conversation ID.
*/
@JsonProperty("id")
private String id;
/**
* The Assistant ID.
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* The creation time of the conversation.
*/
@JsonProperty("create_time")
private String createTime;
/**
* The last update time of the conversation.
*/
@JsonProperty("update_time")
private String updateTime;
/**
* The usage statistics for the conversation.
*/
@JsonProperty("usage")
private Usage usage;
}

View file

@ -0,0 +1,34 @@
package ai.z.openapi.service.assistant.conversation;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* This class represents a list of conversation usage data.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ConversationUsageList {
/**
* The Assistant ID.
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* Whether there are more pages of results.
*/
@JsonProperty("has_more")
private boolean hasMore;
/**
* The list of conversation usage data.
*/
@JsonProperty("conversation_list")
private List<ConversationUsage> conversationList;
}

View file

@ -0,0 +1,47 @@
package ai.z.openapi.service.assistant.conversation;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.Data;
/**
* Response for conversation usage list API calls. This class contains the response data
* for retrieving conversation usage statistics.
*/
@Data
public class ConversationUsageListResponse implements ClientResponse<ConversationUsageListStatus> {
/**
* Response status code.
*/
private int code;
/**
* Response message.
*/
private String msg;
/**
* Indicates if the request was successful.
*/
private boolean success;
/**
* The conversation usage list data.
*/
private ConversationUsageListStatus data;
/**
* Error information if the request failed.
*/
private ChatError error;
public ConversationUsageListResponse() {
}
public ConversationUsageListResponse(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

View file

@ -0,0 +1,32 @@
package ai.z.openapi.service.assistant.conversation;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* This class represents the response containing a list of conversation usage data.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ConversationUsageListStatus {
/**
* The response code.
*/
@JsonProperty("code")
private int code;
/**
* The response message.
*/
@JsonProperty("msg")
private String msg;
/**
* The data containing the conversation usage list.
*/
@JsonProperty("data")
private ConversationUsageList data;
}

View file

@ -0,0 +1,32 @@
package ai.z.openapi.service.assistant.conversation;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* This class represents the usage statistics for a conversation.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Usage {
/**
* The number of tokens in the user's input.
*/
@JsonProperty("prompt_tokens")
private int promptTokens;
/**
* The number of tokens in the model's input.
*/
@JsonProperty("completion_tokens")
private int completionTokens;
/**
* The total number of tokens.
*/
@JsonProperty("total_tokens")
private int totalTokens;
}

View file

@ -0,0 +1,13 @@
package ai.z.openapi.service.assistant.message;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import ai.z.openapi.service.deserialize.JsonTypeMapping;
import ai.z.openapi.service.deserialize.assistant.message.MessageContentDeserializer;
@JsonTypeMapping({ ToolsDeltaBlock.class, TextContentBlock.class })
@JsonDeserialize(using = MessageContentDeserializer.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class MessageContent {
}

View file

@ -0,0 +1,44 @@
package ai.z.openapi.service.assistant.message;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block of text content in a conversation.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("content")
public class TextContentBlock extends MessageContent {
/**
* The content of the text block.
*/
@JsonProperty("content")
private String content;
/**
* The role of the speaker, default is "assistant".
*/
@JsonProperty("role")
private String role = "assistant";
// Getters and Setters
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}

View file

@ -0,0 +1,47 @@
package ai.z.openapi.service.assistant.message;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.assistant.message.tools.ToolsType;
import ai.z.openapi.service.deserialize.JsonTypeField;
import java.util.List;
/**
* This class represents a block of tool call data in a conversation.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("tool_calls")
public class ToolsDeltaBlock extends MessageContent {
/**
* A list of tool call types.
*/
@JsonProperty("tool_calls")
private List<ToolsType> toolCalls;
/**
* The role of the speaker, default is "tool".
*/
@JsonProperty("role")
private String role = "tool";
// Getters and Setters
public List<ToolsType> getToolCalls() {
return toolCalls;
}
public void setToolCalls(List<ToolsType> toolCalls) {
this.toolCalls = toolCalls;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}

View file

@ -0,0 +1,19 @@
package ai.z.openapi.service.assistant.message.tools;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import ai.z.openapi.service.assistant.message.tools.code_interpreter.CodeInterpreterToolBlock;
import ai.z.openapi.service.assistant.message.tools.drawing_tool.DrawingToolBlock;
import ai.z.openapi.service.assistant.message.tools.function.FunctionToolBlock;
import ai.z.openapi.service.assistant.message.tools.retrieval.RetrievalToolBlock;
import ai.z.openapi.service.assistant.message.tools.web_browser.WebBrowserToolBlock;
import ai.z.openapi.service.deserialize.JsonTypeMapping;
import ai.z.openapi.service.deserialize.assistant.message.tools.ToolsTypeDeserializer;
@JsonTypeMapping({ WebBrowserToolBlock.class, RetrievalToolBlock.class, FunctionToolBlock.class, DrawingToolBlock.class,
CodeInterpreterToolBlock.class, })
@JsonDeserialize(using = ToolsTypeDeserializer.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class ToolsType {
}

View file

@ -0,0 +1,41 @@
package ai.z.openapi.service.assistant.message.tools.code_interpreter;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* This class represents a code interpreter that executes code and returns the results.
*/
public class CodeInterpreter {
/**
* The generated code snippet that is input to the code sandbox.
*/
@JsonProperty("input")
private String input;
/**
* The output results after the code execution.
*/
@JsonProperty("outputs")
private List<CodeInterpreterToolOutput> outputs;
// Getters and Setters
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public List<CodeInterpreterToolOutput> getOutputs() {
return outputs;
}
public void setOutputs(List<CodeInterpreterToolOutput> outputs) {
this.outputs = outputs;
}
}

View file

@ -0,0 +1,45 @@
package ai.z.openapi.service.assistant.message.tools.code_interpreter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.assistant.message.tools.ToolsType;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block of code tool data.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("code_interpreter")
public class CodeInterpreterToolBlock extends ToolsType {
/**
* The code interpreter object.
*/
@JsonProperty("code_interpreter")
private CodeInterpreter codeInterpreter;
/**
* The type of tool being called, always "code_interpreter".
*/
@JsonProperty("type")
private String type = "code_interpreter";
// Getters and Setters
public CodeInterpreter getCodeInterpreter() {
return codeInterpreter;
}
public void setCodeInterpreter(CodeInterpreter codeInterpreter) {
this.codeInterpreter = codeInterpreter;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,54 @@
package ai.z.openapi.service.assistant.message.tools.code_interpreter;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents the output result of a code tool.
*/
public class CodeInterpreterToolOutput {
/**
* The type of output, currently only "logs".
*/
@JsonProperty("type")
private String type;
/**
* The log results from the code execution.
*/
@JsonProperty("logs")
private String logs;
/**
* Error message if any occurred during code execution.
*/
@JsonProperty("error_msg")
private String errorMsg;
// Getters and Setters
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getLogs() {
return logs;
}
public void setLogs(String logs) {
this.logs = logs;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View file

@ -0,0 +1,41 @@
package ai.z.openapi.service.assistant.message.tools.drawing_tool;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* The input string that was used to generate the drawing.
*/
public class DrawingTool {
/**
* The input string that was used to generate the drawing.
*/
@JsonProperty("input")
private String input;
/**
* A list of outputs generated by the drawing tool.
*/
@JsonProperty("outputs")
private List<DrawingToolOutput> outputs;
// Getters and Setters
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public List<DrawingToolOutput> getOutputs() {
return outputs;
}
public void setOutputs(List<DrawingToolOutput> outputs) {
this.outputs = outputs;
}
}

View file

@ -0,0 +1,45 @@
package ai.z.openapi.service.assistant.message.tools.drawing_tool;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.assistant.message.tools.ToolsType;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block of drawing tool data.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("drawing_tool")
public class DrawingToolBlock extends ToolsType {
/**
* The drawing tool object that contains input and outputs.
*/
@JsonProperty("drawing_tool")
private DrawingTool drawingTool;
/**
* The type of tool being called, always "drawing_tool".
*/
@JsonProperty("type")
private String type = "drawing_tool";
// Getters and Setters
public DrawingTool getDrawingTool() {
return drawingTool;
}
public void setDrawingTool(DrawingTool drawingTool) {
this.drawingTool = drawingTool;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,26 @@
package ai.z.openapi.service.assistant.message.tools.drawing_tool;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents the output of a drawing tool, containing the generated image.
*/
public class DrawingToolOutput {
/**
* The generated image in a string format.
*/
@JsonProperty("image")
private String image;
// Getters and Setters
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
}

View file

@ -0,0 +1,58 @@
package ai.z.openapi.service.assistant.message.tools.function;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.List;
/**
* This class represents a function tool with a name, arguments, and outputs.
*/
public class FunctionTool {
/**
* The name of the function tool.
*/
@JsonProperty("name")
private String name;
/**
* The arguments for the function tool, which can be a string or a dictionary.
*/
@JsonProperty("arguments")
private JsonNode arguments; // Union type in Java can be represented by Object, and
// deserialization handles it accordingly
/**
* A list of outputs generated by the function tool.
*/
@JsonProperty("outputs")
private List<FunctionToolOutput> outputs;
// Getters and Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public JsonNode getArguments() {
return arguments;
}
public void setArguments(JsonNode arguments) {
this.arguments = arguments;
}
public List<FunctionToolOutput> getOutputs() {
return outputs;
}
public void setOutputs(List<FunctionToolOutput> outputs) {
this.outputs = outputs;
}
}

View file

@ -0,0 +1,45 @@
package ai.z.openapi.service.assistant.message.tools.function;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.assistant.message.tools.ToolsType;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block of function tool data.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("function")
public class FunctionToolBlock extends ToolsType {
/**
* The function tool object that contains the name, arguments, and outputs.
*/
@JsonProperty("function")
private FunctionTool function;
/**
* The type of tool being called, always "function".
*/
@JsonProperty("type")
private String type = "function";
// Getters and Setters
public FunctionTool getFunction() {
return function;
}
public void setFunction(FunctionTool function) {
this.function = function;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,24 @@
package ai.z.openapi.service.assistant.message.tools.function;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents the output of a function tool, containing the generated content.
*/
public class FunctionToolOutput {
/**
* The generated content as a string.
*/
@JsonProperty("content")
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

View file

@ -0,0 +1,26 @@
package ai.z.openapi.service.assistant.message.tools.retrieval;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* This class represents the outputs of a retrieval tool.
*/
public class RetrievalTool {
/**
* A list of text snippets and their respective document names retrieved from the
* knowledge base.
*/
@JsonProperty("outputs")
private List<RetrievalToolOutput> outputs;
public List<RetrievalToolOutput> getOutputs() {
return outputs;
}
public void setOutputs(List<RetrievalToolOutput> outputs) {
this.outputs = outputs;
}
}

View file

@ -0,0 +1,44 @@
package ai.z.openapi.service.assistant.message.tools.retrieval;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block for invoking the retrieval tool.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("retrieval")
public class RetrievalToolBlock {
/**
* An instance of the RetrievalTool class containing the retrieval outputs.
*/
@JsonProperty("retrieval")
private RetrievalTool retrieval;
/**
* The type of tool being used, always set to "retrieval".
*/
@JsonProperty("type")
private String type = "retrieval";
// Getters and Setters
public RetrievalTool getRetrieval() {
return retrieval;
}
public void setRetrieval(RetrievalTool retrieval) {
this.retrieval = retrieval;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,41 @@
package ai.z.openapi.service.assistant.message.tools.retrieval;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents the output of a retrieval tool.
*/
public class RetrievalToolOutput {
/**
* The text snippet retrieved from the knowledge base.
*/
@JsonProperty("text")
private String text;
/**
* The name of the document from which the text snippet was retrieved, returned only
* in intelligent configuration.
*/
@JsonProperty("document")
private String document;
// Getters and Setters
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getDocument() {
return document;
}
public void setDocument(String document) {
this.document = document;
}
}

View file

@ -0,0 +1,41 @@
package ai.z.openapi.service.assistant.message.tools.web_browser;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
/**
* This class represents the input and outputs of a web browser search.
*/
public class WebBrowser {
/**
* The input query for the web browser search.
*/
@JsonProperty("input")
private String input;
/**
* A list of search results returned by the web browser.
*/
@JsonProperty("outputs")
private List<WebBrowserOutput> outputs;
// Getters and Setters
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public List<WebBrowserOutput> getOutputs() {
return outputs;
}
public void setOutputs(List<WebBrowserOutput> outputs) {
this.outputs = outputs;
}
}

View file

@ -0,0 +1,68 @@
package ai.z.openapi.service.assistant.message.tools.web_browser;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This class represents the output of a web browser search result.
*/
public class WebBrowserOutput {
/**
* The title of the search result.
*/
@JsonProperty("title")
private String title;
/**
* The URL link to the search result's webpage.
*/
@JsonProperty("link")
private String link;
/**
* The textual content extracted from the search result.
*/
@JsonProperty("content")
private String content;
/**
* Any error message encountered during the search or retrieval process.
*/
@JsonProperty("error_msg")
private String errorMsg;
// Getters and Setters
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

View file

@ -0,0 +1,45 @@
package ai.z.openapi.service.assistant.message.tools.web_browser;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.service.assistant.message.tools.ToolsType;
import ai.z.openapi.service.deserialize.JsonTypeField;
/**
* This class represents a block for invoking the web browser tool.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeField("web_browser")
public class WebBrowserToolBlock extends ToolsType {
/**
* An instance of the WebBrowser class containing the search input and outputs.
*/
@JsonProperty("web_browser")
private WebBrowser webBrowser;
/**
* The type of tool being used, always set to "web_browser".
*/
@JsonProperty("type")
private String type = "web_browser";
// Getters and Setters
public WebBrowser getWebBrowser() {
return webBrowser;
}
public void setWebBrowser(WebBrowser webBrowser) {
this.webBrowser = webBrowser;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,70 @@
package ai.z.openapi.service.assistant.query_support;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* This class represents the details of an assistant.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AssistantSupport {
/**
* The Assistant ID, used for assistant conversations.
*/
@JsonProperty("assistant_id")
private String assistantId;
/**
* The creation time of the assistant.
*/
@JsonProperty("created_at")
private String createdAt;
/**
* The last update time of the assistant.
*/
@JsonProperty("updated_at")
private String updatedAt;
/**
* The name of the assistant.
*/
@JsonProperty("name")
private String name;
/**
* The avatar of the assistant.
*/
@JsonProperty("avatar")
private String avatar;
/**
* The description of the assistant.
*/
@JsonProperty("description")
private String description;
/**
* The status of the assistant, currently only "publish".
*/
@JsonProperty("status")
private String status;
/**
* The list of tools supported by the assistant.
*/
@JsonProperty("tools")
private List<String> tools;
/**
* The list of recommended prompts to start the assistant.
*/
@JsonProperty("starter_prompts")
private List<String> starterPrompts;
}

View file

@ -0,0 +1,47 @@
package ai.z.openapi.service.assistant.query_support;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.Data;
/**
* Response for assistant support query API calls. This class contains the response data
* for checking assistant support status.
*/
@Data
public class AssistantSupportResponse implements ClientResponse<AssistantSupportStatus> {
/**
* Response status code.
*/
private int code;
/**
* Response message.
*/
private String msg;
/**
* Indicates if the request was successful.
*/
private boolean success;
/**
* The assistant support status data.
*/
private AssistantSupportStatus data;
/**
* Error information if the request failed.
*/
private ChatError error;
public AssistantSupportResponse() {
}
public AssistantSupportResponse(int code, String msg) {
this.code = code;
this.msg = msg;
}
}

View file

@ -0,0 +1,34 @@
package ai.z.openapi.service.assistant.query_support;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* This class represents the response containing a list of assistant supports.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AssistantSupportStatus {
/**
* The response code.
*/
@JsonProperty("code")
private Integer code;
/**
* The response message.
*/
@JsonProperty("msg")
private String msg;
/**
* The list of assistant supports.
*/
@JsonProperty("data")
private List<AssistantSupport> data;
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.service.assistant.query_support;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.List;
/**
* Parameters for querying assistant support status. This class contains the parameters
* needed to query the support status of specific assistants.
*/
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class QuerySupportParams extends CommonRequest implements ClientRequest<QuerySupportParams> {
/**
* List of assistant IDs to query support status for.
*/
@JsonProperty("assistant_id_list")
private List<String> assistantIdList;
}

View file

@ -0,0 +1,42 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.Data;
import java.io.File;
/**
* Response wrapper for audio customization API calls. This class contains the response
* data for audio customization requests, including the generated audio file and status
* information.
*/
@Data
public class AudioCustomizationApiResponse implements ClientResponse<File> {
/**
* Response status code.
*/
private int code;
/**
* Response message.
*/
private String msg;
/**
* Indicates whether the request was successful.
*/
private boolean success;
/**
* The generated audio file.
*/
private File data;
/**
* Error information if the request failed.
*/
private ChatError error;
}

View file

@ -0,0 +1,63 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import ai.z.openapi.service.model.SensitiveWordCheckRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* Request parameters for audio customization API calls. This class contains all the
* necessary parameters for generating customized audio, including input text, model
* selection, voice cloning data, and response format options.
*/
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AudioCustomizationRequest extends CommonRequest implements ClientRequest<AudioCustomizationRequest> {
/**
* Text to generate audio from
*/
private String input;
/**
* Model code to call
*/
private String model;
/**
* Text description of the original audio to clone
*/
@JsonProperty("voice_text")
private String voiceText;
/**
* Original audio file to clone
*/
@JsonProperty("voice_data")
private File voiceData;
/**
* Audio response format
*/
@JsonProperty("response_format")
private String responseFormat;
/**
* Sensitive word detection control
*/
@JsonProperty("sensitive_word_check")
private SensitiveWordCheckRequest sensitiveWordCheck;
}

View file

@ -0,0 +1,31 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.service.model.ChatCompletionResponse;
/**
* Audio service interface
*/
public interface AudioService {
/**
* Creates speech from text using text-to-speech.
* @param request the speech generation request
* @return AudioSpeechApiResponse containing the generated speech
*/
AudioSpeechApiResponse createSpeech(AudioSpeechRequest request);
/**
* Creates customized speech with specific voice characteristics.
* @param request the speech customization request
* @return AudioCustomizationApiResponse containing the customized speech result
*/
AudioCustomizationApiResponse createCustomSpeech(AudioCustomizationRequest request);
/**
* Creates audio transcriptions from audio files.
* @param request the transcription request
* @return ChatCompletionResponse containing the transcription result
*/
ChatCompletionResponse createTranscription(AudioTranscriptionsRequest request);
}

View file

@ -0,0 +1,213 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.ZaiClient;
import ai.z.openapi.api.audio.AudioApi;
import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.service.model.ChatCompletionResponse;
import ai.z.openapi.service.model.ModelData;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.reactivex.Single;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.apache.tika.Tika;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Audio service implementation
*/
@Slf4j
public class AudioServiceImpl implements AudioService {
protected static final ObjectMapper mapper = MessageDeserializeFactory.defaultObjectMapper();
private final ZaiClient zAiClient;
private final AudioApi audioApi;
public AudioServiceImpl(ZaiClient zAiClient) {
this.zAiClient = zAiClient;
this.audioApi = zAiClient.retrofit().create(AudioApi.class);
}
@Override
public AudioSpeechApiResponse createSpeech(AudioSpeechRequest request) {
RequestSupplier<AudioSpeechRequest, java.io.File> supplier = (params) -> {
try {
Single<ResponseBody> responseBody = audioApi.audioSpeech(params);
Path tempDirectory = Files.createTempFile("audio_speech" + UUID.randomUUID(), ".wav");
java.io.File file = tempDirectory.toFile();
writeResponseBodyToFile(responseBody.blockingGet(), file);
return Single.just(file);
}
catch (IOException e) {
throw new RuntimeException(e);
}
};
return this.zAiClient.executeRequest(request, supplier, AudioSpeechApiResponse.class);
}
@Override
public AudioCustomizationApiResponse createCustomSpeech(AudioCustomizationRequest request) {
RequestSupplier<AudioCustomizationRequest, java.io.File> supplier = (params) -> {
try {
java.io.File voiceFile = params.getVoiceData();
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), voiceFile);
MultipartBody.Part voiceData = MultipartBody.Part.createFormData("voice_data", voiceFile.getName(),
requestFile);
Map<String, RequestBody> requestMap = new HashMap<>();
if (params.getInput() != null) {
requestMap.put("input", RequestBody.create(MediaType.parse("text/plain"), params.getInput()));
}
if (params.getModel() != null) {
requestMap.put("model", RequestBody.create(MediaType.parse("text/plain"), params.getModel()));
}
if (params.getVoiceText() != null) {
requestMap.put("voice_text",
RequestBody.create(MediaType.parse("text/plain"), params.getVoiceText()));
}
if (params.getResponseFormat() != null) {
requestMap.put("response_format",
RequestBody.create(MediaType.parse("text/plain"), params.getResponseFormat()));
}
if (params.getSensitiveWordCheck() != null) {
try {
String sensitiveWordCheckJson = mapper.writeValueAsString(params.getSensitiveWordCheck());
requestMap.put("sensitive_word_check",
RequestBody.create(MediaType.parse("application/json"), sensitiveWordCheckJson));
}
catch (Exception e) {
log.error("Error serializing sensitive_word_check: {}", e.getMessage(), e);
}
}
if (params.getRequestId() != null) {
requestMap.put("request_id",
RequestBody.create(MediaType.parse("text/plain"), params.getRequestId()));
}
if (params.getUserId() != null) {
requestMap.put("user_id", RequestBody.create(MediaType.parse("text/plain"), params.getUserId()));
}
Single<ResponseBody> responseBody = audioApi.audioCustomization(requestMap, voiceData);
Path tempDirectory = Files.createTempFile("audio_customization" + UUID.randomUUID(), ".wav");
java.io.File file = tempDirectory.toFile();
writeResponseBodyToFile(responseBody.blockingGet(), file);
return Single.just(file);
}
catch (IOException e) {
log.error("Error create custom speak: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
};
return this.zAiClient.executeRequest(request, supplier, AudioCustomizationApiResponse.class);
}
@Override
public ChatCompletionResponse createTranscription(AudioTranscriptionsRequest request) {
if (request.getStream()) {
return createTranscriptionStream(request);
}
else {
return createTranscriptionSync(request);
}
}
private ChatCompletionResponse createTranscriptionStream(AudioTranscriptionsRequest request) {
FlowableRequestSupplier<AudioTranscriptionsRequest, retrofit2.Call<ResponseBody>> supplier = params -> {
try {
java.io.File file = params.getFile();
Tika tika = new Tika();
String contentType = tika.detect(file);
RequestBody requestFile = RequestBody.create(MediaType.parse(contentType), file);
MultipartBody.Part fileData = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
Map<String, RequestBody> requestMap = new HashMap<>();
if (params.getModel() != null) {
requestMap.put("model", RequestBody.create(MediaType.parse("text/plain"), params.getModel()));
}
if (params.getStream() != null) {
requestMap.put("stream",
RequestBody.create(MediaType.parse("text/plain"), params.getStream().toString()));
}
if (params.getRequestId() != null) {
requestMap.put("request_id",
RequestBody.create(MediaType.parse("text/plain"), params.getRequestId()));
}
if (params.getUserId() != null) {
requestMap.put("user_id", RequestBody.create(MediaType.parse("text/plain"), params.getUserId()));
}
return audioApi.audioTranscriptionsStream(requestMap, fileData);
}
catch (IOException e) {
log.error("Error create transcription: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
};
return this.zAiClient.streamRequest(request, supplier, ChatCompletionResponse.class, ModelData.class);
}
private ChatCompletionResponse createTranscriptionSync(AudioTranscriptionsRequest request) {
RequestSupplier<AudioTranscriptionsRequest, ModelData> supplier = (params) -> {
try {
java.io.File file = params.getFile();
Tika tika = new Tika();
String contentType = tika.detect(file);
RequestBody requestFile = RequestBody.create(MediaType.parse(contentType), file);
MultipartBody.Part fileData = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
Map<String, RequestBody> requestMap = new HashMap<>();
if (params.getModel() != null) {
requestMap.put("model", RequestBody.create(MediaType.parse("text/plain"), params.getModel()));
}
if (params.getStream() != null) {
requestMap.put("stream",
RequestBody.create(MediaType.parse("text/plain"), params.getStream().toString()));
}
if (params.getRequestId() != null) {
requestMap.put("request_id",
RequestBody.create(MediaType.parse("text/plain"), params.getRequestId()));
}
if (params.getUserId() != null) {
requestMap.put("user_id", RequestBody.create(MediaType.parse("text/plain"), params.getUserId()));
}
return audioApi.audioTranscriptions(requestMap, fileData);
}
catch (IOException e) {
log.error("Error create transcription: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
};
return this.zAiClient.executeRequest(request, supplier, ChatCompletionResponse.class);
}
private void writeResponseBodyToFile(ResponseBody body, java.io.File file) {
try (InputStream inputStream = body.byteStream();
OutputStream outputStream = Files.newOutputStream(file.toPath())) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
catch (IOException e) {
log.error("writeResponseBodyToFile error,msg:{}", e.getMessage(), e);
}
}
}

View file

@ -0,0 +1,22 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.service.model.ChatError;
import lombok.Data;
import java.io.File;
@Data
public class AudioSpeechApiResponse implements ClientResponse<File> {
private int code;
private String msg;
private boolean success;
private File data;
private ChatError error;
}

View file

@ -0,0 +1,50 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import ai.z.openapi.service.model.SensitiveWordCheckRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.util.HashMap;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AudioSpeechRequest extends CommonRequest implements ClientRequest<AudioSpeechRequest> {
/**
* Model code to call
*/
private String model;
/**
* Text to generate speech from
*/
private String input;
/**
* Voice tone for speech generation
*/
private String voice;
/**
* Format of the generated speech file
*/
@JsonProperty("response_format")
private String responseFormat;
/**
* Sensitive word detection control
*/
@JsonProperty("sensitive_word_check")
private SensitiveWordCheckRequest sensitiveWordCheck;
}

View file

@ -0,0 +1,51 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientRequest;
import ai.z.openapi.service.CommonRequest;
import ai.z.openapi.service.model.SensitiveWordCheckRequest;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AudioTranscriptionsRequest extends CommonRequest implements ClientRequest<AudioTranscriptionsRequest> {
/**
* Model code to call
*/
private String model;
/**
* Synchronous call: false, SSE call: true
*/
private Boolean stream;
private File file;
/**
* Sampling temperature, controls output randomness, must be positive Range:
* (0.0,1.0], cannot equal 0, default value is 0.95 Higher values make output more
* random and creative; lower values make output more stable or deterministic It's
* recommended to adjust either top_p or temperature parameter based on your use case,
* but not both simultaneously
*/
private Float temperature;
/**
* Sensitive word detection control
*/
@JsonProperty("sensitive_word_check")
private SensitiveWordCheckRequest sensitiveWordCheck;
}

View file

@ -0,0 +1,113 @@
package ai.z.openapi.service.batches;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
@Data
public class Batch {
@JsonProperty("id")
private String id;
@JsonProperty("completion_window")
private String completionWindow;
@JsonProperty("created_at")
private long createdAt;
@JsonProperty("endpoint")
private String endpoint;
@JsonProperty("input_file_id")
private String inputFileId;
@JsonProperty("object")
private String object;
@JsonProperty("status")
private String status;
@JsonProperty("cancelled_at")
private Long cancelledAt;
@JsonProperty("cancelling_at")
private Long cancellingAt;
@JsonProperty("completed_at")
private Long completedAt;
@JsonProperty("error_file_id")
private String errorFileId;
@JsonProperty("errors")
private Errors errors;
@JsonProperty("expired_at")
private Long expiredAt;
@JsonProperty("expires_at")
private Long expiresAt;
@JsonProperty("failed_at")
private Long failedAt;
@JsonProperty("finalizing_at")
private Long finalizingAt;
@JsonProperty("in_progress_at")
private Long inProgressAt;
@JsonProperty("metadata")
private Object metadata;
@JsonProperty("output_file_id")
private String outputFileId;
@JsonProperty("request_counts")
private BatchRequestCounts requestCounts;
}
@Data
class Errors {
@JsonProperty("data")
private List<BatchError> data;
@JsonProperty("object")
private String object;
}
@Data
class BatchRequestCounts {
@JsonProperty("completed")
private int completed;
@JsonProperty("failed")
private int failed;
@JsonProperty("total")
private int total;
}
@Data
class BatchError {
@JsonProperty("code")
private String code;
@JsonProperty("line")
private Long line;
@JsonProperty("message")
private String message;
@JsonProperty("param")
private String param;
}

View file

@ -0,0 +1,56 @@
package ai.z.openapi.service.batches;
import com.fasterxml.jackson.annotation.JsonProperty;
import ai.z.openapi.core.model.ClientRequest;
import lombok.*;
import lombok.experimental.SuperBuilder;
import java.util.Map;
/**
* Parameters for creating batch processing requests. This class contains all the
* necessary parameters to create a batch job, including completion window, endpoint,
* input file, and metadata.
*/
@EqualsAndHashCode(callSuper = false)
@SuperBuilder
@Data
public class BatchCreateParams implements ClientRequest<BatchCreateParams> {
/**
* The time frame within which the batch should be processed.
*/
@JsonProperty("completion_window")
private String completionWindow;
/**
* The API endpoint to be used for batch processing.
*/
@JsonProperty("endpoint")
private String endpoint;
/**
* The ID of the uploaded file containing batch requests. Must be the ID of the
* uploaded file.
*/
@JsonProperty("input_file_id")
private String inputFileId;
/**
* Optional custom metadata for the batch job.
*/
@JsonProperty("metadata")
private Map<String, String> metadata;
public BatchCreateParams() {
}
public BatchCreateParams(String completionWindow, String endpoint, String inputFileId,
Map<String, String> metadata) {
this.completionWindow = completionWindow;
this.endpoint = endpoint;
this.inputFileId = inputFileId;
this.metadata = metadata;
}
}

View file

@ -0,0 +1,16 @@
package ai.z.openapi.service.batches;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.util.List;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class BatchPage {
private String object;
private List<Batch> data;
}

Some files were not shown because too many files have changed in this diff Show more