Skip to main content

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:

bash -c "$(curl -fsSL https://gitee.com/iflow-ai/iflow-cli/raw/main/install.sh)"

2. Add SDK Dependency

<dependency>
<groupId>cn.iflow</groupId>
<artifactId>iflow-cli-sdk</artifactId>
<version>1.0.4-fix</version>
</dependency>

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 NameDescription
IFlowClientMain client class for managing iFlow connections
IFlowOptionsConfiguration options class using builder pattern
IFlowQueryQuery utility class providing sync and async methods

IFlowOptions Parameters

The IFlowOptions class uses builder pattern to configure iFlow client behavior:

Core Connection Parameters
ParameterTypeDefaultDescription
urlString"ws://localhost:8090/acp"WebSocket connection URL
autoStartProcessbooleantrueWhether to auto-start iFlow process. Set to false for manual iFlow startup
processStartPortint8090Starting port number when auto-starting iFlow process
timeoutDuration30 secondsConnection and operation timeout
cwdStringCurrent directoryWorking directory for CLI operations
Authentication Parameters
ParameterTypeDefaultDescription
authMethodIdString"iflow"Authentication method ID (e.g., "use_iflow_ak")
authMethodInfoAuthMethodInfonullAuthentication method info (containing apiKey, baseUrl, modelName, etc.)
Tool Execution Control Parameters
ParameterTypeDefaultDescription
permissionModePermissionModeMANUALTool execution permission mode: AUTO(auto-execute), MANUAL(require confirmation), SELECTIVE(selective auto)
approvalModeApprovalModeDEFAULTProtocol-level approval mode: DEFAULT(standard confirmation), AUTO_EDIT(auto-edit), YOLO(auto-execute), PLAN(plan mode)
autoApproveTypesList<String>[]List of tool types to auto-approve in selective mode
File System Access Parameters
ParameterTypeDefaultDescription
fileAccessbooleanfalseWhether to enable file system access (disabled by default for security)
fileAllowedDirsList<String>nullList of directories iFlow is allowed to access. null means current directory only
fileReadOnlybooleanfalseWhether to allow read operations only (write operations disabled when enabled)
fileMaxSizelong10485760Maximum file size for read operations (bytes), default 10MB
MCP Server Support
ParameterTypeDefaultDescription
mcpServersList<McpServerConfig>[]List of MCP server configurations, supporting multiple transport types

Message Types

Message TypeDescriptionMain Properties
AssistantMessageAI assistant text responsechunk.text, agentId, timestamp
ToolCallMessageTool execution request and statuslabel, status, toolName, content, agentId
PlanMessageStructured task planentries (containing content, priority, status)
TaskFinishMessageTask completion signalstopReason, timestamp
UserMessageUser input messagechunk.text, timestamp
ToolResultMessageTool execution resulttoolCallId, content, agentId
ErrorMessageError informationerrorMessage, timestamp

AgentInfo - Agent Information

The AgentInfo class provides detailed information parsed from iFlow agent IDs:

PropertyTypeDescription
agentIdStringComplete agent ID
agentIndexIntegerAgent index in task
taskIdStringTask or instance ID
timestampLongCreation 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:

  1. Is iFlow installed

    iflow --version
  2. Is the port occupied

    lsof -i :8090
  3. 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>