|
| 1 | +# Qwen Code Java SDK |
| 2 | + |
| 3 | +The Qwen Code Java SDK is a minimum experimental SDK for programmatic access to Qwen Code functionality. It provides a Java interface to interact with the Qwen Code CLI, allowing developers to integrate Qwen Code capabilities into their Java applications. |
| 4 | + |
| 5 | +## Requirements |
| 6 | + |
| 7 | +- Java >= 1.8 |
| 8 | +- Maven >= 3.6.0 (for building from source) |
| 9 | +- qwen-code >= 0.5.0 |
| 10 | + |
| 11 | +### Dependencies |
| 12 | + |
| 13 | +- **Logging**: ch.qos.logback:logback-classic |
| 14 | +- **Utilities**: org.apache.commons:commons-lang3 |
| 15 | +- **JSON Processing**: com.alibaba.fastjson2:fastjson2 |
| 16 | +- **Testing**: JUnit 5 (org.junit.jupiter:junit-jupiter) |
| 17 | + |
| 18 | +## Installation |
| 19 | + |
| 20 | +Add the following dependency to your Maven `pom.xml`: |
| 21 | + |
| 22 | +```xml |
| 23 | +<dependency> |
| 24 | + <groupId>com.alibaba</groupId> |
| 25 | + <artifactId>qwencode-sdk</artifactId> |
| 26 | + <version>{$version}</version> |
| 27 | +</dependency> |
| 28 | +``` |
| 29 | + |
| 30 | +Or if using Gradle, add to your `build.gradle`: |
| 31 | + |
| 32 | +```gradle |
| 33 | +implementation 'com.alibaba:qwencode-sdk:{$version}' |
| 34 | +``` |
| 35 | + |
| 36 | +## Building and Running |
| 37 | + |
| 38 | +### Build Commands |
| 39 | + |
| 40 | +```bash |
| 41 | +# Compile the project |
| 42 | +mvn compile |
| 43 | + |
| 44 | +# Run tests |
| 45 | +mvn test |
| 46 | + |
| 47 | +# Package the JAR |
| 48 | +mvn package |
| 49 | + |
| 50 | +# Install to local repository |
| 51 | +mvn install |
| 52 | +``` |
| 53 | + |
| 54 | +## Quick Start |
| 55 | + |
| 56 | +The simplest way to use the SDK is through the `QwenCodeCli.simpleQuery()` method: |
| 57 | + |
| 58 | +```java |
| 59 | +public static void runSimpleExample() { |
| 60 | + List<String> result = QwenCodeCli.simpleQuery("hello world"); |
| 61 | + result.forEach(logger::info); |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +For more advanced usage with custom transport options: |
| 66 | + |
| 67 | +```java |
| 68 | +public static void runTransportOptionsExample() { |
| 69 | + TransportOptions options = new TransportOptions() |
| 70 | + .setModel("qwen3-coder-flash") |
| 71 | + .setPermissionMode(PermissionMode.AUTO_EDIT) |
| 72 | + .setCwd("./") |
| 73 | + .setEnv(new HashMap<String, String>() {{put("CUSTOM_VAR", "value");}}) |
| 74 | + .setIncludePartialMessages(true) |
| 75 | + .setTurnTimeout(new Timeout(120L, TimeUnit.SECONDS)) |
| 76 | + .setMessageTimeout(new Timeout(90L, TimeUnit.SECONDS)) |
| 77 | + .setAllowedTools(Arrays.asList("read_file", "write_file", "list_directory")); |
| 78 | + |
| 79 | + List<String> result = QwenCodeCli.simpleQuery("who are you, what are your capabilities?", options); |
| 80 | + result.forEach(logger::info); |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +For streaming content handling with custom content consumers: |
| 85 | + |
| 86 | +```java |
| 87 | +public static void runStreamingExample() { |
| 88 | + QwenCodeCli.simpleQuery("who are you, what are your capabilities?", |
| 89 | + new TransportOptions().setMessageTimeout(new Timeout(10L, TimeUnit.SECONDS)), new AssistantContentSimpleConsumers() { |
| 90 | + |
| 91 | + @Override |
| 92 | + public void onText(Session session, TextAssistantContent textAssistantContent) { |
| 93 | + logger.info("Text content received: {}", textAssistantContent.getText()); |
| 94 | + } |
| 95 | + |
| 96 | + @Override |
| 97 | + public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) { |
| 98 | + logger.info("Thinking content received: {}", thingkingAssistantContent.getThinking()); |
| 99 | + } |
| 100 | + |
| 101 | + @Override |
| 102 | + public void onToolUse(Session session, ToolUseAssistantContent toolUseContent) { |
| 103 | + logger.info("Tool use content received: {} with arguments: {}", |
| 104 | + toolUseContent, toolUseContent.getInput()); |
| 105 | + } |
| 106 | + |
| 107 | + @Override |
| 108 | + public void onToolResult(Session session, ToolResultAssistantContent toolResultContent) { |
| 109 | + logger.info("Tool result content received: {}", toolResultContent.getContent()); |
| 110 | + } |
| 111 | + |
| 112 | + @Override |
| 113 | + public void onOtherContent(Session session, AssistantContent<?> other) { |
| 114 | + logger.info("Other content received: {}", other); |
| 115 | + } |
| 116 | + |
| 117 | + @Override |
| 118 | + public void onUsage(Session session, AssistantUsage assistantUsage) { |
| 119 | + logger.info("Usage information received: Input tokens: {}, Output tokens: {}", |
| 120 | + assistantUsage.getUsage().getInputTokens(), assistantUsage.getUsage().getOutputTokens()); |
| 121 | + } |
| 122 | + }.setDefaultPermissionOperation(Operation.allow)); |
| 123 | + logger.info("Streaming example completed."); |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +other examples see src/test/java/com/alibaba/qwen/code/cli/example |
| 128 | + |
| 129 | +## Architecture |
| 130 | + |
| 131 | +The SDK follows a layered architecture: |
| 132 | + |
| 133 | +- **API Layer**: Provides the main entry points through `QwenCodeCli` class with simple static methods for basic usage |
| 134 | +- **Session Layer**: Manages communication sessions with the Qwen Code CLI through the `Session` class |
| 135 | +- **Transport Layer**: Handles the communication mechanism between the SDK and CLI process (currently using process transport via `ProcessTransport`) |
| 136 | +- **Protocol Layer**: Defines data structures for communication based on the CLI protocol |
| 137 | +- **Utils**: Common utilities for concurrent execution, timeout handling, and error management |
| 138 | + |
| 139 | +## Key Features |
| 140 | + |
| 141 | +### Permission Modes |
| 142 | + |
| 143 | +The SDK supports different permission modes for controlling tool execution: |
| 144 | + |
| 145 | +- **`default`**: Write tools are denied unless approved via `canUseTool` callback or in `allowedTools`. Read-only tools execute without confirmation. |
| 146 | +- **`plan`**: Blocks all write tools, instructing AI to present a plan first. |
| 147 | +- **`auto-edit`**: Auto-approve edit tools (edit, write_file) while other tools require confirmation. |
| 148 | +- **`yolo`**: All tools execute automatically without confirmation. |
| 149 | + |
| 150 | +### Session Event Consumers and Assistant Content Consumers |
| 151 | + |
| 152 | +The SDK provides two key interfaces for handling events and content from the CLI: |
| 153 | + |
| 154 | +#### SessionEventConsumers Interface |
| 155 | + |
| 156 | +The `SessionEventConsumers` interface provides callbacks for different types of messages during a session: |
| 157 | + |
| 158 | +- `onSystemMessage`: Handles system messages from the CLI (receives Session and SDKSystemMessage) |
| 159 | +- `onResultMessage`: Handles result messages from the CLI (receives Session and SDKResultMessage) |
| 160 | +- `onAssistantMessage`: Handles assistant messages (AI responses) (receives Session and SDKAssistantMessage) |
| 161 | +- `onPartialAssistantMessage`: Handles partial assistant messages during streaming (receives Session and SDKPartialAssistantMessage) |
| 162 | +- `onUserMessage`: Handles user messages (receives Session and SDKUserMessage) |
| 163 | +- `onOtherMessage`: Handles other types of messages (receives Session and String message) |
| 164 | +- `onControlResponse`: Handles control responses (receives Session and CLIControlResponse) |
| 165 | +- `onControlRequest`: Handles control requests (receives Session and CLIControlRequest, returns CLIControlResponse) |
| 166 | +- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlRequest<CLIControlPermissionRequest>, returns Behavior) |
| 167 | + |
| 168 | +#### AssistantContentConsumers Interface |
| 169 | + |
| 170 | +The `AssistantContentConsumers` interface handles different types of content within assistant messages: |
| 171 | + |
| 172 | +- `onText`: Handles text content (receives Session and TextAssistantContent) |
| 173 | +- `onThinking`: Handles thinking content (receives Session and ThingkingAssistantContent) |
| 174 | +- `onToolUse`: Handles tool use content (receives Session and ToolUseAssistantContent) |
| 175 | +- `onToolResult`: Handles tool result content (receives Session and ToolResultAssistantContent) |
| 176 | +- `onOtherContent`: Handles other content types (receives Session and AssistantContent) |
| 177 | +- `onUsage`: Handles usage information (receives Session and AssistantUsage) |
| 178 | +- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlPermissionRequest, returns Behavior) |
| 179 | +- `onOtherControlRequest`: Handles other control requests (receives Session and ControlRequestPayload, returns ControlResponsePayload) |
| 180 | + |
| 181 | +#### Relationship Between the Interfaces |
| 182 | + |
| 183 | +**Important Note on Event Hierarchy:** |
| 184 | + |
| 185 | +- `SessionEventConsumers` is the **high-level** event processor that handles different message types (system, assistant, user, etc.) |
| 186 | +- `AssistantContentConsumers` is the **low-level** content processor that handles different types of content within assistant messages (text, tools, thinking, etc.) |
| 187 | + |
| 188 | +**Processor Relationship:** |
| 189 | + |
| 190 | +- `SessionEventConsumers` → `AssistantContentConsumers` (SessionEventConsumers uses AssistantContentConsumers to process content within assistant messages) |
| 191 | + |
| 192 | +**Event Derivation Relationships:** |
| 193 | + |
| 194 | +- `onAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent`, `onUsage` |
| 195 | +- `onPartialAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent` |
| 196 | +- `onControlRequest` → `onPermissionRequest`, `onOtherControlRequest` |
| 197 | + |
| 198 | +**Event Timeout Relationships:** |
| 199 | + |
| 200 | +Each event handler method has a corresponding timeout method that allows customizing the timeout behavior for that specific event: |
| 201 | + |
| 202 | +- `onSystemMessage` ↔ `onSystemMessageTimeout` |
| 203 | +- `onResultMessage` ↔ `onResultMessageTimeout` |
| 204 | +- `onAssistantMessage` ↔ `onAssistantMessageTimeout` |
| 205 | +- `onPartialAssistantMessage` ↔ `onPartialAssistantMessageTimeout` |
| 206 | +- `onUserMessage` ↔ `onUserMessageTimeout` |
| 207 | +- `onOtherMessage` ↔ `onOtherMessageTimeout` |
| 208 | +- `onControlResponse` ↔ `onControlResponseTimeout` |
| 209 | +- `onControlRequest` ↔ `onControlRequestTimeout` |
| 210 | + |
| 211 | +For AssistantContentConsumers timeout methods: |
| 212 | + |
| 213 | +- `onText` ↔ `onTextTimeout` |
| 214 | +- `onThinking` ↔ `onThinkingTimeout` |
| 215 | +- `onToolUse` ↔ `onToolUseTimeout` |
| 216 | +- `onToolResult` ↔ `onToolResultTimeout` |
| 217 | +- `onOtherContent` ↔ `onOtherContentTimeout` |
| 218 | +- `onPermissionRequest` ↔ `onPermissionRequestTimeout` |
| 219 | +- `onOtherControlRequest` ↔ `onOtherControlRequestTimeout` |
| 220 | + |
| 221 | +**Default Timeout Values:** |
| 222 | + |
| 223 | +- `SessionEventSimpleConsumers` default timeout: 180 seconds (Timeout.TIMEOUT_180_SECONDS) |
| 224 | +- `AssistantContentSimpleConsumers` default timeout: 60 seconds (Timeout.TIMEOUT_60_SECONDS) |
| 225 | + |
| 226 | +**Timeout Hierarchy Requirements:** |
| 227 | + |
| 228 | +For proper operation, the following timeout relationships should be maintained: |
| 229 | + |
| 230 | +- `onAssistantMessageTimeout` return value should be greater than `onTextTimeout`, `onThinkingTimeout`, `onToolUseTimeout`, `onToolResultTimeout`, and `onOtherContentTimeout` return values |
| 231 | +- `onControlRequestTimeout` return value should be greater than `onPermissionRequestTimeout` and `onOtherControlRequestTimeout` return values |
| 232 | + |
| 233 | +### Transport Options |
| 234 | + |
| 235 | +The `TransportOptions` class allows configuration of how the SDK communicates with the Qwen Code CLI: |
| 236 | + |
| 237 | +- `pathToQwenExecutable`: Path to the Qwen Code CLI executable |
| 238 | +- `cwd`: Working directory for the CLI process |
| 239 | +- `model`: AI model to use for the session |
| 240 | +- `permissionMode`: Permission mode that controls tool execution |
| 241 | +- `env`: Environment variables to pass to the CLI process |
| 242 | +- `maxSessionTurns`: Limits the number of conversation turns in a session |
| 243 | +- `coreTools`: List of core tools that should be available to the AI |
| 244 | +- `excludeTools`: List of tools to exclude from being available to the AI |
| 245 | +- `allowedTools`: List of tools that are pre-approved for use without additional confirmation |
| 246 | +- `authType`: Authentication type to use for the session |
| 247 | +- `includePartialMessages`: Enables receiving partial messages during streaming responses |
| 248 | +- `skillsEnable`: Enables or disables skills functionality for the session |
| 249 | +- `turnTimeout`: Timeout for a complete turn of conversation |
| 250 | +- `messageTimeout`: Timeout for individual messages within a turn |
| 251 | +- `resumeSessionId`: ID of a previous session to resume |
| 252 | +- `otherOptions`: Additional command-line options to pass to the CLI |
| 253 | + |
| 254 | +### Session Control Features |
| 255 | + |
| 256 | +- **Session creation**: Use `QwenCodeCli.newSession()` to create a new session with custom options |
| 257 | +- **Session management**: The `Session` class provides methods to send prompts, handle responses, and manage session state |
| 258 | +- **Session cleanup**: Always close sessions using `session.close()` to properly terminate the CLI process |
| 259 | +- **Session resumption**: Use `setResumeSessionId()` in `TransportOptions` to resume a previous session |
| 260 | +- **Session interruption**: Use `session.interrupt()` to interrupt a currently running prompt |
| 261 | +- **Dynamic model switching**: Use `session.setModel()` to change the model during a session |
| 262 | +- **Dynamic permission mode switching**: Use `session.setPermissionMode()` to change the permission mode during a session |
| 263 | + |
| 264 | +### Thread Pool Configuration |
| 265 | + |
| 266 | +The SDK uses a thread pool for managing concurrent operations with the following default configuration: |
| 267 | + |
| 268 | +- **Core Pool Size**: 30 threads |
| 269 | +- **Maximum Pool Size**: 100 threads |
| 270 | +- **Keep-Alive Time**: 60 seconds |
| 271 | +- **Queue Capacity**: 300 tasks (using LinkedBlockingQueue) |
| 272 | +- **Thread Naming**: "qwen_code_cli-pool-{number}" |
| 273 | +- **Daemon Threads**: false |
| 274 | +- **Rejected Execution Handler**: CallerRunsPolicy |
| 275 | + |
| 276 | +## Error Handling |
| 277 | + |
| 278 | +The SDK provides specific exception types for different error scenarios: |
| 279 | + |
| 280 | +- `SessionControlException`: Thrown when there's an issue with session control (creation, initialization, etc.) |
| 281 | +- `SessionSendPromptException`: Thrown when there's an issue sending a prompt or receiving a response |
| 282 | +- `SessionClosedException`: Thrown when attempting to use a closed session |
| 283 | + |
| 284 | +## FAQ / Troubleshooting |
| 285 | + |
| 286 | +### Q: Do I need to install the Qwen CLI separately? |
| 287 | + |
| 288 | +A: yes, requires Qwen CLI 0.5.5 or higher. |
| 289 | + |
| 290 | +### Q: What Java versions are supported? |
| 291 | + |
| 292 | +A: The SDK requires Java 1.8 or higher. |
| 293 | + |
| 294 | +### Q: How do I handle long-running requests? |
| 295 | + |
| 296 | +A: The SDK includes timeout utilities. You can configure timeouts using the `Timeout` class in `TransportOptions`. |
| 297 | + |
| 298 | +### Q: Why are some tools not executing? |
| 299 | + |
| 300 | +A: This is likely due to permission modes. Check your permission mode settings and consider using `allowedTools` to pre-approve certain tools. |
| 301 | + |
| 302 | +### Q: How do I resume a previous session? |
| 303 | + |
| 304 | +A: Use the `setResumeSessionId()` method in `TransportOptions` to resume a previous session. |
| 305 | + |
| 306 | +### Q: Can I customize the environment for the CLI process? |
| 307 | + |
| 308 | +A: Yes, use the `setEnv()` method in `TransportOptions` to pass environment variables to the CLI process. |
| 309 | + |
| 310 | +## License |
| 311 | + |
| 312 | +Apache-2.0 - see [LICENSE](./LICENSE) for details. |
0 commit comments