Evolvable.ai Java SDK Documentation
Version: 0.1.10 | Java: 11+ | Package: ai.datawise:evolvable-ai-java-client
Table of Contents
- Installation
- Client Setup
- Chat Operations
- Authentication Operations
- Admin Operations
- Spring Boot Integration
- Error Handling
- Data Models Reference
Installation
Add to your pom.xml:
<repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/evolvable-ai/evolvable-ai-java-client</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>ai.datawise</groupId>
<artifactId>evolvable-ai-java-client</artifactId>
<version>0.1.10</version>
</dependency>
</dependencies>Client Setup
Standard Client
EvolvableAiClient client = EvolvableAiClient.builder()
.baseUrl("https://your-api-endpoint.com")
.apiKey("your-api-key")
.tenant("your-tenant-id")
.connectionTimeout(Duration.ofSeconds(30)) // optional, default 30s
.readTimeout(Duration.ofSeconds(60)) // optional, default 60s
.writeTimeout(Duration.ofSeconds(60)) // optional, default 60s
.debug(false) // optional, enables debug logging
.build();Admin Client
EvolvableAiAdmin admin = EvolvableAiAdmin.builder()
.baseUrl("https://your-api-endpoint.com")
.username("admin-username")
.password("admin-password")
.build();
// Login before using admin operations
admin.adminLogin(new AdminLoginRequest()).join();All operations return
CompletableFuture<T>. Call.join()to block for the result or compose with.thenApply(),.thenAccept(), etc.
Chat Operations
Access via client.chat().
Send a Message
ChatRequest request = ChatRequest.simple("Hello, how can you help me?");
Message response = client.chat().send(request).join();
System.out.println(response.getValue());
System.out.println("Conversation ID: " + response.getConversationId());Continue a Conversation
// Use the conversationId from a previous message to maintain context
Message reply = client.chat()
.send(request, previousMessage.getConversationId())
.join();Chat with a Specific Agent
Message response = client.chat()
.sendWithAgent(request, "agent-id-123")
.join();
// Combine: continue a conversation with a specific agent
Message response = client.chat()
.send(request, conversationId, "agent-id-123")
.join();Streaming Chat
Receive chunks in real-time via a callback:
ChatRequest request = ChatRequest.simple("Tell me a story.");
Message finalMessage = client.chat().stream(request, event -> {
if (event instanceof StreamingChunkDto) {
System.out.print(((StreamingChunkDto) event).getChunk());
} else if (event instanceof StreamingStartDto) {
System.out.println("[Stream started]");
} else if (event instanceof StreamingEndDto) {
System.out.println("\n[Stream ended]");
} else if (event instanceof StreamingErrorDto) {
System.err.println("Error: " + ((StreamingErrorDto) event).getMessage());
}
}).join();Streaming variants mirror the non-streaming API:
// Stream within a conversation
client.chat().stream(request, conversationId, onEvent).join();
// Stream with a specific agent
client.chat().streamWithAgent(request, "agent-id", onEvent).join();
// Stream within a conversation with a specific agent
client.chat().stream(request, conversationId, "agent-id", onEvent).join();Streaming Event Types
| Type | Class | Description |
|---|---|---|
START | StreamingStartDto | Stream has begun |
CHUNK | StreamingChunkDto | Text chunk (.getChunk()) |
END | StreamingEndDto | Stream completed |
ERROR | StreamingErrorDto | Error occurred |
INFO | StreamingInfoDto | Informational message |
EVENT | — | Custom domain event |
TITLE | — | Title event |
OPTION | — | Option selection event |
ChatRequest Fields
ChatRequest request = ChatRequest.builder()
.message("Your message here")
.starterMessage(false) // optional: is this a conversation starter?
.metadata(List.of( // optional: key-value metadata
new SystemMetadataPair("key", "value")
))
.build();Message Response Fields
| Field | Type | Description |
|---|---|---|
id | Long | Message ID |
value | String | Response text content |
conversationId | Long | Conversation thread ID |
tokens | Integer | Tokens consumed |
createdAt | Instant | Timestamp |
type | MessageType | Bot or Human |
events | List<Event> | Domain events emitted |
Authentication Operations
Access via client.auth().
Server-to-Server Authentication
Generate a token for a user to authenticate with the widget/API:
// For a widget user
ServerToServerAuthRequest request = ServerToServerAuthRequest.builder()
.userId("user-123")
.username("johndoe")
.email("john@example.com")
.source(UserSource.WIDGET_USER)
.build();
ServerToServerAuthResponse auth = client.auth()
.serverToServer(request)
.join();
String accessToken = auth.getAccessToken();For external users, use UserSource.EXTERNAL_USER. You can also include MCP auth entries for tool authentication.
Admin Operations
All admin operations require an authenticated EvolvableAiAdmin client. Token refresh is handled automatically, or can be triggered manually:
admin.refreshToken().join();Tenants
Access via admin.tenants().
TenantOperations tenants = admin.tenants();
// List tenants (paginated)
PageResponse<TenantDto> page = tenants.list(0, 20).join();
page.getContent().forEach(t -> System.out.println(t.getName()));
// Create a tenant
CreateTenantRequest request = CreateTenantRequest.builder()
.name("Acme Corp")
.schema("acme")
.adminEmail("admin@acme.com")
.tokenLimit(100000L)
.expirationDays(365)
.build();
CreateTenantResponse created = tenants.create(request).join();
UUID tenantId = created.getTenantId();
// Deactivate a tenant
tenants.deactivate(tenantId).join();
// Deactivate all expired tenants
tenants.deactivateExpired().join();Settings
Access via admin.settings().
SettingsOperations settings = admin.settings();
// Get tenant settings
SettingsDto current = settings.get("my-tenant").join();
// Update tenant settings
SettingsDto updated = settings.update("my-tenant", modified).join();SettingsDto covers API keys, endpoints, RAG defaults, chat defaults, widget configuration, and rate limits.
LLM Servers
Access via admin.llmServers().
LlmServerOperations llmServers = admin.llmServers();
// Create a server
LlmServerCreateRequest request = LlmServerCreateRequest.builder()
.name("OpenAI Production")
.type(LlmServerType.OPENAI)
.url("https://api.openai.com")
.apiKey("sk-...")
.build();
LlmServerViewDto server = llmServers.create("my-tenant", request).join();
// List servers (paginated)
PageResponse<LlmServerViewDto> page = llmServers.list("my-tenant", 0, 10).join();
// Update a server
LlmServerViewDto updated = llmServers.update("my-tenant", server.getId(), updateRequest).join();
// Delete a server
llmServers.delete("my-tenant", server.getId()).join();
// List supported server types
LlmServerTypesViewDto types = llmServers.listTypes("my-tenant").join();Models
Access via admin.models().
ModelOperations models = admin.models();
// Sync models from configured LLM servers
String result = models.sync("my-tenant").join();
// List models with search/filter params
ModelSearchParams params = ModelSearchParams.builder()
.type(ModelType.CHAT)
.enabled(true)
.build();
PageResponse<ModelViewDto> page = models.list("my-tenant", params).join();
// Enable or disable a model
models.setEnabled("my-tenant", SetModelEnabledRequest.builder()
.modelId("model-id")
.enabled(true)
.build()).join();
// Update embedding settings
models.updateEmbeddingSettings("my-tenant", SetModelEmbeddingSettingsRequest.builder()
.modelId("model-id")
.dimensions(1536)
.distanceType(DistanceType.COSINE)
.build()).join();
// Delete a model
models.delete("my-tenant", DeleteModelRequest.builder()
.modelId("model-id")
.build()).join();Agencies
Access via admin.agencies().
Agencies are organizational groups that contain agents and members.
AgencyOperations agencies = admin.agencies();
// List agencies
PageResponse<AgencyViewListDto> page = agencies.list("my-tenant", "search-term", 0, 20).join();
// Get a single agency
AgencyViewDto agency = agencies.get("my-tenant", "agency-id").join();
// Create an agency
String agencyId = agencies.create("my-tenant", CreateAgencyRequest.builder()
.name("Support Team")
.description("Customer support agency")
.documentTags(List.of("support", "faq"))
.build()).join();
// Update an agency
agencies.update("my-tenant", agencyId, UpdateAgencyRequest.builder()
.name("Support Team v2")
.build()).join();
// Delete an agency
agencies.delete("my-tenant", agencyId).join();Agency Members & Admins
// List members
PageResponse<AgencyMemberDto> members = agencies
.listMembers("my-tenant", agencyId, null, 0, 20).join();
// List admins
PageResponse<AgencyAdminDto> admins = agencies
.listAdmins("my-tenant", agencyId, null, 0, 10).join();
// Add an admin
agencies.addAdmin("my-tenant", agencyId, AddAgencyMemberRequest.builder()
.userId("user-id-456")
.build()).join();
// Remove an admin
agencies.removeAdmin("my-tenant", agencyId, "user-id-456").join();Conversation Flows
// Get conversation flows
List<ConversationFlowDto> flows = agencies
.getConversationFlows("my-tenant", agencyId).join();
// Update conversation flows
agencies.updateConversationFlows("my-tenant", agencyId,
UpdateConversationFlowsRequest.builder()
.flows(updatedFlows)
.build()).join();Agents
Access via admin.agents().
Agents are AI assistants configured with instructions, tools, and guardrails.
CRUD
AgentOperations agents = admin.agents();
// List all agents in a tenant
List<AgentWithAgencyViewDto> all = agents.listAll("my-tenant", true).join(); // true = top-level only
// List agents in an agency (paginated)
PageResponse<AgentViewDto> page = agents
.listByAgency("my-tenant", agencyId, "search", 0, 20).join();
// Get a single agent
AgentViewDto agent = agents.get("my-tenant", "agent-id").join();
// Create an agent
String agentId = agents.create("my-tenant", agencyId, CreateUpdateAgentRequest.builder()
.name("Customer Support Bot")
.description("Handles customer inquiries")
.customInstructions("Always be polite and helpful.")
.build()).join();
// Create with an avatar image
String agentId = agents.create("my-tenant", agencyId, request,
imageBytes, "avatar.png").join();
// Update an agent
agents.update("my-tenant", agentId, CreateUpdateAgentRequest.builder()
.name("Updated Name")
.build()).join();
// Delete an agent
agents.delete("my-tenant", agentId).join();Tools
List<AgentToolDto> tools = agents.getTools("my-tenant", agentId).join();Events
// Add an event definition
AgentEventDefinitionDto event = agents.addEvent("my-tenant", agentId,
AgentAddEventRequest.builder()
.name("order_placed")
.description("Triggered when a customer places an order")
.build()).join();
// Remove an event
agents.removeEvent("my-tenant", agentId, event.getId()).join();MCP Servers
Connect external tool servers to an agent using the Model Context Protocol (MCP):
// Connect via HTTP
McpServerViewDto mcpServer = agents.connectMcpServerHttp("my-tenant", agentId,
ConnectMcpServerHttpRequest.builder()
.name("My Tools")
.url("https://tools.example.com/mcp")
.build()).join();
// Connect via SSE
agents.connectMcpServerSse("my-tenant", agentId,
ConnectMcpServerSseRequest.builder()
.name("SSE Tools")
.url("https://tools.example.com/sse")
.build()).join();
// Connect via STDIO
agents.connectMcpServerStdio("my-tenant", agentId,
ConnectMcpServerStdioRequest.builder()
.name("Local Tool")
.command("node /path/to/tool.js")
.build()).join();
// List connected MCP servers
List<McpServerViewDto> servers = agents.listMcpServers("my-tenant", agentId).join();
// Sync tools from an MCP server
McpServerViewDto synced = agents.syncMcpServer("my-tenant", agentId, mcpServer.getId()).join();
// Update MCP server config
agents.updateMcpServer("my-tenant", agentId, mcpServer.getId(),
UpdateMcpServerRequest.builder().name("Renamed").build()).join();
// Delete an MCP server connection
agents.deleteMcpServer("my-tenant", agentId, mcpServer.getId()).join();Guardrails
// List guardrails attached to an agent
List<AgentGuardrailViewResponse> guardrails = agents
.listGuardrails("my-tenant", agentId).join();
// Add a guardrail
AgentGuardrailIdResponse result = agents.addGuardrail("my-tenant", agentId,
AddGuardrailRequest.builder()
.guardrailId(42L)
.build()).join();
// Remove a guardrail
agents.removeGuardrail("my-tenant", agentId, result.getGuardrailId()).join();Evaluations
// Create an evaluation
CreateEvalResponse eval = agents.createEval("my-tenant", agentId,
CreateEvalRequest.builder()
.name("Response Quality Check")
.build()).join();
// Get evaluation details
AgentEvalViewDto evalDetails = agents.getEval("my-tenant", agentId, eval.getEvalId()).join();
// List evaluations (paginated)
PageResponse<AgentEvalViewDto> evals = agents.listEvals("my-tenant", agentId, 0, 10).join();
// Run an evaluation
ExecuteEvalResponse result = agents.executeEval("my-tenant", agentId, eval.getEvalId()).join();UI Coordinates
Used for positioning agents on a visual canvas:
agents.updateUiCoordinates("my-tenant", agentId,
UpdateAgentUiCoordinatesRequest.builder()
.x(100.0)
.y(200.0)
.build()).join();Spring Boot Integration
Configuration
evolvable:
ai:
base-url: https://your-api-endpoint.com
api-key: your-api-key
tenant: your-tenant-id
enabled: true
connection-timeout: 30s
read-timeout: 60s
write-timeout: 60s
debug: false
admin:
enabled: true
username: admin-username
password: admin-passwordUsage
Inject the auto-configured beans:
@Service
public class MyService {
private final EvolvableAiClient client;
private final EvolvableAiAdmin admin; // inject only if admin is enabled
public MyService(EvolvableAiClient client, EvolvableAiAdmin admin) {
this.client = client;
this.admin = admin;
}
public String chat(String message) {
return client.chat()
.send(ChatRequest.simple(message))
.thenApply(Message::getValue)
.join();
}
}Error Handling
All exceptions extend EvolvableAiException (a RuntimeException):
| Exception | Cause |
|---|---|
ApiException | Non-2xx HTTP response from the API |
AuthenticationException | Invalid credentials or expired token |
NetworkException | Connection failure or timeout |
try {
Message response = client.chat().send(request).join();
} catch (ApiException e) {
System.err.println("API error " + e.getStatusCode() + ": " + e.getMessage());
} catch (AuthenticationException e) {
System.err.println("Auth failed: " + e.getMessage());
} catch (NetworkException e) {
System.err.println("Network error: " + e.getMessage());
} catch (EvolvableAiException e) {
System.err.println("SDK error: " + e.getMessage());
}When using CompletableFuture without .join():
client.chat().send(request)
.thenAccept(msg -> System.out.println(msg.getValue()))
.exceptionally(ex -> {
Throwable cause = ex.getCause(); // unwrap CompletionException
if (cause instanceof ApiException apiEx) {
// handle API error
}
return null;
});Data Models Reference
Enums
| Enum | Values |
|---|---|
MessageType | Bot, Human |
UserSource | WIDGET_USER, EXTERNAL_USER |
ModelType | CHAT, EMBEDDING |
LlmServerType | OPENAI, ANTHROPIC, and others |
LlmServerAuthorizationType | BEARER_TOKEN, API_KEY_HEADER, and others |
McpTransport | STDIO, HTTP, SSE |
McpAuthType | ENV, STATIC, DYNAMIC |
DistanceType | COSINE, EUCLIDEAN, and others |
StreamingEvent | START, CHUNK, OPTION, EVENT, END, ERROR, TITLE, INFO |
PageResponse<T>
All paginated endpoints return PageResponse<T>:
| Field | Type | Description |
|---|---|---|
content | List<T> | Items on the current page |
pageNumber | int | Current page (0-indexed) |
pageSize | int | Items per page |
totalElements | long | Total item count |
totalPages | int | Total number of pages |
Resource Cleanup
Close clients when done to release underlying HTTP connections:
client.close();
admin.close();In Spring Boot, beans are closed automatically on application shutdown.