diff --git a/.gitignore b/.gitignore
index 792704b..fe42722 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**
!**/src/test/**
+.DS_Store
### STS ###
.apt_generated
diff --git a/core/pom.xml b/core/pom.xml
index cb6d4e3..45159ca 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -49,7 +49,7 @@
UTF-8
2.0.11
3.14.9
- 2.11.3
+ 2.18.3
2.9.0
3.1.8
4.2.2
diff --git a/core/src/main/java/ai/z/openapi/AbstractAiClient.java b/core/src/main/java/ai/z/openapi/AbstractAiClient.java
index 59dfce9..ae240d4 100644
--- a/core/src/main/java/ai/z/openapi/AbstractAiClient.java
+++ b/core/src/main/java/ai/z/openapi/AbstractAiClient.java
@@ -42,6 +42,7 @@ import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -477,6 +478,19 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
return self();
}
+ /**
+ * Config the custom headers
+ * @param customHeaders custom headers
+ * @return this Builder instance for method chaining
+ */
+ public B customHeaders(Map customHeaders) {
+ if (customHeaders == null || customHeaders.isEmpty()) {
+ throw new IllegalArgumentException("Custom headers cannot be null or empty");
+ }
+ config.setCustomHeaders(customHeaders);
+ return self();
+ }
+
/**
* Disables token caching, forcing the client to use API keys for direct requests.
* This is the default behavior.
diff --git a/core/src/main/java/ai/z/openapi/api/chat/ChatApi.java b/core/src/main/java/ai/z/openapi/api/chat/ChatApi.java
index ab80b4b..0359bea 100644
--- a/core/src/main/java/ai/z/openapi/api/chat/ChatApi.java
+++ b/core/src/main/java/ai/z/openapi/api/chat/ChatApi.java
@@ -7,10 +7,13 @@ import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
+import retrofit2.http.HeaderMap;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Streaming;
+import java.util.Map;
+
/**
* Chat Completions API for advanced GLM-4 series models Provides synchronous,
* asynchronous, and streaming chat completion capabilities Supports complex reasoning,
@@ -36,6 +39,23 @@ public interface ChatApi {
@POST("chat/completions")
Call createChatCompletionStream(@Body ChatCompletionCreateParams request);
+ /**
+ * Create a streaming chat completion with custom headers support Returns response
+ * content incrementally through Server-Sent Events (SSE) for immediate user feedback
+ * Optimized for interactive applications requiring low latency and progressive
+ * content delivery Supports all GLM-4 models with configurable streaming parameters
+ * and custom HTTP headers
+ * @param request Chat completion parameters including model selection, messages, and
+ * streaming settings
+ * @param headers Custom HTTP headers to be added to the request
+ * @return Streaming response body with incremental content, usage statistics, and
+ * completion indicators
+ */
+ @Streaming
+ @POST("chat/completions")
+ Call createChatCompletionStream(@Body ChatCompletionCreateParams request,
+ @HeaderMap Map headers);
+
/**
* Create an asynchronous chat completion for long-running tasks Submits the request
* and returns immediately with a task ID for later result retrieval Ideal for complex
@@ -64,6 +84,22 @@ public interface ChatApi {
@POST("chat/completions")
Single createChatCompletion(@Body ChatCompletionCreateParams request);
+ /**
+ * Create a synchronous chat completion with custom headers support Waits for the
+ * GLM-4 model to complete execution and returns the final result with custom HTTP
+ * headers Supports complex reasoning, tool calling, function execution, and
+ * multi-modal understanding Features advanced capabilities like web search
+ * integration, code interpretation, and image analysis
+ * @param request Chat completion parameters including model selection, conversation
+ * messages, generation settings, tools configuration, and response format
+ * @param headers Custom HTTP headers to be added to the request
+ * @return Complete chat completion response with generated content, usage statistics,
+ * tool call results, and reasoning traces
+ */
+ @POST("chat/completions")
+ Single createChatCompletion(@Body ChatCompletionCreateParams request,
+ @HeaderMap Map headers);
+
/**
* Query the result of an asynchronous chat completion task Retrieves the completion
* result or current status using the task ID from async request Provides detailed
diff --git a/core/src/main/java/ai/z/openapi/core/Constants.java b/core/src/main/java/ai/z/openapi/core/Constants.java
index 34aeef6..886a103 100644
--- a/core/src/main/java/ai/z/openapi/core/Constants.java
+++ b/core/src/main/java/ai/z/openapi/core/Constants.java
@@ -260,6 +260,11 @@ public final class Constants {
*/
public static final String ModelCogVideoX2 = "cogvideox-2";
+ /**
+ * CogVideoX-3 model
+ */
+ public static final String ModelCogVideoX3 = "cogvideox-3";
+
/**
* Vidu Q1 Text model - High-performance video generation from text input. Supports
* general and anime styles.
diff --git a/core/src/main/java/ai/z/openapi/core/config/ZaiConfig.java b/core/src/main/java/ai/z/openapi/core/config/ZaiConfig.java
index ad6b8f0..b2f7d08 100644
--- a/core/src/main/java/ai/z/openapi/core/config/ZaiConfig.java
+++ b/core/src/main/java/ai/z/openapi/core/config/ZaiConfig.java
@@ -5,6 +5,7 @@ import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import static ai.z.openapi.core.Constants.Z_AI_BASE_URL;
@@ -63,6 +64,11 @@ public class ZaiConfig {
*/
private String apiSecret;
+ /**
+ * Custom Http Request Headers
+ */
+ private Map customHeaders;
+
/**
* JWT token expiration time in milliseconds (default: 30 minutes).
*/
@@ -408,4 +414,12 @@ public class ZaiConfig {
return source_channel;
}
+ /**
+ * Get custom headers
+ * @return
+ */
+ public Map getCustomHeaders() {
+ return customHeaders;
+ }
+
}
diff --git a/core/src/main/java/ai/z/openapi/core/token/AuthenticationInterceptor.java b/core/src/main/java/ai/z/openapi/core/token/HttpRequestInterceptor.java
similarity index 65%
rename from core/src/main/java/ai/z/openapi/core/token/AuthenticationInterceptor.java
rename to core/src/main/java/ai/z/openapi/core/token/HttpRequestInterceptor.java
index 322da34..ba3f550 100644
--- a/core/src/main/java/ai/z/openapi/core/token/AuthenticationInterceptor.java
+++ b/core/src/main/java/ai/z/openapi/core/token/HttpRequestInterceptor.java
@@ -7,16 +7,17 @@ import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
+import java.util.Map;
import java.util.Objects;
/**
* OkHttp Interceptor that adds an authorization token header
*/
-public class AuthenticationInterceptor implements Interceptor {
+public class HttpRequestInterceptor implements Interceptor {
private final ZaiConfig config;
- public AuthenticationInterceptor(ZaiConfig config) {
+ public HttpRequestInterceptor(ZaiConfig config) {
Objects.requireNonNull(config.getApiKey(), "Z.ai token required");
this.config = config;
}
@@ -31,17 +32,22 @@ public class AuthenticationInterceptor implements Interceptor {
TokenManager tokenManager = GlobalTokenManager.getTokenManagerV4();
accessToken = tokenManager.getToken(this.config);
}
- String source_channel = "java-sdk";
+ String source_channel = "z-ai-sdk-java";
if (StringUtils.isNotEmpty(config.getSource_channel())) {
source_channel = config.getSource_channel();
}
- Request request = chain.request()
+ Request.Builder 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);
+ .header("Zai-SDK-Ver", "0.0.2")
+ .header("Accept-Language", "en-US,en");
+ if (Objects.nonNull(config.getCustomHeaders())) {
+ for (Map.Entry entry : config.getCustomHeaders().entrySet()) {
+ request.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+ return chain.proceed(request.build());
}
}
diff --git a/core/src/main/java/ai/z/openapi/service/chat/ChatService.java b/core/src/main/java/ai/z/openapi/service/chat/ChatService.java
index f382290..cf1ed62 100644
--- a/core/src/main/java/ai/z/openapi/service/chat/ChatService.java
+++ b/core/src/main/java/ai/z/openapi/service/chat/ChatService.java
@@ -5,6 +5,8 @@ import ai.z.openapi.service.model.ChatCompletionResponse;
import ai.z.openapi.service.model.AsyncResultRetrieveParams;
import ai.z.openapi.service.model.QueryModelResultResponse;
+import java.util.Map;
+
/**
* Chat completion service interface
*/
@@ -32,4 +34,13 @@ public interface ChatService {
*/
QueryModelResultResponse retrieveAsyncResult(AsyncResultRetrieveParams request);
+ /**
+ * Creates a chat completion with custom headers support. This method allows passing
+ * custom HTTP headers along with the chat completion request.
+ * @param request the chat completion request parameters
+ * @param customHeaders custom HTTP headers to be added to the request
+ * @return ChatCompletionResponse containing the completion result
+ */
+ ChatCompletionResponse createChatCompletion(ChatCompletionCreateParams request, Map customHeaders);
+
}
\ No newline at end of file
diff --git a/core/src/main/java/ai/z/openapi/service/chat/ChatServiceImpl.java b/core/src/main/java/ai/z/openapi/service/chat/ChatServiceImpl.java
index 273c747..6c29a2d 100644
--- a/core/src/main/java/ai/z/openapi/service/chat/ChatServiceImpl.java
+++ b/core/src/main/java/ai/z/openapi/service/chat/ChatServiceImpl.java
@@ -7,11 +7,12 @@ import ai.z.openapi.service.model.ChatCompletionResponse;
import ai.z.openapi.service.model.AsyncResultRetrieveParams;
import ai.z.openapi.service.model.QueryModelResultResponse;
import ai.z.openapi.service.model.ModelData;
+import ai.z.openapi.service.model.ChatRequestWithHeaders;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
-import ai.z.openapi.utils.StringUtils;
import okhttp3.ResponseBody;
+import java.util.Map;
import java.util.Objects;
/**
@@ -64,6 +65,38 @@ public class ChatServiceImpl implements ChatService {
return this.zAiClient.executeRequest(request, supplier, ChatCompletionResponse.class);
}
+ @Override
+ public ChatCompletionResponse createChatCompletion(ChatCompletionCreateParams request,
+ Map customHeaders) {
+ if (Objects.isNull(customHeaders)) {
+ throw new IllegalArgumentException("customHeaders can not be null");
+ }
+ validateParams(request);
+ if (Objects.nonNull(request.getStream()) && request.getStream()) {
+ return streamChatCompletionWithHeaders(request, customHeaders);
+ }
+ else {
+ return syncChatCompletionWithHeaders(request, customHeaders);
+ }
+ }
+
+ private ChatCompletionResponse streamChatCompletionWithHeaders(ChatCompletionCreateParams request,
+ Map customHeaders) {
+ ChatRequestWithHeaders requestWithHeaders = new ChatRequestWithHeaders(request, customHeaders);
+ FlowableRequestSupplier> supplier = (wrapper) -> chatApi
+ .createChatCompletionStream(wrapper.getRequest(), wrapper.getCustomHeaders());
+ return this.zAiClient.streamRequest(requestWithHeaders, supplier, ChatCompletionResponse.class,
+ ModelData.class);
+ }
+
+ private ChatCompletionResponse syncChatCompletionWithHeaders(ChatCompletionCreateParams request,
+ Map customHeaders) {
+ ChatRequestWithHeaders requestWithHeaders = new ChatRequestWithHeaders(request, customHeaders);
+ RequestSupplier supplier = (wrapper) -> chatApi
+ .createChatCompletion(wrapper.getRequest(), wrapper.getCustomHeaders());
+ return this.zAiClient.executeRequest(requestWithHeaders, supplier, ChatCompletionResponse.class);
+ }
+
private void validateParams(ChatCompletionCreateParams request) {
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
diff --git a/core/src/main/java/ai/z/openapi/service/file/UploadFilePurpose.java b/core/src/main/java/ai/z/openapi/service/file/UploadFilePurpose.java
new file mode 100644
index 0000000..c674394
--- /dev/null
+++ b/core/src/main/java/ai/z/openapi/service/file/UploadFilePurpose.java
@@ -0,0 +1,17 @@
+package ai.z.openapi.service.file;
+
+public enum UploadFilePurpose {
+
+ BATCH("batch"), FILE_EXTRACT("file-extract"), CODE_INTERPRETER("code-interpreter"), AGENT("agent");
+
+ private final String value;
+
+ UploadFilePurpose(final String value) {
+ this.value = value;
+ }
+
+ public String value() {
+ return value;
+ }
+
+}
diff --git a/core/src/main/java/ai/z/openapi/service/model/ChatRequestWithHeaders.java b/core/src/main/java/ai/z/openapi/service/model/ChatRequestWithHeaders.java
new file mode 100644
index 0000000..ccb3045
--- /dev/null
+++ b/core/src/main/java/ai/z/openapi/service/model/ChatRequestWithHeaders.java
@@ -0,0 +1,32 @@
+package ai.z.openapi.service.model;
+
+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 java.util.Map;
+
+/**
+ * Chat request wrapper that includes custom headers support. This class wraps the
+ * original ChatCompletionCreateParams and adds custom headers functionality.
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ChatRequestWithHeaders extends CommonRequest implements ClientRequest {
+
+ /**
+ * The original chat completion request parameters
+ */
+ private ChatCompletionCreateParams request;
+
+ /**
+ * Custom headers to be added to the HTTP request
+ */
+ private Map customHeaders;
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/ai/z/openapi/utils/OkHttps.java b/core/src/main/java/ai/z/openapi/utils/OkHttps.java
index 4d48348..37b253a 100644
--- a/core/src/main/java/ai/z/openapi/utils/OkHttps.java
+++ b/core/src/main/java/ai/z/openapi/utils/OkHttps.java
@@ -1,7 +1,7 @@
package ai.z.openapi.utils;
import ai.z.openapi.core.config.ZaiConfig;
-import ai.z.openapi.core.token.AuthenticationInterceptor;
+import ai.z.openapi.core.token.HttpRequestInterceptor;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
@@ -42,7 +42,7 @@ public final class OkHttps {
throw new IllegalArgumentException("Configuration cannot be null");
}
- OkHttpClient.Builder builder = new OkHttpClient.Builder().addInterceptor(new AuthenticationInterceptor(config));
+ OkHttpClient.Builder builder = new OkHttpClient.Builder().addInterceptor(new HttpRequestInterceptor(config));
// Configure timeouts
configureTimeouts(builder, config);
diff --git a/core/src/test/java/ai/z/openapi/service/assistant/AssistantServiceTest.java b/core/src/test/java/ai/z/openapi/service/assistant/AssistantServiceTest.java
index e6eea8b..eaf8f71 100644
--- a/core/src/test/java/ai/z/openapi/service/assistant/AssistantServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/assistant/AssistantServiceTest.java
@@ -61,44 +61,6 @@ public class AssistantServiceTest {
"AssistantService should be an instance of AssistantServiceImpl");
}
- // As of 2025-07-23, no models support synchronous completion
- // @Test
- // @DisplayName("Test Synchronous Assistant Completion - Basic Functionality")
- // @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
- // void testSyncAssistantCompletion() throws JsonProcessingException {
- // // Prepare test data
- // MessageTextContent textContent = MessageTextContent.builder()
- // .text("Hello, please introduce yourself")
- // .type("text")
- // .build();
-
- // ConversationMessage message = ConversationMessage.builder()
- // .role("user")
- // .content(Collections.singletonList(textContent))
- // .build();
-
- // String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
-
- // AssistantParameters request = AssistantParameters.builder()
- // .model(Constants.ModelChatGLM4Assistant)
- // .assistantId(TEST_ASSISTANT_ID)
- // .stream(false)
- // .messages(Collections.singletonList(message))
- // .requestId(requestId)
- // .build();
-
- // // Execute test
- // AssistantApiResponse response = assistantService.assistantCompletion(request);
-
- // // Verify results
- // assertNotNull(response, "Response should not be null");
- // assertTrue(response.isSuccess(), "Response should be successful");
- // assertNotNull(response.getData(), "Response data should not be null");
- // assertNull(response.getError(), "Response error should be null");
- // logger.info("Synchronous assistant completion response: {}",
- // mapper.writeValueAsString(response));
- // }
-
@Test
@DisplayName("Test Stream Assistant Completion")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
@@ -188,13 +150,17 @@ public class AssistantServiceTest {
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testQueryConversationUsage() {
// Prepare test data
- ConversationParameters request = ConversationParameters.builder().assistantId(TEST_ASSISTANT_ID).build();
+ ConversationParameters request = ConversationParameters.builder()
+ .assistantId(TEST_ASSISTANT_ID)
+ .page(1)
+ .pageSize(5)
+ .build();
// Execute test
ConversationUsageListResponse response = assistantService.queryConversationUsage(request);
// Verify results
- assertNotNull(response, "Response should not be null");
+ assertNotNull(response.getData(), "Response should not be null");
logger.info("Query conversation usage response: {}", response);
}
@@ -216,7 +182,7 @@ public class AssistantServiceTest {
.messages(Collections.singletonList(message))
.build();
- AssistantApiResponse response = assistantService.assistantCompletion(request);
+ AssistantApiResponse response = assistantService.assistantCompletionStream(request);
// Should handle error gracefully
assertNotNull(response, "Response should not be null even for invalid assistant ID");
@@ -258,45 +224,29 @@ public class AssistantServiceTest {
AssistantParameters request = AssistantParameters.builder()
.assistantId(TEST_ASSISTANT_ID)
- .stream(false)
+ .stream(true)
.messages(messages)
.requestId(requestId)
.build();
- AssistantApiResponse response = assistantService.assistantCompletion(request);
-
+ AssistantApiResponse response = assistantService.assistantCompletionStream(request);
+ response.getFlowable().doOnNext(accumulator -> {
+ if (accumulator.getChoices() != null && !accumulator.getChoices().isEmpty()) {
+ MessageContent delta = accumulator.getChoices().get(0).getDelta();
+ if (delta != null) {
+ try {
+ logger.info("MessageContent: {}", mapper.writeValueAsString(delta));
+ }
+ catch (JsonProcessingException e) {
+ logger.error("Error processing message content", e);
+ }
+ }
+ }
+ })
+ .doOnComplete(() -> logger.info("Stream response completed, received messages"))
+ .doOnError(throwable -> logger.error("Stream error: {}", throwable.getMessage()))
+ .blockingSubscribe();
assertNotNull(response, "Multi-turn conversation response should not be null");
- logger.info("Multi-turn conversation response: {}", mapper.writeValueAsString(response));
- }
-
- @Test
- @DisplayName("Test Assistant with Translation Parameters")
- @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
- void testAssistantWithTranslation() throws JsonProcessingException {
- // This test is based on the existing
- // TestAssistantClientApiService.testTranslateAssistantCompletion
- MessageTextContent textContent = MessageTextContent.builder().text("Hello there").type("text").build();
-
- ConversationMessage message = ConversationMessage.builder()
- .role("user")
- .content(Collections.singletonList(textContent))
- .build();
-
- String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
-
- AssistantParameters request = AssistantParameters.builder()
- .assistantId("9996ijk789lmn012o345p999") // Translation assistant ID
- .stream(false)
- .messages(Collections.singletonList(message))
- .requestId(requestId)
- .build();
-
- // Execute test
- AssistantApiResponse response = assistantService.assistantCompletion(request);
-
- // Verify results
- assertNotNull(response, "Translation response should not be null");
- logger.info("Translation assistant response: {}", mapper.writeValueAsString(response));
}
}
diff --git a/core/src/test/java/ai/z/openapi/service/audio/AudioServiceTest.java b/core/src/test/java/ai/z/openapi/service/audio/AudioServiceTest.java
index f527c67..d70d574 100644
--- a/core/src/test/java/ai/z/openapi/service/audio/AudioServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/audio/AudioServiceTest.java
@@ -303,7 +303,7 @@ public class AudioServiceTest {
@DisplayName("Should transcribe different audio formats successfully")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void shouldTranscribeDifferentAudioFormatsSuccessfully() throws JsonProcessingException {
- String[] audioFiles = { "asr.wav", "asr.webm" };
+ String[] audioFiles = { "asr.wav", "asr.mp3" };
for (String audioFile : audioFiles) {
String requestId = String.format(REQUEST_ID_TEMPLATE + "-%s", System.currentTimeMillis(), audioFile);
diff --git a/core/src/test/java/ai/z/openapi/service/chat/ChatServiceTest.java b/core/src/test/java/ai/z/openapi/service/chat/ChatServiceTest.java
index 088a20d..3cb32a6 100644
--- a/core/src/test/java/ai/z/openapi/service/chat/ChatServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/chat/ChatServiceTest.java
@@ -508,4 +508,187 @@ public class ChatServiceTest {
logger.info("CodeGeex code completion test completed");
}
+ @Test
+ @DisplayName("Test Synchronous Chat Completion with Custom Headers")
+ @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
+ void testSyncChatCompletionWithCustomHeaders() throws JsonProcessingException {
+ // Prepare test data
+ List messages = new ArrayList<>();
+ ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), "Hello, please introduce yourself");
+ messages.add(userMessage);
+
+ String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
+
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4)
+ .stream(Boolean.FALSE)
+ .messages(messages)
+ .requestId(requestId)
+ .build();
+
+ // Prepare custom headers
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-Custom-User-ID", "test-user-123");
+ customHeaders.put("X-Request-Source", "junit-test");
+ customHeaders.put("Session-Id", "session-" + System.currentTimeMillis());
+
+ // Execute test
+ ChatCompletionResponse response = chatService.createChatCompletion(request, customHeaders);
+
+ // Verify results
+ assertNotNull(response, "Response should not be null");
+ assertTrue(response.isSuccess(), "Response should be successful");
+ assertNotNull(response.getData(), "Response data should not be null");
+ assertEquals(requestId, response.getData().getRequestId(), "Request ID should match");
+ assertNotNull(response.getData().getChoices(), "Response data should not be null");
+ assertFalse(response.getData().getChoices().isEmpty(), "Response data should not be empty");
+ assertNull(response.getError(), "Response error should be null");
+ logger.info("Synchronous chat completion with custom headers response: {}",
+ mapper.writeValueAsString(response));
+ }
+
+ @Test
+ @DisplayName("Test Stream Chat Completion with Custom Headers")
+ @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
+ void testStreamChatCompletionWithCustomHeaders() throws JsonProcessingException {
+ // Prepare test data
+ List messages = new ArrayList<>();
+ ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(),
+ "Please write a short poem about spring");
+ messages.add(userMessage);
+
+ String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
+
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4)
+ .stream(Boolean.TRUE)
+ .messages(messages)
+ .requestId(requestId)
+ .temperature(0.7F)
+ .maxTokens(100)
+ .build();
+
+ // Prepare custom headers
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-Custom-User-ID", "stream-test-user-456");
+ customHeaders.put("X-Request-Source", "junit-stream-test");
+ customHeaders.put("X-Stream-Mode", "enabled");
+ customHeaders.put("Content-Type", "application/json");
+
+ // Execute test
+ ChatCompletionResponse response = chatService.createChatCompletion(request, customHeaders);
+
+ // Verify results
+ assertNotNull(response, "Response should not be null");
+ assertTrue(response.isSuccess(), "Response should be successful");
+
+ // Test stream data processing
+ AtomicInteger messageCount = new AtomicInteger(0);
+ AtomicBoolean isFirst = new AtomicBoolean(true);
+
+ response.getFlowable().doOnNext(modelData -> {
+ if (isFirst.getAndSet(false)) {
+ logger.info("Starting to receive stream response with custom headers:");
+ }
+ if (modelData.getChoices() != null && !modelData.getChoices().isEmpty()) {
+ Choice choice = modelData.getChoices().get(0);
+ if (choice.getDelta() != null && choice.getDelta().getContent() != null) {
+ logger.info("Received content: {}", choice.getDelta().getContent());
+ messageCount.incrementAndGet();
+ }
+ }
+ })
+ .doOnComplete(() -> logger.info(
+ "Stream response with custom headers completed, received {} messages in total", messageCount.get()))
+ .blockingSubscribe();
+
+ assertTrue(messageCount.get() > 0, "Should receive at least one message");
+
+ logger.info("Stream chat completion with custom headers test completed");
+ }
+
+ @Test
+ @DisplayName("Test Custom Headers Validation - Null Headers")
+ void testCustomHeadersValidation_NullHeaders() {
+ // Prepare test data
+ List messages = new ArrayList<>();
+ ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), "Hello");
+ messages.add(userMessage);
+
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4)
+ .stream(Boolean.FALSE)
+ .messages(messages)
+ .build();
+
+ // Test with null custom headers
+ assertThrows(IllegalArgumentException.class, () -> {
+ chatService.createChatCompletion(request, null);
+ }, "Null custom headers should throw IllegalArgumentException");
+ }
+
+ @Test
+ @DisplayName("Test Custom Headers with Empty Map")
+ @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
+ void testCustomHeadersWithEmptyMap() throws JsonProcessingException {
+ // Prepare test data
+ List messages = new ArrayList<>();
+ ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), "Hello");
+ messages.add(userMessage);
+
+ String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
+
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4)
+ .stream(Boolean.FALSE)
+ .messages(messages)
+ .requestId(requestId)
+ .build();
+
+ // Test with empty custom headers map
+ Map emptyHeaders = new HashMap<>();
+ ChatCompletionResponse response = chatService.createChatCompletion(request, emptyHeaders);
+
+ // Verify results
+ assertNotNull(response, "Response should not be null");
+ assertTrue(response.isSuccess(), "Response should be successful");
+ assertNotNull(response.getData(), "Response data should not be null");
+ logger.info("Chat completion with empty custom headers response: {}", mapper.writeValueAsString(response));
+ }
+
+ @Test
+ @DisplayName("Test Custom Headers with Special Characters")
+ @EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
+ void testCustomHeadersWithSpecialCharacters() throws JsonProcessingException {
+ // Prepare test data
+ List messages = new ArrayList<>();
+ ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), "Hello");
+ messages.add(userMessage);
+
+ String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
+
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4)
+ .stream(Boolean.FALSE)
+ .messages(messages)
+ .requestId(requestId)
+ .build();
+
+ // Prepare custom headers with special characters
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-User-Agent", "ZAI-SDK/1.0 (Java; Test)");
+ customHeaders.put("X-Client-Version", "v1.2.3-beta");
+ customHeaders.put("X-Request-Timestamp", String.valueOf(System.currentTimeMillis()));
+ customHeaders.put("X-Trace-ID", "trace-" + UUID.randomUUID());
+
+ // Execute test
+ ChatCompletionResponse response = chatService.createChatCompletion(request, customHeaders);
+
+ // Verify results
+ assertNotNull(response, "Response should not be null");
+ assertTrue(response.isSuccess(), "Response should be successful");
+ assertNotNull(response.getData(), "Response data should not be null");
+ logger.info("Chat completion with special character headers response: {}", mapper.writeValueAsString(response));
+ }
+
}
diff --git a/core/src/test/java/ai/z/openapi/service/chat/ChatServiceWithHeadersIntegrationTest.java b/core/src/test/java/ai/z/openapi/service/chat/ChatServiceWithHeadersIntegrationTest.java
new file mode 100644
index 0000000..ffc8d91
--- /dev/null
+++ b/core/src/test/java/ai/z/openapi/service/chat/ChatServiceWithHeadersIntegrationTest.java
@@ -0,0 +1,120 @@
+package ai.z.openapi.service.chat;
+
+import ai.z.openapi.service.model.ChatCompletionCreateParams;
+import ai.z.openapi.service.model.ChatMessage;
+import ai.z.openapi.service.model.ChatMessageRole;
+import ai.z.openapi.service.model.ChatRequestWithHeaders;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * Unit test for ChatRequestWithHeaders wrapper functionality. Tests the wrapper class
+ * used for custom headers in chat requests.
+ */
+class ChatServiceWithHeadersIntegrationTest {
+
+ @Test
+ void testChatRequestWithHeaders_StreamingRequest() {
+ // Arrange
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model("glm-4-flash")
+ .messages(Arrays
+ .asList(ChatMessage.builder().role(ChatMessageRole.USER.value()).content("Hello, world!").build()))
+ .stream(true)
+ .build();
+
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-Custom-User-ID", "user123");
+ customHeaders.put("X-Request-Source", "test");
+
+ // Act
+ ChatRequestWithHeaders wrapper = new ChatRequestWithHeaders(request, customHeaders);
+
+ // Assert
+ assertNotNull(wrapper);
+ assertNotNull(wrapper.getRequest());
+ assertNotNull(wrapper.getCustomHeaders());
+ assertEquals(request, wrapper.getRequest());
+ assertEquals(customHeaders, wrapper.getCustomHeaders());
+ assertEquals(2, wrapper.getCustomHeaders().size());
+ assertEquals("user123", wrapper.getCustomHeaders().get("X-Custom-User-ID"));
+ assertEquals("test", wrapper.getCustomHeaders().get("X-Request-Source"));
+ }
+
+ @Test
+ void testChatRequestWithHeaders_NonStreamingRequest() {
+ // Arrange
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model("glm-4-flash")
+ .messages(Arrays
+ .asList(ChatMessage.builder().role(ChatMessageRole.USER.value()).content("What is AI?").build()))
+ .stream(false) // Non-streaming request
+ .build();
+
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-Custom-User-ID", "user456");
+ customHeaders.put("X-Request-Source", "test-sync");
+
+ // Act
+ ChatRequestWithHeaders wrapper = new ChatRequestWithHeaders(request, customHeaders);
+
+ // Assert
+ assertNotNull(wrapper);
+ assertNotNull(wrapper.getRequest());
+ assertNotNull(wrapper.getCustomHeaders());
+ assertEquals(request, wrapper.getRequest());
+ assertEquals(customHeaders, wrapper.getCustomHeaders());
+ assertFalse(wrapper.getRequest().getStream()); // Verify non-streaming
+ assertEquals("user456", wrapper.getCustomHeaders().get("X-Custom-User-ID"));
+ assertEquals("test-sync", wrapper.getCustomHeaders().get("X-Request-Source"));
+ }
+
+ @Test
+ void testChatRequestWithHeaders_NullHeaders() {
+ // Arrange
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model("glm-4-flash")
+ .messages(Arrays
+ .asList(ChatMessage.builder().role(ChatMessageRole.USER.value()).content("Test message").build()))
+ .stream(false)
+ .build();
+
+ // Act
+ ChatRequestWithHeaders wrapper = new ChatRequestWithHeaders(request, null);
+
+ // Assert
+ assertNotNull(wrapper);
+ assertNotNull(wrapper.getRequest());
+ assertEquals(request, wrapper.getRequest());
+ // Custom headers are converted to empty map when null
+ assertNull(wrapper.getCustomHeaders());
+ }
+
+ @Test
+ void testChatRequestWithHeaders_ToString() {
+ // Arrange
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model("glm-4-flash")
+ .messages(Arrays
+ .asList(ChatMessage.builder().role(ChatMessageRole.USER.value()).content("Test message").build()))
+ .stream(false)
+ .build();
+
+ Map headers = new HashMap<>();
+ headers.put("X-Test", "value");
+
+ // Act
+ ChatRequestWithHeaders wrapper = new ChatRequestWithHeaders(request, headers);
+ String toStringResult = wrapper.toString();
+
+ // Assert
+ assertNotNull(toStringResult);
+ assertTrue(toStringResult.contains("ChatRequestWithHeaders"));
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java b/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java
index 53e94cb..a18dd46 100644
--- a/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/file/FileServiceTest.java
@@ -37,13 +37,6 @@ public class FileServiceTest {
// Request ID template
private static final String REQUEST_ID_TEMPLATE = "file-test-%d";
- // Test file purposes
- private static final String PURPOSE_FINE_TUNE = "fine-tune";
-
- private static final String PURPOSE_ASSISTANTS = "assistants";
-
- private static final String PURPOSE_BATCH = "batch";
-
@BeforeEach
void setUp() {
ZaiConfig zaiConfig = new ZaiConfig();
@@ -74,7 +67,7 @@ public class FileServiceTest {
FileUploadParams request = FileUploadParams.builder()
.filePath(tempFile.toString())
- .purpose(PURPOSE_ASSISTANTS)
+ .purpose(UploadFilePurpose.AGENT.value())
.requestId(requestId)
.build();
@@ -87,7 +80,7 @@ public class FileServiceTest {
assertNotNull(response.getData(), "Response data should not be null");
assertNotNull(response.getData().getId(), "File ID should not be null");
assertEquals("file", response.getData().getObject(), "Object type should be 'file'");
- assertEquals(PURPOSE_ASSISTANTS, response.getData().getPurpose(), "Purpose should match");
+ assertEquals(UploadFilePurpose.AGENT.value(), response.getData().getPurpose(), "Purpose should match");
assertNotNull(response.getData().getFilename(), "Filename should not be null");
assertNotNull(response.getData().getBytes(), "File size should not be null");
assertTrue(response.getData().getBytes() > 0, "File size should be greater than 0");
@@ -119,7 +112,7 @@ public class FileServiceTest {
FileUploadParams request = FileUploadParams.builder()
.filePath(tempFile.toString())
- .purpose(PURPOSE_FINE_TUNE)
+ .purpose(UploadFilePurpose.FILE_EXTRACT.value())
.requestId(requestId)
.extraJson(extraJson)
.build();
@@ -131,7 +124,8 @@ public class FileServiceTest {
assertNotNull(response, "Response should not be null");
assertTrue(response.isSuccess(), "Response should be successful");
assertNotNull(response.getData(), "Response data should not be null");
- assertEquals(PURPOSE_FINE_TUNE, response.getData().getPurpose(), "Purpose should match");
+ assertEquals(UploadFilePurpose.FILE_EXTRACT.value(), response.getData().getPurpose(),
+ "Purpose should match");
assertNull(response.getError(), "Response error should be null");
logger.info("File upload with extra JSON response: {}", mapper.writeValueAsString(response));
@@ -149,7 +143,7 @@ public class FileServiceTest {
FileUploadParams request = FileUploadParams.builder()
.filePath("/non/existent/file.txt")
- .purpose(PURPOSE_ASSISTANTS)
+ .purpose(UploadFilePurpose.AGENT.value())
.requestId(requestId)
.build();
@@ -187,7 +181,7 @@ public class FileServiceTest {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
FileListParams request = FileListParams.builder()
- .purpose(PURPOSE_ASSISTANTS)
+ .purpose(UploadFilePurpose.AGENT.value())
.limit(5)
.order("asc")
.requestId(requestId)
@@ -205,7 +199,8 @@ public class FileServiceTest {
// Verify purpose filter if files exist
if (response.getData().getData() != null && !response.getData().getData().isEmpty()) {
response.getData().getData().forEach(file -> {
- assertEquals(PURPOSE_ASSISTANTS, file.getPurpose(), "All files should have the specified purpose");
+ assertEquals(UploadFilePurpose.AGENT.value(), file.getPurpose(),
+ "All files should have the specified purpose");
});
}
@@ -269,7 +264,7 @@ public class FileServiceTest {
FileUploadParams uploadRequest = FileUploadParams.builder()
.filePath(tempFile.toString())
- .purpose(PURPOSE_ASSISTANTS)
+ .purpose(UploadFilePurpose.FILE_EXTRACT.value())
.requestId(requestId)
.build();
diff --git a/core/src/test/java/ai/z/openapi/service/image/ImageServiceTest.java b/core/src/test/java/ai/z/openapi/service/image/ImageServiceTest.java
index f905018..d19a549 100644
--- a/core/src/test/java/ai/z/openapi/service/image/ImageServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/image/ImageServiceTest.java
@@ -110,13 +110,13 @@ public class ImageServiceTest {
@DisplayName("Test Different Image Sizes")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testDifferentImageSizes() throws JsonProcessingException {
- String[] sizes = { "256x256", "512x512", "1024x1024" };
+ String[] sizes = { "512x512", "1024x1024" };
for (String size : sizes) {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
CreateImageRequest request = CreateImageRequest.builder()
- .model(Constants.ModelCogView3Plus)
+ .model(Constants.ModelCogView4)
.prompt("A simple geometric pattern")
.size(size)
.requestId(requestId)
@@ -124,7 +124,8 @@ public class ImageServiceTest {
ImageResponse response = imageService.createImage(request);
- assertNotNull(response, "Response should not be null for size: " + size);
+ assertNotNull(response.getData(), "Response should not be null for size: " + size);
+ assertEquals(200, response.getCode());
logger.info("Size {} response: {}", size, mapper.writeValueAsString(response));
}
}
diff --git a/core/src/test/java/ai/z/openapi/service/videos/VideosServiceTest.java b/core/src/test/java/ai/z/openapi/service/videos/VideosServiceTest.java
index 24ed764..c7f843f 100644
--- a/core/src/test/java/ai/z/openapi/service/videos/VideosServiceTest.java
+++ b/core/src/test/java/ai/z/openapi/service/videos/VideosServiceTest.java
@@ -1,6 +1,7 @@
package ai.z.openapi.service.videos;
import ai.z.openapi.ZaiClient;
+import ai.z.openapi.core.Constants;
import ai.z.openapi.core.config.ZaiConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -35,11 +36,6 @@ public class VideosServiceTest {
// Request ID template
private static final String REQUEST_ID_TEMPLATE = "video-test-%d";
- // Video model constants
- private static final String MODEL_COGVIDEOX = "cogvideox";
-
- private static final String MODEL_COGVIDEO3 = "cogvideo-3";
-
@BeforeEach
void setUp() {
ZaiConfig zaiConfig = new ZaiConfig();
@@ -67,7 +63,7 @@ public class VideosServiceTest {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
VideoCreateParams request = VideoCreateParams.builder()
- .model(MODEL_COGVIDEOX)
+ .model(Constants.ModelCogVideoX3)
.prompt("A beautiful sunset over the ocean with waves gently crashing on the shore")
.requestId(requestId)
.withAudio(Boolean.TRUE)
@@ -80,6 +76,7 @@ public class VideosServiceTest {
// Verify results
assertNotNull(response, "Response should not be null");
+ assertEquals(200, response.getCode());
assertTrue(response.isSuccess(), "Response should be successful");
assertNotNull(response.getData(), "Response data should not be null");
assertNotNull(response.getData().getId(), "Response data ID should not be null");
@@ -95,7 +92,7 @@ public class VideosServiceTest {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
VideoCreateParams request = VideoCreateParams.builder()
- .model(MODEL_COGVIDEOX)
+ .model(Constants.ModelCogVideoX3)
.prompt("A person walking in a beautiful garden")
.requestId(requestId)
.build();
@@ -114,6 +111,7 @@ public class VideosServiceTest {
// Verify result response
assertNotNull(resultResponse, "Result response should not be null");
+ assertEquals(200, resultResponse.getCode());
assertNotNull(resultResponse.getData(), "Result response data should not be null");
assertNotNull(resultResponse.getData().getId(), "Result response task ID should not be null");
logger.info("Video generation result: taskId={}, response={}", taskId,
@@ -138,7 +136,7 @@ public class VideosServiceTest {
}
@ParameterizedTest
- @ValueSource(strings = { MODEL_COGVIDEOX, MODEL_COGVIDEO3 })
+ @ValueSource(strings = { Constants.ModelCogVideoX2, Constants.ModelCogVideoX3 })
@DisplayName("Test Different Video Models")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testDifferentModels(String model) throws JsonProcessingException {
@@ -153,6 +151,7 @@ public class VideosServiceTest {
VideosResponse response = videosService.videoGenerations(request);
assertNotNull(response, "Response should not be null");
+ assertEquals(200, response.getCode());
logger.info("Model {} response: {}", model, mapper.writeValueAsString(response));
}
@@ -177,7 +176,7 @@ public class VideosServiceTest {
@Test
@DisplayName("Test Parameter Validation - Empty Prompt")
void testValidation_EmptyPrompt() {
- VideoCreateParams request = VideoCreateParams.builder().model(MODEL_COGVIDEOX).prompt("").build();
+ VideoCreateParams request = VideoCreateParams.builder().model(Constants.ModelCogVideoX3).prompt("").build();
assertThrows(IllegalArgumentException.class, () -> {
videosService.videoGenerations(request);
@@ -202,17 +201,18 @@ public class VideosServiceTest {
Base64.Encoder encoder = Base64.getEncoder();
String imageUrl = encoder.encodeToString(bytes);
VideoCreateParams request = VideoCreateParams.builder()
- .model(MODEL_COGVIDEOX)
+ .model(Constants.ModelCogVideoX3)
.prompt("Transform this image into a dynamic video scene")
.imageUrl(imageUrl)
.requestId(requestId)
.withAudio(Boolean.FALSE)
- .duration(3)
+ .duration(5)
.build();
VideosResponse response = videosService.videoGenerations(request);
assertNotNull(response, "Response should not be null");
+ assertEquals(200, response.getCode());
logger.info("Video generation with image response: {}", mapper.writeValueAsString(response));
}
@@ -223,19 +223,20 @@ public class VideosServiceTest {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
VideoCreateParams request = VideoCreateParams.builder()
- .model(MODEL_COGVIDEOX)
+ .model(Constants.ModelCogVideoX3)
.prompt("A futuristic city with flying cars and neon lights")
.requestId(requestId)
- .quality("high")
+ .quality("speed")
.withAudio(Boolean.TRUE)
.size("1280x720")
- .duration(10)
+ .duration(5)
.fps(30)
.build();
VideosResponse response = videosService.videoGenerations(request);
assertNotNull(response, "Response should not be null");
+ assertEquals(200, response.getCode());
logger.info("Video generation with custom settings response: {}", mapper.writeValueAsString(response));
}
diff --git a/core/src/test/resources/asr.mp3 b/core/src/test/resources/asr.mp3
new file mode 100644
index 0000000..ff9b04d
Binary files /dev/null and b/core/src/test/resources/asr.mp3 differ
diff --git a/samples/src/main/ai.z.openapi.samples/AgentExample.java b/samples/src/main/ai.z.openapi.samples/AgentExample.java
index 80c4b73..f13a124 100644
--- a/samples/src/main/ai.z.openapi.samples/AgentExample.java
+++ b/samples/src/main/ai.z.openapi.samples/AgentExample.java
@@ -21,7 +21,7 @@ public class AgentExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
diff --git a/samples/src/main/ai.z.openapi.samples/AgentVideoExample.java b/samples/src/main/ai.z.openapi.samples/AgentVideoExample.java
index ae22243..2902937 100644
--- a/samples/src/main/ai.z.openapi.samples/AgentVideoExample.java
+++ b/samples/src/main/ai.z.openapi.samples/AgentVideoExample.java
@@ -21,7 +21,7 @@ public class AgentVideoExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
diff --git a/samples/src/main/ai.z.openapi.samples/ChatAsyncCompletionExample.java b/samples/src/main/ai.z.openapi.samples/ChatAsyncCompletionExample.java
index 307aa17..6b2ff83 100644
--- a/samples/src/main/ai.z.openapi.samples/ChatAsyncCompletionExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ChatAsyncCompletionExample.java
@@ -19,13 +19,13 @@ public class ChatAsyncCompletionExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZhipuAiClient client = ZhipuAiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Create chat request
diff --git a/samples/src/main/ai.z.openapi.samples/ChatCompletionExample.java b/samples/src/main/ai.z.openapi.samples/ChatCompletionExample.java
index 00cfd75..41e4723 100644
--- a/samples/src/main/ai.z.openapi.samples/ChatCompletionExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ChatCompletionExample.java
@@ -14,13 +14,13 @@ public class ChatCompletionExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZhipuAiClient client = ZhipuAiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Create chat request
diff --git a/samples/src/main/ai.z.openapi.samples/ChatCompletionWithCustomHeadersExample.java b/samples/src/main/ai.z.openapi.samples/ChatCompletionWithCustomHeadersExample.java
new file mode 100644
index 0000000..dc3f917
--- /dev/null
+++ b/samples/src/main/ai.z.openapi.samples/ChatCompletionWithCustomHeadersExample.java
@@ -0,0 +1,90 @@
+package ai.z.openapi.samples;
+
+import ai.z.openapi.ZhipuAiClient;
+import ai.z.openapi.service.model.*;
+import ai.z.openapi.core.Constants;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Chat Completion with Custom Headers Example
+ * Demonstrates how to use ZaiClient for chat conversations with custom HTTP headers
+ */
+public class ChatCompletionWithCustomHeadersExample {
+
+ public static void main(String[] args) {
+ // Create client, recommended to set API Key via environment variable
+ // export ZAI_API_KEY=your.api_key
+ // for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
+ ZhipuAiClient client = ZhipuAiClient.builder().build();
+
+ // Create chat request
+ ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4_5)
+ .messages(Arrays.asList(
+ ChatMessage.builder()
+ .role(ChatMessageRole.USER.value())
+ .content("Hello, how are you?")
+ .build()
+ ))
+ .stream(true) // Enable streaming for custom headers support
+ .temperature(0.7f)
+ .maxTokens(1024)
+ .build();
+
+ // Create custom headers
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("X-Custom-User-ID", "user123");
+ customHeaders.put("X-Request-Source", "mobile-app");
+ customHeaders.put("Session-Id", "session-abc-123");
+
+ try {
+ // Execute request with custom headers
+ // This works for both streaming and non-streaming requests
+ ChatCompletionResponse response = client.chat().createChatCompletion(request, customHeaders);
+
+ // Example for non-streaming request with custom headers
+ ChatCompletionCreateParams nonStreamingRequest = ChatCompletionCreateParams.builder()
+ .model(Constants.ModelChatGLM4_5)
+ .messages(Arrays.asList(
+ ChatMessage.builder()
+ .role(ChatMessageRole.USER.value())
+ .content("What is artificial intelligence?")
+ .build()
+ ))
+ .stream(false) // Explicitly set to false for non-streaming
+ .temperature(0.7f)
+ .maxTokens(1024)
+ .build();
+
+ ChatCompletionResponse nonStreamingResponse = client.chat()
+ .createChatCompletion(nonStreamingRequest, customHeaders);
+
+ System.out.println("Non-streaming response: " + nonStreamingResponse.getData());
+
+ if (response.isSuccess() && response.getFlowable() != null) {
+ System.out.println("Streaming response with custom headers:");
+ 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("Error: " + response.getMsg());
+ }
+ } catch (Exception e) {
+ System.err.println("Exception occurred: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/src/main/ai.z.openapi.samples/ClientConfigurationExample.java b/samples/src/main/ai.z.openapi.samples/ClientConfigurationExample.java
index 905c474..4abe954 100644
--- a/samples/src/main/ai.z.openapi.samples/ClientConfigurationExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ClientConfigurationExample.java
@@ -1,6 +1,10 @@
package ai.z.openapi.samples;
import ai.z.openapi.ZaiClient;
+import ai.z.openapi.ZhipuAiClient;
+
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -12,14 +16,17 @@ public class ClientConfigurationExample {
public static void main(String[] args) {
System.out.println("=== Basic Configuration Example ===");
- ZaiClient basicClient = ZaiClient.builder().build();
+ ZaiClient basicClient = ZaiClient.builder().apiKey("xxx.xxx").build();
System.out.println("✓ Basic client created successfully");
// Complete configuration example
System.out.println("\n=== Complete Configuration Example ===");
+ Map customHeaders = new HashMap<>();
+ customHeaders.put("Session-Id", "custom-session-id-xx");
ZaiClient advancedClient = ZaiClient.builder()
- .apiKey("your.api.key")
+ .apiKey("your.api_key")
.baseUrl("https://api.z.ai/api/paas/v4/")
+ .customHeaders(customHeaders)
.enableTokenCache()
.tokenExpire(3600000) // 1 hour
.connectionPool(10, 5, TimeUnit.MINUTES)
@@ -28,14 +35,25 @@ public class ClientConfigurationExample {
// ZHIPU platform specific client
System.out.println("\n=== ZHIPU Platform Specific Configuration ===");
- ZaiClient zhipuClient = ZaiClient.ofZHIPU("your.api.key").build();
+ ZaiClient zhipuClient = ZaiClient.ofZHIPU("your.api_key").build();
System.out.println("✓ ZHIPU platform client created successfully");
-
+
+ ZhipuAiClient zhipuAiClient = ZhipuAiClient.builder()
+ .apiKey("your.api_key")
+ .baseUrl("https://api.z.ai/api/paas/v4/")
+ .customHeaders(customHeaders)
+ .enableTokenCache()
+ .tokenExpire(3600000) // 1 hour
+ .connectionPool(10, 5, TimeUnit.MINUTES)
+ .build();
+ System.out.println("✓ ZHIPU platform client created successfully");
+
// Custom configuration example
System.out.println("\n=== Custom Configuration Example ===");
ZaiClient customClient = ZaiClient.builder()
- .apiKey("your.api.key")
+ .apiKey("your.api_key")
.baseUrl("https://custom.api.endpoint/")
+ .customHeaders(customHeaders)
.enableTokenCache()
.tokenExpire(7200000)
.connectionPool(20, 10, TimeUnit.MINUTES)
diff --git a/samples/src/main/ai.z.openapi.samples/CogVideoX3Example.java b/samples/src/main/ai.z.openapi.samples/CogVideoX3Example.java
index 77ec566..d41a4be 100644
--- a/samples/src/main/ai.z.openapi.samples/CogVideoX3Example.java
+++ b/samples/src/main/ai.z.openapi.samples/CogVideoX3Example.java
@@ -16,13 +16,13 @@ public class CogVideoX3Example {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Video generation examples
diff --git a/samples/src/main/ai.z.openapi.samples/CogVideoXExample.java b/samples/src/main/ai.z.openapi.samples/CogVideoXExample.java
index 6a532a2..913aec5 100644
--- a/samples/src/main/ai.z.openapi.samples/CogVideoXExample.java
+++ b/samples/src/main/ai.z.openapi.samples/CogVideoXExample.java
@@ -13,13 +13,13 @@ public class CogVideoXExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Basic Video Generation
diff --git a/samples/src/main/ai.z.openapi.samples/GLM41VThinkingExample.java b/samples/src/main/ai.z.openapi.samples/GLM41VThinkingExample.java
index 7c8c635..70280cd 100644
--- a/samples/src/main/ai.z.openapi.samples/GLM41VThinkingExample.java
+++ b/samples/src/main/ai.z.openapi.samples/GLM41VThinkingExample.java
@@ -16,7 +16,7 @@ public class GLM41VThinkingExample {
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder()
- .apiKey("your.api.key")
+ .apiKey("your.api_key")
.build();
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
diff --git a/samples/src/main/ai.z.openapi.samples/GLM4VPlusExample.java b/samples/src/main/ai.z.openapi.samples/GLM4VPlusExample.java
index f40c3bc..b0ac710 100644
--- a/samples/src/main/ai.z.openapi.samples/GLM4VPlusExample.java
+++ b/samples/src/main/ai.z.openapi.samples/GLM4VPlusExample.java
@@ -15,7 +15,7 @@ public class GLM4VPlusExample {
public static void main(String[] args) {
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
- ZaiClient client = ZaiClient.builder().apiKey("your.api.key").build();
+ ZaiClient client = ZaiClient.builder().apiKey("your.api_key").build();
ChatCompletionCreateParams request = ChatCompletionCreateParams.builder()
.model("glm-4v-plus-0111")
diff --git a/samples/src/main/ai.z.openapi.samples/ViduAspectVideoExample.java b/samples/src/main/ai.z.openapi.samples/ViduAspectVideoExample.java
index 52a9c12..5d09478 100644
--- a/samples/src/main/ai.z.openapi.samples/ViduAspectVideoExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ViduAspectVideoExample.java
@@ -15,7 +15,7 @@ public class ViduAspectVideoExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
diff --git a/samples/src/main/ai.z.openapi.samples/ViduImageToVideoExample.java b/samples/src/main/ai.z.openapi.samples/ViduImageToVideoExample.java
index c08a23f..ad6ec8a 100644
--- a/samples/src/main/ai.z.openapi.samples/ViduImageToVideoExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ViduImageToVideoExample.java
@@ -13,7 +13,7 @@ public class ViduImageToVideoExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
diff --git a/samples/src/main/ai.z.openapi.samples/ViduStartEndVideoExample.java b/samples/src/main/ai.z.openapi.samples/ViduStartEndVideoExample.java
index 3bdf339..006fc8e 100644
--- a/samples/src/main/ai.z.openapi.samples/ViduStartEndVideoExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ViduStartEndVideoExample.java
@@ -15,7 +15,7 @@ public class ViduStartEndVideoExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
diff --git a/samples/src/main/ai.z.openapi.samples/ViduTextToVideoExample.java b/samples/src/main/ai.z.openapi.samples/ViduTextToVideoExample.java
index 0d9ca4c..2a219ff 100644
--- a/samples/src/main/ai.z.openapi.samples/ViduTextToVideoExample.java
+++ b/samples/src/main/ai.z.openapi.samples/ViduTextToVideoExample.java
@@ -13,13 +13,13 @@ public class ViduTextToVideoExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Example: Generate video from text using Vidu
diff --git a/samples/src/main/ai.z.openapi.samples/WebSearchExample.java b/samples/src/main/ai.z.openapi.samples/WebSearchExample.java
index ee8088c..8db3ede 100644
--- a/samples/src/main/ai.z.openapi.samples/WebSearchExample.java
+++ b/samples/src/main/ai.z.openapi.samples/WebSearchExample.java
@@ -23,13 +23,13 @@ public class WebSearchExample {
public static void main(String[] args) {
// Create client, recommended to set API Key via environment variable
- // export ZAI_API_KEY=your.api.key
+ // export ZAI_API_KEY=your.api_key
// for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
ZaiClient client = ZaiClient.builder().build();
// Or set API Key via code
// ZaiClient client = ZaiClient.builder()
- // .apiKey("your.api.key")
+ // .apiKey("your.api_key")
// .build();
// Example 1: Basic Web Search