From 229e5f0db035df8b34b85375be680cb7c89796f5 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Fri, 7 Nov 2025 13:51:20 +0800 Subject: [PATCH 1/5] feat: add web reader api --- .../java/ai/z/openapi/AbstractAiClient.java | 17 ++++ .../openapi/api/web_reader/WebReaderApi.java | 22 +++++ .../service/web_reader/WebReaderRequest.java | 82 +++++++++++++++++++ .../service/web_reader/WebReaderResponse.java | 20 +++++ .../service/web_reader/WebReaderResult.java | 42 ++++++++++ .../service/web_reader/WebReaderService.java | 15 ++++ .../web_reader/WebReaderServiceImpl.java | 27 ++++++ .../web_reader/WebReaderServiceTest.java | 32 ++++++++ .../WebReaderExample.java | 80 ++++++++++++++++++ 9 files changed, 337 insertions(+) create mode 100644 core/src/main/java/ai/z/openapi/api/web_reader/WebReaderApi.java create mode 100644 core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java create mode 100644 core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResponse.java create mode 100644 core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResult.java create mode 100644 core/src/main/java/ai/z/openapi/service/web_reader/WebReaderService.java create mode 100644 core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java create mode 100644 core/src/test/java/ai/z/openapi/service/web_reader/WebReaderServiceTest.java create mode 100644 samples/src/main/ai.z.openapi.samples/WebReaderExample.java diff --git a/core/src/main/java/ai/z/openapi/AbstractAiClient.java b/core/src/main/java/ai/z/openapi/AbstractAiClient.java index 7df1add..bc305ae 100644 --- a/core/src/main/java/ai/z/openapi/AbstractAiClient.java +++ b/core/src/main/java/ai/z/openapi/AbstractAiClient.java @@ -21,6 +21,8 @@ import ai.z.openapi.service.batches.BatchServiceImpl; import ai.z.openapi.service.web_search.WebSearchService; import ai.z.openapi.service.web_search.WebSearchServiceImpl; +import ai.z.openapi.service.web_reader.WebReaderService; +import ai.z.openapi.service.web_reader.WebReaderServiceImpl; import ai.z.openapi.service.videos.VideosService; import ai.z.openapi.service.videos.VideosServiceImpl; import ai.z.openapi.service.assistant.AssistantService; @@ -99,6 +101,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService { /** Web search service for internet search capabilities */ private WebSearchService webSearchService; + /** Web reader service for parsing web pages */ + private WebReaderService webReaderService; + /** Videos service for video processing */ private VideosService videosService; @@ -230,6 +235,18 @@ public synchronized WebSearchService webSearch() { return webSearchService; } + /** + * Returns the web reader service for parsing web pages. This service reads and + * extracts content, metadata, images, and links from URLs. + * @return the WebReaderService instance (lazily initialized) + */ + public synchronized WebReaderService webReader() { + if (webReaderService == null) { + this.webReaderService = new WebReaderServiceImpl(this); + } + return webReaderService; + } + /** * Returns the videos service for video processing. This service handles video * analysis, generation, and manipulation. diff --git a/core/src/main/java/ai/z/openapi/api/web_reader/WebReaderApi.java b/core/src/main/java/ai/z/openapi/api/web_reader/WebReaderApi.java new file mode 100644 index 0000000..55840c1 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/api/web_reader/WebReaderApi.java @@ -0,0 +1,22 @@ +package ai.z.openapi.api.web_reader; + +import ai.z.openapi.service.web_reader.WebReaderRequest; +import ai.z.openapi.service.web_reader.WebReaderResult; +import io.reactivex.rxjava3.core.Single; +import retrofit2.http.Body; +import retrofit2.http.POST; + +/** + * Web Reader API for reading and parsing web page content. + */ +public interface WebReaderApi { + + /** + * Read and parse content from a given URL. + * @param request reader parameters including url and options + * @return parsed content and metadata + */ + @POST("reader") + Single reader(@Body WebReaderRequest request); + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java new file mode 100644 index 0000000..32a6f87 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java @@ -0,0 +1,82 @@ +package ai.z.openapi.service.web_reader; + +import com.fasterxml.jackson.annotation.JsonProperty; +import ai.z.openapi.core.model.ClientRequest; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class WebReaderRequest implements ClientRequest { + + /** + * The target URL to read and parse content from. + */ + @JsonProperty("url") + private String url; + + /** + * Unique request identifier, used for tracing. + */ + @JsonProperty("request_id") + private String requestId; + + /** + * User ID associated with the request. + */ + @JsonProperty("user_id") + private String userId; + + /** + * Timeout in seconds for the reader operation. + */ + @JsonProperty("timeout") + private Integer timeout; + + /** + * Whether to bypass cache when reading. + */ + @JsonProperty("no_cache") + private Boolean noCache; + + /** + * Return format of the reader output, e.g., markdown or plain. + */ + @JsonProperty("return_format") + private String returnFormat; + + /** + * Whether to retain image placeholders in the content. + */ + @JsonProperty("retain_images") + private Boolean retainImages; + + /** + * Whether to disable GitHub-Flavored Markdown processing. + */ + @JsonProperty("no_gfm") + private Boolean noGfm; + + /** + * Whether to keep image data URLs inline. + */ + @JsonProperty("keep_img_data_url") + private Boolean keepImgDataUrl; + + /** + * Whether to include images summary in the result. + */ + @JsonProperty("with_images_summary") + private Boolean withImagesSummary; + + /** + * Whether to include links summary in the result. + */ + @JsonProperty("with_links_summary") + private Boolean withLinksSummary; + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResponse.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResponse.java new file mode 100644 index 0000000..aa15fb6 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResponse.java @@ -0,0 +1,20 @@ +package ai.z.openapi.service.web_reader; + +import ai.z.openapi.core.model.ClientResponse; +import ai.z.openapi.service.model.ChatError; +import lombok.Data; + +@Data +public class WebReaderResponse implements ClientResponse { + + private int code; + + private String msg; + + private boolean success; + + private WebReaderResult data; + + private ChatError error; + +} diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResult.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResult.java new file mode 100644 index 0000000..337ebc1 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderResult.java @@ -0,0 +1,42 @@ +package ai.z.openapi.service.web_reader; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.Map; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class WebReaderResult { + + @JsonProperty("reader_result") + private ReaderData readerResult; + + @Data + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public static class ReaderData { + + private Map images; + + private Map links; + + private String title; + + private String description; + + private String url; + + private String content; + + private String publishedTime; + + private Map metadata; + + private Map external; + + } + +} diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderService.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderService.java new file mode 100644 index 0000000..d3a8b3f --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderService.java @@ -0,0 +1,15 @@ +package ai.z.openapi.service.web_reader; + +/** + * Web reader service interface + */ +public interface WebReaderService { + + /** + * Creates a web reader request to parse a URL. + * @param request the web reader request + * @return WebReaderResponse containing the reader result + */ + WebReaderResponse createWebReader(WebReaderRequest request); + +} \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java new file mode 100644 index 0000000..df34233 --- /dev/null +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java @@ -0,0 +1,27 @@ +package ai.z.openapi.service.web_reader; + +import ai.z.openapi.AbstractAiClient; +import ai.z.openapi.api.web_reader.WebReaderApi; +import ai.z.openapi.utils.RequestSupplier; + +/** + * Web reader service implementation + */ +public class WebReaderServiceImpl implements WebReaderService { + + private final AbstractAiClient zAiClient; + + private final WebReaderApi webReaderApi; + + public WebReaderServiceImpl(AbstractAiClient zAiClient) { + this.zAiClient = zAiClient; + this.webReaderApi = zAiClient.retrofit().create(WebReaderApi.class); + } + + @Override + public WebReaderResponse createWebReader(WebReaderRequest request) { + RequestSupplier supplier = webReaderApi::reader; + return this.zAiClient.executeRequest(request, supplier, WebReaderResponse.class); + } + +} \ No newline at end of file diff --git a/core/src/test/java/ai/z/openapi/service/web_reader/WebReaderServiceTest.java b/core/src/test/java/ai/z/openapi/service/web_reader/WebReaderServiceTest.java new file mode 100644 index 0000000..79b774b --- /dev/null +++ b/core/src/test/java/ai/z/openapi/service/web_reader/WebReaderServiceTest.java @@ -0,0 +1,32 @@ +package ai.z.openapi.service.web_reader; + +import ai.z.openapi.ZaiClient; +import ai.z.openapi.core.config.ZaiConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +@DisplayName("WebReaderService Tests") +public class WebReaderServiceTest { + + private WebReaderService webReaderService; + + @BeforeEach + void setUp() { + ZaiConfig zaiConfig = new ZaiConfig(); + if (zaiConfig.getApiKey() == null) { + zaiConfig.setApiKey("id.test-api-key"); + } + ZaiClient client = new ZaiClient(zaiConfig); + webReaderService = client.webReader(); + } + + @Test + @DisplayName("Test WebReaderService Instantiation") + void testWebReaderServiceInstantiation() { + assertNotNull(webReaderService, "WebReaderService should be instantiated"); + } + +} \ No newline at end of file diff --git a/samples/src/main/ai.z.openapi.samples/WebReaderExample.java b/samples/src/main/ai.z.openapi.samples/WebReaderExample.java new file mode 100644 index 0000000..7f59c56 --- /dev/null +++ b/samples/src/main/ai.z.openapi.samples/WebReaderExample.java @@ -0,0 +1,80 @@ +package ai.z.openapi.samples; + +import ai.z.openapi.ZaiClient; +import ai.z.openapi.service.web_reader.WebReaderRequest; +import ai.z.openapi.service.web_reader.WebReaderResponse; +import ai.z.openapi.service.web_reader.WebReaderResult; + +/** + * Web Reader Example + * Demonstrates how to use ZaiClient for web page reading and parsing capabilities + */ +public class WebReaderExample { + + 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 + ZaiClient client = ZaiClient.builder().ofZHIPU().build(); + + basicWebReader(client); + } + + /** + * Example of basic web reader functionality + */ + private static void basicWebReader(ZaiClient client) { + System.out.println("\n=== Web Reader Example ==="); + + // Create web reader request + WebReaderRequest request = WebReaderRequest.builder() + .url("https://example.com/") + .returnFormat("markdown") + .withImagesSummary(Boolean.TRUE) + .withLinksSummary(Boolean.TRUE) + .build(); + + try { + // Execute request + WebReaderResponse response = client.webReader().createWebReader(request); + + if (response.isSuccess()) { + System.out.println("Read successful!"); + + WebReaderResult result = response.getData(); + if (result != null && result.getReaderResult() != null) { + WebReaderResult.ReaderData data = result.getReaderResult(); + System.out.println("Title: " + data.getTitle()); + System.out.println("URL: " + data.getUrl()); + System.out.println("Description: " + data.getDescription()); + + String content = data.getContent(); + if (content != null) { + String preview = content.length() > 300 ? content.substring(0, 300) + "..." : content; + System.out.println("\nContent preview:\n" + preview); + } + + if (data.getImages() != null) { + System.out.println("\nImages count: " + data.getImages().size()); + } + if (data.getLinks() != null) { + System.out.println("Links count: " + data.getLinks().size()); + } + } + else { + System.out.println("No reader result returned."); + } + } + else { + System.err.println("Error: " + response.getMsg()); + if (response.getError() != null) { + System.err.println("Error detail: " + response.getError()); + } + } + } + catch (Exception e) { + System.err.println("Exception occurred: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file From 0066ef19a4cb67b2f5941ccb603e36bfe00aad56 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Fri, 7 Nov 2025 14:12:25 +0800 Subject: [PATCH 2/5] feat: add web reader api --- .../service/web_reader/WebReaderRequest.java | 33 +++++++++++++++++++ .../web_reader/WebReaderServiceImpl.java | 4 +++ 2 files changed, 37 insertions(+) diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java index 32a6f87..b65ff2e 100644 --- a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderRequest.java @@ -6,6 +6,8 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import java.net.URI; +import java.net.URISyntaxException; @SuperBuilder @NoArgsConstructor @@ -79,4 +81,35 @@ public class WebReaderRequest implements ClientRequest { @JsonProperty("with_links_summary") private Boolean withLinksSummary; + /** + * Validate request fields that require constraints. Ensures {@code url} is non-empty + * and a syntactically valid HTTP/HTTPS URL. + * @throws IllegalArgumentException if validation fails + */ + public void validate() { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("request url cannot be null or empty"); + } + String normalized = url.trim(); + try { + URI initial = new URI(normalized); + URI candidate = initial; + String scheme = initial.getScheme(); + if (scheme == null) { + String candidateStr = normalized.startsWith("//") ? ("https:" + normalized) : ("https://" + normalized); + candidate = new URI(candidateStr); + scheme = candidate.getScheme(); + } + if (!("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))) { + throw new IllegalArgumentException("request url must use http or https"); + } + if (candidate.getHost() == null || candidate.getHost().trim().isEmpty()) { + throw new IllegalArgumentException("request url must contain a valid host"); + } + } + catch (URISyntaxException ex) { + throw new IllegalArgumentException("request url is invalid: " + ex.getMessage()); + } + } + } \ No newline at end of file diff --git a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java index df34233..125956a 100644 --- a/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java +++ b/core/src/main/java/ai/z/openapi/service/web_reader/WebReaderServiceImpl.java @@ -20,6 +20,10 @@ public WebReaderServiceImpl(AbstractAiClient zAiClient) { @Override public WebReaderResponse createWebReader(WebReaderRequest request) { + if (request == null) { + throw new IllegalArgumentException("request cannot be null"); + } + request.validate(); RequestSupplier supplier = webReaderApi::reader; return this.zAiClient.executeRequest(request, supplier, WebReaderResponse.class); } From 6352471ab60e84eea3ea7a14a279b65117ffbc04 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Mon, 10 Nov 2025 15:00:59 +0800 Subject: [PATCH 3/5] feat: add web reader api --- samples/src/main/ai.z.openapi.samples/CustomClientExample.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/src/main/ai.z.openapi.samples/CustomClientExample.java b/samples/src/main/ai.z.openapi.samples/CustomClientExample.java index 14d9101..4c459c0 100644 --- a/samples/src/main/ai.z.openapi.samples/CustomClientExample.java +++ b/samples/src/main/ai.z.openapi.samples/CustomClientExample.java @@ -34,9 +34,8 @@ public static void main(String[] args) throws Exception { .baseUrl(Constants.ZHIPU_AI_BASE_URL) .customHeaders(Collections.emptyMap()) .disableTokenCache(true) - .requestTimeOut(600) + .readTimeout(600) .timeOutTimeUnit(TimeUnit.SECONDS) - .connectTimeout(60) .connectionPoolKeepAliveDuration(10) .connectionPoolTimeUnit(TimeUnit.SECONDS) .connectionPoolMaxIdleConnections(20) From ea0f0d2188749804b3456189f237679b8adc9879 Mon Sep 17 00:00:00 2001 From: tomsun28 Date: Mon, 10 Nov 2025 15:09:25 +0800 Subject: [PATCH 4/5] feat: add web reader api --- core/src/main/java/ai/z/openapi/AbstractAiClient.java | 2 +- core/src/main/java/ai/z/openapi/ZhipuAiClient.java | 2 +- samples/src/main/ai.z.openapi.samples/CustomTimeoutExample.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/ai/z/openapi/AbstractAiClient.java b/core/src/main/java/ai/z/openapi/AbstractAiClient.java index bc305ae..66e8261 100644 --- a/core/src/main/java/ai/z/openapi/AbstractAiClient.java +++ b/core/src/main/java/ai/z/openapi/AbstractAiClient.java @@ -618,7 +618,7 @@ public B tokenExpire(int expireMillis) { /** * Configures network request timeout settings. - * @param requestTimeOut the overall request timeout + * @param requestTimeOut the overall request timeout, 0 is no timeout * @param connectTimeout the connection timeout * @param readTimeout the read timeout * @param writeTimeout the write timeout diff --git a/core/src/main/java/ai/z/openapi/ZhipuAiClient.java b/core/src/main/java/ai/z/openapi/ZhipuAiClient.java index 0618d02..1f2b6fc 100644 --- a/core/src/main/java/ai/z/openapi/ZhipuAiClient.java +++ b/core/src/main/java/ai/z/openapi/ZhipuAiClient.java @@ -87,7 +87,7 @@ public static Builder builder() { *

*
{@code
 	 * ZhipuAiClient client = new ZhipuAiClient.Builder("your-api-key")
-	 *     .networkConfig(30, 10, 30, 30, TimeUnit.SECONDS)
+	 *     .networkConfig(0, 10, 30, 30, TimeUnit.SECONDS)
 	 *     .connectionPool(10, 5, TimeUnit.MINUTES)
 	 *     .enableTokenCache()
 	 *     .build();
diff --git a/samples/src/main/ai.z.openapi.samples/CustomTimeoutExample.java b/samples/src/main/ai.z.openapi.samples/CustomTimeoutExample.java
index 5bdfdda..1bed73a 100644
--- a/samples/src/main/ai.z.openapi.samples/CustomTimeoutExample.java
+++ b/samples/src/main/ai.z.openapi.samples/CustomTimeoutExample.java
@@ -25,7 +25,7 @@ public static void main(String[] args) {
         // export ZAI_API_KEY=your.api_key
         // for Z.ai use the `ZaiClient`, for Zhipu AI use the ZhipuAiClient
         ZhipuAiClient client = ZhipuAiClient.builder()
-            .networkConfig(30, 10, 30, 30, TimeUnit.SECONDS)
+            .networkConfig(0, 10, 30, 30, TimeUnit.SECONDS)
             .build();
 
         // Create chat request

From 0f6aed7b49db79027d498d1b0ad7ed222ad0deb1 Mon Sep 17 00:00:00 2001
From: tomsun28 
Date: Mon, 10 Nov 2025 15:30:22 +0800
Subject: [PATCH 5/5] feat: add web reader api

---
 .../ai/z/openapi/core/config/ZaiConfig.java   |  2 +-
 .../service/audio/AudioServiceImpl.java       |  6 +++--
 .../main/java/ai/z/openapi/utils/OkHttps.java | 23 ++++++++++++++++++-
 3 files changed, 27 insertions(+), 4 deletions(-)

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 c85c235..bad6fe7 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
@@ -97,7 +97,7 @@ public class ZaiConfig {
 
 	/**
 	 * Request timeout in specified time unit. The whole timeout for complete calls, is
-	 * the okhttp call timeout.
+	 * the okhttp call timeout. The 0 value means no timeout.
 	 */
 	private Integer requestTimeOut;
 
diff --git a/core/src/main/java/ai/z/openapi/service/audio/AudioServiceImpl.java b/core/src/main/java/ai/z/openapi/service/audio/AudioServiceImpl.java
index 3c5eb26..f0c0d1e 100644
--- a/core/src/main/java/ai/z/openapi/service/audio/AudioServiceImpl.java
+++ b/core/src/main/java/ai/z/openapi/service/audio/AudioServiceImpl.java
@@ -8,11 +8,12 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import io.reactivex.rxjava3.core.Single;
-import lombok.extern.slf4j.Slf4j;
 import okhttp3.MediaType;
 import okhttp3.MultipartBody;
 import okhttp3.RequestBody;
 import okhttp3.ResponseBody;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -26,9 +27,10 @@
 /**
  * Audio service implementation
  */
-@Slf4j
 public class AudioServiceImpl implements AudioService {
 
+	private static final Logger log = LoggerFactory.getLogger(AudioServiceImpl.class);
+
 	protected static final ObjectMapper mapper = MessageDeserializeFactory.defaultObjectMapper();
 
 	private final AbstractAiClient zAiClient;
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 af2e964..b41db8c 100644
--- a/core/src/main/java/ai/z/openapi/utils/OkHttps.java
+++ b/core/src/main/java/ai/z/openapi/utils/OkHttps.java
@@ -4,6 +4,9 @@
 import ai.z.openapi.core.token.HttpRequestInterceptor;
 import okhttp3.ConnectionPool;
 import okhttp3.OkHttpClient;
+import okhttp3.internal.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.concurrent.TimeUnit;
 
@@ -15,6 +18,8 @@
  */
 public final class OkHttps {
 
+	private static final Logger logger = LoggerFactory.getLogger(OkHttps.class);
+
 	// The default value is 0 which imposes no timeout.
 	private static final int DEFAULT_CALL_TIMEOUT_SECONDS = 0;
 
@@ -60,37 +65,53 @@ public static OkHttpClient create(ZaiConfig config) {
 	 */
 	private static void configureTimeouts(OkHttpClient.Builder builder, ZaiConfig config) {
 		TimeUnit timeUnit = config.getTimeOutTimeUnit();
-
+		int callTimeout;
+		int connectTimeout;
+		int readTimeout;
+		int writeTimeout;
 		// Configure call timeout
 		if (config.getRequestTimeOut() != null && config.getRequestTimeOut() > 0) {
 			builder.callTimeout(config.getRequestTimeOut(), timeUnit);
+			callTimeout = Util.checkDuration("callTimeout", config.getRequestTimeOut(), timeUnit);
 		}
 		else {
 			builder.callTimeout(DEFAULT_CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+			callTimeout = Util.checkDuration("callTimeout", DEFAULT_CALL_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 		}
 
 		// Configure connect timeout
 		if (config.getConnectTimeout() != null && config.getConnectTimeout() > 0) {
 			builder.connectTimeout(config.getConnectTimeout(), timeUnit);
+			connectTimeout = Util.checkDuration("connectTimeout", config.getConnectTimeout(), timeUnit);
 		}
 		else {
 			builder.connectTimeout(DEFAULT_CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+			connectTimeout = Util.checkDuration("connectTimeout", DEFAULT_CONNECT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 		}
 
 		// Configure read timeout
 		if (config.getReadTimeout() != null && config.getReadTimeout() > 0) {
 			builder.readTimeout(config.getReadTimeout(), timeUnit);
+			readTimeout = Util.checkDuration("readTimeout", config.getReadTimeout(), timeUnit);
 		}
 		else {
 			builder.readTimeout(DEFAULT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+			readTimeout = Util.checkDuration("readTimeout", DEFAULT_READ_TIMEOUT_SECONDS, TimeUnit.SECONDS);
 		}
 
 		// Configure write timeout
 		if (config.getWriteTimeout() != null && config.getWriteTimeout() > 0) {
 			builder.writeTimeout(config.getWriteTimeout(), timeUnit);
+			writeTimeout = Util.checkDuration("writeTimeout", config.getWriteTimeout(), timeUnit);
 		}
 		else {
 			builder.writeTimeout(DEFAULT_WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+			writeTimeout = Util.checkDuration("writeTimeout", DEFAULT_WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+		}
+		if (callTimeout != 0 && (callTimeout <= (connectTimeout + readTimeout + writeTimeout))) {
+			logger.error("Wrong Request(Call) timeout configuration");
+			logger.error(
+					"Request(Call) timeout is less than or equal to the sum of connect, read, and write timeouts. This may cause issues with the client.");
 		}
 	}