> T checkSuccess(T response) {
+ if (response == null || !response.isSuccess()) {
+ StringBuilder errorMessage = new StringBuilder("Failed to generate chat message:");
+ if (response != null && response.getErrors() != null) {
+ errorMessage.append(response.getErrors().stream()
+ .map(ApiResponse.Error::getMessage)
+ .reduce((a, b) -> a + "\n" + b)
+ .orElse(""));
+ }
+ throw new RuntimeException(errorMessage.toString());
+ }
+ return response;
+ }
+
+ private SuccessfulHttpResponse execute(Object apiRequest, String accountIdentifier, String modelName) {
+ HttpRequest httpRequest = HttpRequest.builder()
+ .method(POST)
+ .url(BASE_URL + "client/v4/accounts/" + accountIdentifier + "/ai/run/" + modelName)
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Authorization", authorizationHeader)
+ .body(toJson(apiRequest))
+ .build();
+ return httpClient.execute(httpRequest);
+ }
+
+ /**
+ * Builder access.
+ *
+ * @return builder instance
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link WorkersAiClient}.
+ */
+ public static class Builder {
+
+ private HttpClientBuilder httpClientBuilder;
+ private Duration timeout;
+ private String apiToken;
+
+ /**
+ * Sets the {@link HttpClientBuilder} used to create the underlying HTTP client.
+ *
+ * @param httpClientBuilder the HTTP client builder.
+ * @return {@code this}.
+ */
+ public Builder httpClientBuilder(HttpClientBuilder httpClientBuilder) {
+ this.httpClientBuilder = httpClientBuilder;
+ return this;
+ }
+
+ /**
+ * Sets the timeout used for both connecting and reading.
+ *
+ * @param timeout the timeout.
+ * @return {@code this}.
+ */
+ public Builder timeout(Duration timeout) {
+ this.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * Sets the API token used for authorization.
+ *
+ * @param apiToken the API token.
+ * @return {@code this}.
+ */
+ public Builder apiToken(String apiToken) {
+ this.apiToken = apiToken;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link WorkersAiClient}.
+ *
+ * @return a new client instance.
+ */
+ public WorkersAiClient build() {
+ return new WorkersAiClient(this);
+ }
+ }
}
diff --git a/langchain4j-workers-ai/src/main/java/dev/langchain4j/model/workersai/client/WorkersAiJsonUtils.java b/langchain4j-workers-ai/src/main/java/dev/langchain4j/model/workersai/client/WorkersAiJsonUtils.java
new file mode 100644
index 0000000000..c2cb9b9e46
--- /dev/null
+++ b/langchain4j-workers-ai/src/main/java/dev/langchain4j/model/workersai/client/WorkersAiJsonUtils.java
@@ -0,0 +1,35 @@
+package dev.langchain4j.model.workersai.client;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Internal JSON (de)serialization helper for the Workers AI client.
+ *
+ * The mapper is intentionally a plain {@link ObjectMapper} with default configuration, mirroring
+ * the behavior of the Jackson converter that was previously used by the Retrofit-based client.
+ */
+class WorkersAiJsonUtils {
+
+ private WorkersAiJsonUtils() throws InstantiationException {
+ throw new InstantiationException("Can't instantiate this utility class.");
+ }
+
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ static String toJson(Object object) {
+ try {
+ return OBJECT_MAPPER.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static T fromJson(String jsonStr, Class clazz) {
+ try {
+ return OBJECT_MAPPER.readValue(jsonStr, clazz);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}