Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion core/src/main/java/ai/z/openapi/AbstractAiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -601,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
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/ai/z/openapi/ZhipuAiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static Builder builder() {
* </p>
* <pre>{@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();
Expand Down
22 changes: 22 additions & 0 deletions core/src/main/java/ai/z/openapi/api/web_reader/WebReaderApi.java
Original file line number Diff line number Diff line change
@@ -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<WebReaderResult> reader(@Body WebReaderRequest request);

}
2 changes: 1 addition & 1 deletion core/src/main/java/ai/z/openapi/core/config/ZaiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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;
import java.net.URI;
import java.net.URISyntaxException;

@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class WebReaderRequest implements ClientRequest<WebReaderRequest> {

/**
* 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;

/**
* 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());
}
}

}
Original file line number Diff line number Diff line change
@@ -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<WebReaderResult> {

private int code;

private String msg;

private boolean success;

private WebReaderResult data;

private ChatError error;

}
Original file line number Diff line number Diff line change
@@ -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<String, String> images;

private Map<String, String> links;

private String title;

private String description;

private String url;

private String content;

private String publishedTime;

private Map<String, Object> metadata;

private Map<String, Object> external;

}

}
Original file line number Diff line number Diff line change
@@ -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);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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) {
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
}
request.validate();
RequestSupplier<WebReaderRequest, WebReaderResult> supplier = webReaderApi::reader;
return this.zAiClient.executeRequest(request, supplier, WebReaderResponse.class);
}

}
Loading