Java SDK
Overview
The iFlow SDK is an SDK for programmatic interaction with iFlow CLI. It can be quickly integrated into your business system, combining with workflow and other intelligent agent extensions to enable developers to build AI-driven applications with conversation, tool execution, and task planning capabilities, giving business systems AI capabilities. Currently supports Python, Java, TypeScript, and Android versions.
✨ Core Feature: SDK automatically manages iFlow processes - no manual configuration needed!
Built on Project Reactor, it supports modern reactive programming, providing Java developers with powerful asynchronous processing capabilities.
System Requirements
- Java: 11 or higher
- iFlow CLI: 0.2.24 or higher
- Operating System: Windows, macOS, Linux
Installation
1. Install iFlow CLI
If you haven't installed iFlow CLI yet:
- Mac/Linux/Ubuntu
- Windows
bash -c "$(curl -fsSL https://gitee.com/iflow-ai/iflow-cli/raw/main/install.sh)"
npm install -g @iflow-ai/iflow-cli@latest
2. Add SDK Dependency
- Maven
- Gradle
<dependency>
<groupId>cn.iflow</groupId>
<artifactId>iflow-cli-sdk</artifactId>
<version>1.0.4-fix</version>
</dependency>
implementation 'cn.iflow:iflow-cli-sdk:1.0.4-fix'
Quick Start
Basic Example
The SDK automatically detects and starts iFlow processes without manual configuration:
import cn.iflow.sdk.core.IFlowClient;
import cn.iflow.sdk.types.messages.*;
import reactor.core.publisher.Flux;
public class BasicExample {
public static void main(String[] args) throws Exception {
// SDK automatically handles:
// 1. Detect if iFlow is installed
// 2. Start iFlow process (if not running)
// 3. Find available port and establish connection
// 4. Automatically clean up resources on exit
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Hello, iFlow!").block();
// Use blockLast() to wait for stream completion, avoiding premature resource closure
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof TaskFinishMessage) {
System.out.println("\nConversation completed");
}
})
.doOnError(error -> error.printStackTrace())
.doOnComplete(() -> System.out.println("Stream ended"))
.blockLast(); // Block until stream completes
}
}
}
Simple Query
The simplest way to use the SDK is through the IFlowQuery utility class:
import cn.iflow.sdk.query.IFlowQuery;
import cn.iflow.sdk.types.messages.Message;
import java.util.List;
public class SimpleQuery {
public static void main(String[] args) throws Exception {
// Synchronous query
List<Message> response = IFlowQuery.querySync("What is the capital of France?");
for (Message msg : response) {
if (msg instanceof AssistantMessage) {
System.out.println(((AssistantMessage) msg).getChunk().getText());
}
}
}
}
Core Concepts
IFlowClient
IFlowClient is the main interface for interacting with iFlow CLI, built on Project Reactor:
import cn.iflow.sdk.core.IFlowClient;
import cn.iflow.sdk.types.config.IFlowOptions;
import cn.iflow.sdk.types.enums.PermissionMode;
import java.time.Duration;
// Using default configuration (automatic process management)
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Your question").block();
// Process messages...
}
// Using custom configuration
IFlowOptions options = IFlowOptions.builder()
.url("ws://localhost:8090/acp") // WebSocket URL
.autoStartProcess(true) // Auto-start iFlow
.timeout(Duration.ofSeconds(30)) // Timeout duration
.permissionMode(PermissionMode.AUTO) // Tool call permission mode
.build();
try (IFlowClient client = IFlowClient.create(options)) {
client.connect().block();
client.sendMessage("Your question").block();
// Process messages...
}
Reactive Programming Support
The SDK is built on Project Reactor, supporting Mono and Flux:
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
try (IFlowClient client = IFlowClient.create()) {
// Mono<Void> - single operation
Mono<Void> connectionMono = client.connect();
Mono<Void> sendMono = client.sendMessage("Hello");
// Flux<Message> - message stream
Flux<Message> messageFlux = client.receiveMessages();
// Chain operations
connectionMono
.then(sendMono)
.then(messageFlux.take(10).collectList())
.subscribe(messages -> {
// Process message list
});
}
Message Types
The SDK supports multiple message types corresponding to different iFlow protocol responses:
AssistantMessage - AI Assistant Response
import cn.iflow.sdk.types.messages.AssistantMessage;
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Please introduce Java").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
// Access agent info (if available)
if (assistantMsg.getAgentInfo() != null) {
System.out.println("Agent ID: " + assistantMsg.getAgentInfo().getAgentId());
if (assistantMsg.getAgentInfo().getTaskId() != null) {
System.out.println("Task ID: " + assistantMsg.getAgentInfo().getTaskId());
}
}
} else if (message instanceof TaskFinishMessage) {
System.out.println("\nResponse completed");
}
})
.blockLast(); // Wait for stream completion
}
ToolCallMessage - Tool Execution
import cn.iflow.sdk.types.messages.ToolCallMessage;
import cn.iflow.sdk.types.enums.ToolCallStatus;
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("List files in current directory").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof ToolCallMessage) {
ToolCallMessage toolCall = (ToolCallMessage) message;
System.out.println("Tool call status: " + toolCall.getStatus());
System.out.println("Tool title: " + toolCall.getTitle());
// Access tool call content
if (toolCall.getContent() != null) {
System.out.println("Tool content: " + toolCall.getContent());
}
// Access agent info
if (toolCall.getAgentInfo() != null) {
System.out.println("Agent ID: " + toolCall.getAgentInfo().getAgentId());
}
} else if (message instanceof TaskFinishMessage) {
System.out.println("Tool execution completed");
}
})
.blockLast(); // Wait for stream completion
}
PlanMessage - Task Planning
import cn.iflow.sdk.types.messages.PlanMessage;
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Help me create a Java project structure").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof PlanMessage) {
PlanMessage planMsg = (PlanMessage) message;
System.out.println("Execution plan:");
planMsg.getEntries().forEach(entry -> {
String statusIcon = "completed".equals(entry.getStatus()) ? "✅" : "⏳";
System.out.printf("%s [%s] %s%n",
statusIcon, entry.getPriority(), entry.getContent());
});
} else if (message instanceof TaskFinishMessage) {
System.out.println("Planning completed");
}
})
.blockLast(); // Wait for stream completion
}
TaskFinishMessage - Task Completion
import cn.iflow.sdk.types.messages.TaskFinishMessage;
import cn.iflow.sdk.types.enums.StopReason;
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Calculate 1+1").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof TaskFinishMessage) {
TaskFinishMessage finishMsg = (TaskFinishMessage) message;
System.out.println(); // New line
if (finishMsg.getStopReason() == StopReason.END_TURN) {
System.out.println("Task completed normally");
} else if (finishMsg.getStopReason() == StopReason.MAX_TOKENS) {
System.out.println("Reached maximum token limit");
}
// TaskFinishMessage indicates conversation end
}
})
.blockLast(); // Wait for stream completion
}
Common Use Cases
Interactive Chatbot
import java.util.Scanner;
public class ChatBot {
public static void main(String[] args) throws Exception {
System.out.println("iFlow Chatbot (type 'quit' to exit)");
System.out.println("-".repeat(50));
Scanner scanner = new Scanner(System.in);
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
while (true) {
System.out.print("\nYou: ");
String userInput = scanner.nextLine();
if ("quit".equalsIgnoreCase(userInput) ||
"exit".equalsIgnoreCase(userInput) ||
"q".equalsIgnoreCase(userInput)) {
System.out.println("Goodbye!");
break;
}
client.sendMessage(userInput).block();
System.out.print("iFlow: ");
client.receiveMessages()
.takeUntil(msg -> msg instanceof TaskFinishMessage)
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof TaskFinishMessage) {
System.out.println(); // New line
}
})
.blockLast(); // Wait for current conversation to complete
}
}
}
}
Tool Call Control
import cn.iflow.sdk.types.config.IFlowOptions;
import cn.iflow.sdk.types.enums.PermissionMode;
public class ToolControlExample {
public static void main(String[] args) throws Exception {
IFlowOptions options = IFlowOptions.builder()
.permissionMode(PermissionMode.AUTO) // Auto-approve tool calls
.fileAccess(true) // Enable file system access
.fileReadOnly(false) // Allow file writes
.build();
try (IFlowClient client = IFlowClient.create(options)) {
client.connect().block();
client.sendMessage("Create a file named test.txt with some content").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof ToolCallMessage) {
ToolCallMessage toolCall = (ToolCallMessage) message;
System.out.println("Tool request: " + toolCall.getTitle());
} else if (message instanceof TaskFinishMessage) {
System.out.println("\nTask completed");
}
})
.blockLast(); // Wait for stream completion
}
}
}
Advanced Configuration
Manual Process Management
If you need to manually manage the iFlow process:
import cn.iflow.sdk.types.config.IFlowOptions;
public class ManualProcessExample {
public static void main(String[] args) throws Exception {
// Disable automatic process management
IFlowOptions options = IFlowOptions.builder()
.autoStartProcess(false)
.url("ws://localhost:8090/acp") // Connect to existing iFlow
.build();
try (IFlowClient client = IFlowClient.create(options)) {
client.connect().block();
client.sendMessage("Your question").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof TaskFinishMessage) {
System.out.println("\nConversation completed");
}
})
.blockLast(); // Wait for stream completion
}
}
}
Note Manual mode requires you to start iFlow separately:
iflow --experimental-acp --port 8090
Error Handling
The SDK provides detailed error handling mechanisms:
import cn.iflow.sdk.exceptions.*;
public class ErrorHandlingExample {
public static void main(String[] args) {
try (IFlowClient client = IFlowClient.create()) {
client.connect().block();
client.sendMessage("Test").block();
client.receiveMessages()
.doOnNext(message -> {
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
System.out.print(assistantMsg.getChunk().getText());
} else if (message instanceof TaskFinishMessage) {
System.out.println("\nTask completed");
}
})
.doOnError(error -> {
if (error instanceof ConnectionException) {
System.err.println("Connection error: " + error.getMessage());
} else if (error instanceof TimeoutException) {
System.err.println("Timeout error: " + error.getMessage());
} else {
System.err.println("Unknown error: " + error.getMessage());
}
})
.blockLast(); // Wait for stream completion
} catch (ConnectionException e) {
System.err.println("Connection error: " + e.getMessage());
} catch (Exception e) {
System.err.println("Unknown error: " + e.getMessage());
}
}
}
Synchronous Query Utility
For scenarios requiring synchronous calls, use the query utility class:
import cn.iflow.sdk.query.IFlowQuery;
import cn.iflow.sdk.types.config.IFlowOptions;
import java.time.Duration;
import java.util.List;
public class SyncQueryExample {
public static void main(String[] args) throws Exception {
// Synchronous call with timeout control
IFlowOptions options = IFlowOptions.builder()
.timeout(Duration.ofSeconds(30))
.build();
List<Message> response = IFlowQuery.querySync("Your question", options);
for (Message msg : response) {
if (msg instanceof AssistantMessage) {
System.out.println(((AssistantMessage) msg).getChunk().getText());
}
}
}
}
API Reference
Core Classes
| Class Name | Description |
|---|---|
IFlowClient | Main client class for managing iFlow connections |
IFlowOptions | Configuration options class using builder pattern |
IFlowQuery | Query utility class providing sync and async methods |
IFlowOptions Parameters
The IFlowOptions class uses builder pattern to configure iFlow client behavior:
Core Connection Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
url | String | "ws://localhost:8090/acp" | WebSocket connection URL |
autoStartProcess | boolean | true | Whether to auto-start iFlow process. Set to false for manual iFlow startup |
processStartPort | int | 8090 | Starting port number when auto-starting iFlow process |
timeout | Duration | 30 seconds | Connection and operation timeout |
cwd | String | Current directory | Working directory for CLI operations |
Authentication Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
authMethodId | String | "iflow" | Authentication method ID (e.g., "use_iflow_ak") |
authMethodInfo | AuthMethodInfo | null | Authentication method info (containing apiKey, baseUrl, modelName, etc.) |
Tool Execution Control Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
permissionMode | PermissionMode | MANUAL | Tool execution permission mode: AUTO(auto-execute), MANUAL(require confirmation), SELECTIVE(selective auto) |
approvalMode | ApprovalMode | DEFAULT | Protocol-level approval mode: DEFAULT(standard confirmation), AUTO_EDIT(auto-edit), YOLO(auto-execute), PLAN(plan mode) |
autoApproveTypes | List<String> | [] | List of tool types to auto-approve in selective mode |
File System Access Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
fileAccess | boolean | false | Whether to enable file system access (disabled by default for security) |
fileAllowedDirs | List<String> | null | List of directories iFlow is allowed to access. null means current directory only |
fileReadOnly | boolean | false | Whether to allow read operations only (write operations disabled when enabled) |
fileMaxSize | long | 10485760 | Maximum file size for read operations (bytes), default 10MB |
MCP Server Support
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpServers | List<McpServerConfig> | [] | List of MCP server configurations, supporting multiple transport types |
Message Types
| Message Type | Description | Main Properties |
|---|---|---|
AssistantMessage | AI assistant text response | chunk.text, agentId, timestamp |
ToolCallMessage | Tool execution request and status | label, status, toolName, content, agentId |
PlanMessage | Structured task plan | entries (containing content, priority, status) |
TaskFinishMessage | Task completion signal | stopReason, timestamp |
UserMessage | User input message | chunk.text, timestamp |
ToolResultMessage | Tool execution result | toolCallId, content, agentId |
ErrorMessage | Error information | errorMessage, timestamp |
AgentInfo - Agent Information
The AgentInfo class provides detailed information parsed from iFlow agent IDs:
| Property | Type | Description |
|---|---|---|
agentId | String | Complete agent ID |
agentIndex | Integer | Agent index in task |
taskId | String | Task or instance ID |
timestamp | Long | Creation timestamp |
AgentInfo Usage Example
import cn.iflow.sdk.types.protocol.AgentInfo;
// Get agent info from message
if (message instanceof AssistantMessage) {
AssistantMessage assistantMsg = (AssistantMessage) message;
AgentInfo agentInfo = assistantMsg.getAgentInfo();
if (agentInfo != null) {
System.out.println("Agent ID: " + agentInfo.getAgentId());
System.out.println("Task ID: " + agentInfo.getTaskId());
System.out.println("Agent Index: " + agentInfo.getAgentIndex());
System.out.println("Timestamp: " + agentInfo.getTimestamp());
}
}
Troubleshooting
Connection Issues
If you encounter connection errors, check:
-
Is iFlow installed
iflow --version -
Is the port occupied
- Linux/Mac
- Windows
lsof -i :8090netstat -an | findstr :8090 -
Manually test connection
iflow --experimental-acp --port 8090
Timeout Issues
For long-running tasks, increase the timeout:
import java.time.Duration;
IFlowOptions options = IFlowOptions.builder()
.timeout(Duration.ofMinutes(10)) // 10 minute timeout
.build();
IFlowClient client = IFlowClient.create(options);
Debug Logging
Enable detailed logging for debugging:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DebuggingExample {
private static final Logger logger = LoggerFactory.getLogger(DebuggingExample.class);
public static void main(String[] args) throws Exception {
// Configure log level to DEBUG in logback.xml or log4j2.xml
try (IFlowClient client = IFlowClient.create()) {
logger.debug("Starting connection to iFlow");
client.connect().block();
logger.debug("Connection successful");
// Other operations...
}
}
}
Maven Build Configuration
Ensure your pom.xml contains correct Java version configuration:
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>