mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-01-07 17:39:17 +00:00
Compare commits
23 Commits
release/v0
...
qwencode-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4eb3adea8 | ||
|
|
7dc7c6380d | ||
|
|
d2d2b845c5 | ||
|
|
96080f84a6 | ||
|
|
2b6218e564 | ||
|
|
24edf32da8 | ||
|
|
51b08f700c | ||
|
|
32e8b01cf0 | ||
|
|
db9d5cb45d | ||
|
|
73848d3867 | ||
|
|
6a62167f79 | ||
|
|
6ff437671e | ||
|
|
30f9e9c782 | ||
|
|
e4caa7a856 | ||
|
|
ac7ba95d65 | ||
|
|
4154493640 | ||
|
|
422998d7f0 | ||
|
|
68628bf952 | ||
|
|
e5efad89e0 | ||
|
|
e09bb5f5c0 | ||
|
|
24d11179d8 | ||
|
|
2ef8b6f350 | ||
|
|
5779f7ab1d |
24
packages/sdk-java/.editorconfig
Normal file
24
packages/sdk-java/.editorconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
|
||||
[*.java]
|
||||
ij_java_doc_align_exception_comments = false
|
||||
ij_java_doc_align_param_comments = false
|
||||
|
||||
[*.{yaml, yml, sh, ps1}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{md, mkd, markdown}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{**/res/**.xml, **/AndroidManifest.xml}]
|
||||
ij_continuation_indent_size = 4
|
||||
14
packages/sdk-java/.gitignore
vendored
Normal file
14
packages/sdk-java/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Maven
|
||||
log/
|
||||
target/
|
||||
|
||||
/docs/
|
||||
378
packages/sdk-java/QWEN.md
Normal file
378
packages/sdk-java/QWEN.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# Qwen Code Java SDK
|
||||
|
||||
## Project Overview
|
||||
|
||||
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.
|
||||
|
||||
**Context Information:**
|
||||
|
||||
- Current Date: Monday 5 January 2026
|
||||
- Operating System: darwin
|
||||
- Working Directory: /Users/weigeng/repos/qwen-code/packages/sdk-java
|
||||
|
||||
## Project Details
|
||||
|
||||
- **Group ID**: com.alibaba
|
||||
- **Artifact ID**: qwencode-sdk (as per pom.xml)
|
||||
- **Version**: 0.0.1-SNAPSHOT
|
||||
- **Packaging**: JAR
|
||||
- **Java Version**: 1.8+ (source and target)
|
||||
- **License**: Apache-2.0
|
||||
|
||||
## Architecture
|
||||
|
||||
The SDK follows a layered architecture:
|
||||
|
||||
- **API Layer**: Provides the main entry points through `QwenCodeCli` class with simple static methods for basic usage
|
||||
- **Session Layer**: Manages communication sessions with the Qwen Code CLI through the `Session` class
|
||||
- **Transport Layer**: Handles the communication mechanism between the SDK and CLI process (currently using process transport via `ProcessTransport`)
|
||||
- **Protocol Layer**: Defines data structures for communication based on the CLI protocol
|
||||
- **Utils**: Common utilities for concurrent execution, timeout handling, and error management
|
||||
|
||||
## Key Components
|
||||
|
||||
### Main Classes
|
||||
|
||||
- `QwenCodeCli`: Main entry point with static methods for simple queries
|
||||
- `Session`: Manages communication sessions with the CLI
|
||||
- `Transport`: Abstracts the communication mechanism (currently using process transport)
|
||||
- `ProcessTransport`: Implementation that communicates via process execution
|
||||
- `TransportOptions`: Configuration class for transport layer settings
|
||||
- `SessionEventSimpleConsumers`: High-level event handler for processing responses
|
||||
- `AssistantContentSimpleConsumers`: Handles different types of content within assistant messages
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **Logging**: ch.qos.logback:logback-classic
|
||||
- **Utilities**: org.apache.commons:commons-lang3
|
||||
- **JSON Processing**: com.alibaba.fastjson2:fastjson2
|
||||
- **Testing**: JUnit 5 (org.junit.jupiter:junit-jupiter)
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Java 8 or higher
|
||||
- Apache Maven 3.6.0 or higher
|
||||
|
||||
### Build Commands
|
||||
|
||||
```bash
|
||||
# Compile the project
|
||||
mvn compile
|
||||
|
||||
# Run tests
|
||||
mvn test
|
||||
|
||||
# Package the JAR
|
||||
mvn package
|
||||
|
||||
# Install to local repository
|
||||
mvn install
|
||||
|
||||
# Run checkstyle verification
|
||||
mvn checkstyle:check
|
||||
|
||||
# Generate Javadoc
|
||||
mvn javadoc:javadoc
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
The project includes basic unit tests using JUnit 5. The main test class `QwenCodeCliTest` demonstrates how to use the SDK to make simple queries to the Qwen Code CLI.
|
||||
|
||||
### Code Quality
|
||||
|
||||
The project uses Checkstyle for code formatting and style enforcement. The configuration is defined in `checkstyle.xml` and includes rules for:
|
||||
|
||||
- Whitespace and indentation
|
||||
- Naming conventions
|
||||
- Import ordering
|
||||
- Code structure
|
||||
- Line endings (LF only)
|
||||
- No trailing whitespace
|
||||
- 8-space indentation for line wrapping
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### Coding Standards
|
||||
|
||||
- Java 8 language features are supported
|
||||
- Follow standard Java naming conventions
|
||||
- Use UTF-8 encoding for source files
|
||||
- Line endings should be LF (Unix-style)
|
||||
- No trailing whitespace allowed
|
||||
- Use 8-space indentation for line wrapping
|
||||
|
||||
### Testing Practices
|
||||
|
||||
- Write unit tests using JUnit 5
|
||||
- Test classes should be in the `src/test/java` directory
|
||||
- Follow the naming convention `*Test.java` for test classes
|
||||
- Use appropriate assertions to validate functionality
|
||||
|
||||
### Documentation
|
||||
|
||||
- API documentation should follow JavaDoc conventions
|
||||
- Update README files when adding new features
|
||||
- Include examples in documentation
|
||||
|
||||
## API Reference
|
||||
|
||||
### QwenCodeCli Class
|
||||
|
||||
The main class provides several primary methods:
|
||||
|
||||
- `simpleQuery(String prompt)`: Synchronous method that returns a list of responses
|
||||
- `simpleQuery(String prompt, TransportOptions transportOptions)`: Synchronous method with custom transport options
|
||||
- `simpleQuery(String prompt, TransportOptions transportOptions, AssistantContentConsumers assistantContentConsumers)`: Advanced method with custom content consumers
|
||||
- `newSession()`: Creates a new session with default options
|
||||
- `newSession(TransportOptions transportOptions)`: Creates a new session with custom options
|
||||
|
||||
### Permission Modes
|
||||
|
||||
The SDK supports different permission modes for controlling tool execution:
|
||||
|
||||
- **`default`**: Write tools are denied unless approved via `canUseTool` callback or in `allowedTools`. Read-only tools execute without confirmation.
|
||||
- **`plan`**: Blocks all write tools, instructing AI to present a plan first.
|
||||
- **`auto-edit`**: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
|
||||
- **`yolo`**: All tools execute automatically without confirmation.
|
||||
|
||||
### Transport Options
|
||||
|
||||
The `TransportOptions` class allows configuration of how the SDK communicates with the Qwen Code CLI:
|
||||
|
||||
- `pathToQwenExecutable`: Path to the Qwen Code CLI executable
|
||||
- `cwd`: Working directory for the CLI process
|
||||
- `model`: AI model to use for the session
|
||||
- `permissionMode`: Permission mode that controls tool execution
|
||||
- `env`: Environment variables to pass to the CLI process
|
||||
- `maxSessionTurns`: Limits the number of conversation turns in a session
|
||||
- `coreTools`: List of core tools that should be available to the AI
|
||||
- `excludeTools`: List of tools to exclude from being available to the AI
|
||||
- `allowedTools`: List of tools that are pre-approved for use without additional confirmation
|
||||
- `authType`: Authentication type to use for the session
|
||||
- `includePartialMessages`: Enables receiving partial messages during streaming responses
|
||||
- `skillsEnable`: Enables or disables skills functionality for the session
|
||||
- `turnTimeout`: Timeout for a complete turn of conversation
|
||||
- `messageTimeout`: Timeout for individual messages within a turn
|
||||
- `resumeSessionId`: ID of a previous session to resume
|
||||
- `otherOptions`: Additional command-line options to pass to the CLI
|
||||
|
||||
### Session Control Features
|
||||
|
||||
- **Session creation**: Use `QwenCodeCli.newSession()` to create a new session with custom options
|
||||
- **Session management**: The `Session` class provides methods to send prompts, handle responses, and manage session state
|
||||
- **Session cleanup**: Always close sessions using `session.close()` to properly terminate the CLI process
|
||||
- **Session resumption**: Use `setResumeSessionId()` in `TransportOptions` to resume a previous session
|
||||
- **Session interruption**: Use `session.interrupt()` to interrupt a currently running prompt
|
||||
- **Dynamic model switching**: Use `session.setModel()` to change the model during a session
|
||||
- **Dynamic permission mode switching**: Use `session.setPermissionMode()` to change the permission mode during a session
|
||||
|
||||
### Thread Pool Configuration
|
||||
|
||||
The SDK uses a thread pool for managing concurrent operations with the following default configuration:
|
||||
|
||||
- **Core Pool Size**: 30 threads
|
||||
- **Maximum Pool Size**: 100 threads
|
||||
- **Keep-Alive Time**: 60 seconds
|
||||
- **Queue Capacity**: 300 tasks (using LinkedBlockingQueue)
|
||||
- **Thread Naming**: "qwen_code_cli-pool-{number}"
|
||||
- **Daemon Threads**: false
|
||||
- **Rejected Execution Handler**: CallerRunsPolicy
|
||||
|
||||
### Session Event Consumers and Assistant Content Consumers
|
||||
|
||||
The SDK provides two key interfaces for handling events and content from the CLI:
|
||||
|
||||
#### SessionEventConsumers Interface
|
||||
|
||||
The `SessionEventConsumers` interface provides callbacks for different types of messages during a session:
|
||||
|
||||
- `onSystemMessage`: Handles system messages from the CLI (receives Session and SDKSystemMessage)
|
||||
- `onResultMessage`: Handles result messages from the CLI (receives Session and SDKResultMessage)
|
||||
- `onAssistantMessage`: Handles assistant messages (AI responses) (receives Session and SDKAssistantMessage)
|
||||
- `onPartialAssistantMessage`: Handles partial assistant messages during streaming (receives Session and SDKPartialAssistantMessage)
|
||||
- `onUserMessage`: Handles user messages (receives Session and SDKUserMessage)
|
||||
- `onOtherMessage`: Handles other types of messages (receives Session and String message)
|
||||
- `onControlResponse`: Handles control responses (receives Session and CLIControlResponse)
|
||||
- `onControlRequest`: Handles control requests (receives Session and CLIControlRequest, returns CLIControlResponse)
|
||||
- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlRequest<CLIControlPermissionRequest>, returns Behavior)
|
||||
|
||||
#### AssistantContentConsumers Interface
|
||||
|
||||
The `AssistantContentConsumers` interface handles different types of content within assistant messages:
|
||||
|
||||
- `onText`: Handles text content (receives Session and TextAssistantContent)
|
||||
- `onThinking`: Handles thinking content (receives Session and ThingkingAssistantContent)
|
||||
- `onToolUse`: Handles tool use content (receives Session and ToolUseAssistantContent)
|
||||
- `onToolResult`: Handles tool result content (receives Session and ToolResultAssistantContent)
|
||||
- `onOtherContent`: Handles other content types (receives Session and AssistantContent)
|
||||
- `onUsage`: Handles usage information (receives Session and AssistantUsage)
|
||||
- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlPermissionRequest, returns Behavior)
|
||||
- `onOtherControlRequest`: Handles other control requests (receives Session and ControlRequestPayload, returns ControlResponsePayload)
|
||||
|
||||
#### Relationship Between the Interfaces
|
||||
|
||||
**Important Note on Event Hierarchy:**
|
||||
|
||||
- `SessionEventConsumers` is the **high-level** event processor that handles different message types (system, assistant, user, etc.)
|
||||
- `AssistantContentConsumers` is the **low-level** content processor that handles different types of content within assistant messages (text, tools, thinking, etc.)
|
||||
|
||||
**Processor Relationship:**
|
||||
|
||||
- `SessionEventConsumers` → `AssistantContentConsumers` (SessionEventConsumers uses AssistantContentConsumers to process content within assistant messages)
|
||||
|
||||
**Event Derivation Relationships:**
|
||||
|
||||
- `onAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent`, `onUsage`
|
||||
- `onPartialAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent`
|
||||
- `onControlRequest` → `onPermissionRequest`, `onOtherControlRequest`
|
||||
|
||||
**Event Timeout Relationships:**
|
||||
|
||||
Each event handler method has a corresponding timeout method that allows customizing the timeout behavior for that specific event:
|
||||
|
||||
- `onSystemMessage` ↔ `onSystemMessageTimeout`
|
||||
- `onResultMessage` ↔ `onResultMessageTimeout`
|
||||
- `onAssistantMessage` ↔ `onAssistantMessageTimeout`
|
||||
- `onPartialAssistantMessage` ↔ `onPartialAssistantMessageTimeout`
|
||||
- `onUserMessage` ↔ `onUserMessageTimeout`
|
||||
- `onOtherMessage` ↔ `onOtherMessageTimeout`
|
||||
- `onControlResponse` ↔ `onControlResponseTimeout`
|
||||
- `onControlRequest` ↔ `onControlRequestTimeout`
|
||||
|
||||
For AssistantContentConsumers timeout methods:
|
||||
|
||||
- `onText` ↔ `onTextTimeout`
|
||||
- `onThinking` ↔ `onThinkingTimeout`
|
||||
- `onToolUse` ↔ `onToolUseTimeout`
|
||||
- `onToolResult` ↔ `onToolResultTimeout`
|
||||
- `onOtherContent` ↔ `onOtherContentTimeout`
|
||||
- `onPermissionRequest` ↔ `onPermissionRequestTimeout`
|
||||
- `onOtherControlRequest` ↔ `onOtherControlRequestTimeout`
|
||||
|
||||
**Default Timeout Values:**
|
||||
|
||||
- `SessionEventSimpleConsumers` default timeout: 180 seconds (Timeout.TIMEOUT_180_SECONDS)
|
||||
- `AssistantContentSimpleConsumers` default timeout: 60 seconds (Timeout.TIMEOUT_60_SECONDS)
|
||||
|
||||
**Timeout Hierarchy Requirements:**
|
||||
|
||||
For proper operation, the following timeout relationships should be maintained:
|
||||
|
||||
- `onAssistantMessageTimeout` return value should be greater than `onTextTimeout`, `onThinkingTimeout`, `onToolUseTimeout`, `onToolResultTimeout`, and `onOtherContentTimeout` return values
|
||||
- `onControlRequestTimeout` return value should be greater than `onPermissionRequestTimeout` and `onOtherControlRequestTimeout` return values
|
||||
|
||||
#### Relationship Between the Interfaces
|
||||
|
||||
- `AssistantContentSimpleConsumers` is the default implementation of `AssistantContentConsumers`
|
||||
- `SessionEventSimpleConsumers` is the concrete implementation that combines both interfaces and depends on an `AssistantContentConsumers` instance to handle content within assistant messages
|
||||
- The timeout methods in `SessionEventConsumers` now include the message object as a parameter (e.g., `onSystemMessageTimeout(Session session, SDKSystemMessage systemMessage)`)
|
||||
|
||||
Event processing is subject to the timeout settings configured in `TransportOptions` and `SessionEventConsumers`. For detailed timeout configuration options, see the "Timeout" section above.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
The SDK includes several example files in `src/test/java/com/alibaba/qwen/code/cli/example/` that demonstrate different aspects of the API:
|
||||
|
||||
### Basic Usage
|
||||
|
||||
- `QuickStartExample.java`: Demonstrates simple query usage, transport options configuration, and streaming content handling
|
||||
|
||||
### Session Control
|
||||
|
||||
- `SessionExample.java`: Shows session control features including permission mode changes, model switching, interruption, and event handling
|
||||
|
||||
### Configuration
|
||||
|
||||
- `ThreadPoolConfigurationExample.java`: Shows how to configure the thread pool used by the SDK
|
||||
|
||||
## Error Handling
|
||||
|
||||
The SDK provides specific exception types for different error scenarios:
|
||||
|
||||
- `SessionControlException`: Thrown when there's an issue with session control (creation, initialization, etc.)
|
||||
- `SessionSendPromptException`: Thrown when there's an issue sending a prompt or receiving a response
|
||||
- `SessionClosedException`: Thrown when attempting to use a closed session
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── example/
|
||||
│ └── java/
|
||||
│ └── com/
|
||||
│ └── alibaba/
|
||||
│ └── qwen/
|
||||
│ └── code/
|
||||
│ └── example/
|
||||
├── main/
|
||||
│ └── java/
|
||||
│ └── com/
|
||||
│ └── alibaba/
|
||||
│ └── qwen/
|
||||
│ └── code/
|
||||
│ └── cli/
|
||||
│ ├── QwenCodeCli.java
|
||||
│ ├── protocol/
|
||||
│ ├── session/
|
||||
│ ├── transport/
|
||||
│ └── utils/
|
||||
└── test/
|
||||
├── java/
|
||||
│ └── com/
|
||||
│ └── alibaba/
|
||||
│ └── qwen/
|
||||
│ └── code/
|
||||
│ └── cli/
|
||||
│ ├── QwenCodeCliTest.java
|
||||
│ ├── session/
|
||||
│ │ └── SessionTest.java
|
||||
│ └── transport/
|
||||
│ ├── PermissionModeTest.java
|
||||
│ └── process/
|
||||
│ └── ProcessTransportTest.java
|
||||
└── temp/
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
- `pom.xml`: Maven build configuration and dependencies
|
||||
- `checkstyle.xml`: Code style and formatting rules
|
||||
- `.editorconfig`: Editor configuration settings
|
||||
|
||||
## FAQ / Troubleshooting
|
||||
|
||||
### Q: Do I need to install the Qwen CLI separately?
|
||||
|
||||
A: No, from v0.1.1, the CLI is bundled with the SDK, so no standalone CLI installation is needed.
|
||||
|
||||
### Q: What Java versions are supported?
|
||||
|
||||
A: The SDK requires Java 1.8 or higher.
|
||||
|
||||
### Q: How do I handle long-running requests?
|
||||
|
||||
A: The SDK includes timeout utilities. You can configure timeouts using the `Timeout` class in `TransportOptions`.
|
||||
|
||||
### Q: Why are some tools not executing?
|
||||
|
||||
A: This is likely due to permission modes. Check your permission mode settings and consider using `allowedTools` to pre-approve certain tools.
|
||||
|
||||
### Q: How do I resume a previous session?
|
||||
|
||||
A: Use the `setResumeSessionId()` method in `TransportOptions` to resume a previous session.
|
||||
|
||||
### Q: Can I customize the environment for the CLI process?
|
||||
|
||||
A: Yes, use the `setEnv()` method in `TransportOptions` to pass environment variables to the CLI process.
|
||||
|
||||
### Q: What happens if the CLI process crashes?
|
||||
|
||||
A: The SDK will throw appropriate exceptions. Make sure to handle `SessionControlException` and implement retry logic if needed.
|
||||
|
||||
## Maintainers
|
||||
|
||||
- **Developer**: skyfire (gengwei.gw(at)alibaba-inc.com)
|
||||
- **Organization**: Alibaba Group
|
||||
312
packages/sdk-java/README.md
Normal file
312
packages/sdk-java/README.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# Qwen Code Java SDK
|
||||
|
||||
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.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Java >= 1.8
|
||||
- Maven >= 3.6.0 (for building from source)
|
||||
- qwen-code >= 0.5.0
|
||||
|
||||
### Dependencies
|
||||
|
||||
- **Logging**: ch.qos.logback:logback-classic
|
||||
- **Utilities**: org.apache.commons:commons-lang3
|
||||
- **JSON Processing**: com.alibaba.fastjson2:fastjson2
|
||||
- **Testing**: JUnit 5 (org.junit.jupiter:junit-jupiter)
|
||||
|
||||
## Installation
|
||||
|
||||
Add the following dependency to your Maven `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>qwencode-sdk</artifactId>
|
||||
<version>{$version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
Or if using Gradle, add to your `build.gradle`:
|
||||
|
||||
```gradle
|
||||
implementation 'com.alibaba:qwencode-sdk:{$version}'
|
||||
```
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Build Commands
|
||||
|
||||
```bash
|
||||
# Compile the project
|
||||
mvn compile
|
||||
|
||||
# Run tests
|
||||
mvn test
|
||||
|
||||
# Package the JAR
|
||||
mvn package
|
||||
|
||||
# Install to local repository
|
||||
mvn install
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
The simplest way to use the SDK is through the `QwenCodeCli.simpleQuery()` method:
|
||||
|
||||
```java
|
||||
public static void runSimpleExample() {
|
||||
List<String> result = QwenCodeCli.simpleQuery("hello world");
|
||||
result.forEach(logger::info);
|
||||
}
|
||||
```
|
||||
|
||||
For more advanced usage with custom transport options:
|
||||
|
||||
```java
|
||||
public static void runTransportOptionsExample() {
|
||||
TransportOptions options = new TransportOptions()
|
||||
.setModel("qwen3-coder-flash")
|
||||
.setPermissionMode(PermissionMode.AUTO_EDIT)
|
||||
.setCwd("./")
|
||||
.setEnv(new HashMap<String, String>() {{put("CUSTOM_VAR", "value");}})
|
||||
.setIncludePartialMessages(true)
|
||||
.setTurnTimeout(new Timeout(120L, TimeUnit.SECONDS))
|
||||
.setMessageTimeout(new Timeout(90L, TimeUnit.SECONDS))
|
||||
.setAllowedTools(Arrays.asList("read_file", "write_file", "list_directory"));
|
||||
|
||||
List<String> result = QwenCodeCli.simpleQuery("who are you, what are your capabilities?", options);
|
||||
result.forEach(logger::info);
|
||||
}
|
||||
```
|
||||
|
||||
For streaming content handling with custom content consumers:
|
||||
|
||||
```java
|
||||
public static void runStreamingExample() {
|
||||
QwenCodeCli.simpleQuery("who are you, what are your capabilities?",
|
||||
new TransportOptions().setMessageTimeout(new Timeout(10L, TimeUnit.SECONDS)), new AssistantContentSimpleConsumers() {
|
||||
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
logger.info("Text content received: {}", textAssistantContent.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
logger.info("Thinking content received: {}", thingkingAssistantContent.getThinking());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseContent) {
|
||||
logger.info("Tool use content received: {} with arguments: {}",
|
||||
toolUseContent, toolUseContent.getInput());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultContent) {
|
||||
logger.info("Tool result content received: {}", toolResultContent.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
logger.info("Other content received: {}", other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage assistantUsage) {
|
||||
logger.info("Usage information received: Input tokens: {}, Output tokens: {}",
|
||||
assistantUsage.getUsage().getInputTokens(), assistantUsage.getUsage().getOutputTokens());
|
||||
}
|
||||
}.setDefaultPermissionOperation(Operation.allow));
|
||||
logger.info("Streaming example completed.");
|
||||
}
|
||||
```
|
||||
|
||||
other examples see src/test/java/com/alibaba/qwen/code/cli/example
|
||||
|
||||
## Architecture
|
||||
|
||||
The SDK follows a layered architecture:
|
||||
|
||||
- **API Layer**: Provides the main entry points through `QwenCodeCli` class with simple static methods for basic usage
|
||||
- **Session Layer**: Manages communication sessions with the Qwen Code CLI through the `Session` class
|
||||
- **Transport Layer**: Handles the communication mechanism between the SDK and CLI process (currently using process transport via `ProcessTransport`)
|
||||
- **Protocol Layer**: Defines data structures for communication based on the CLI protocol
|
||||
- **Utils**: Common utilities for concurrent execution, timeout handling, and error management
|
||||
|
||||
## Key Features
|
||||
|
||||
### Permission Modes
|
||||
|
||||
The SDK supports different permission modes for controlling tool execution:
|
||||
|
||||
- **`default`**: Write tools are denied unless approved via `canUseTool` callback or in `allowedTools`. Read-only tools execute without confirmation.
|
||||
- **`plan`**: Blocks all write tools, instructing AI to present a plan first.
|
||||
- **`auto-edit`**: Auto-approve edit tools (edit, write_file) while other tools require confirmation.
|
||||
- **`yolo`**: All tools execute automatically without confirmation.
|
||||
|
||||
### Session Event Consumers and Assistant Content Consumers
|
||||
|
||||
The SDK provides two key interfaces for handling events and content from the CLI:
|
||||
|
||||
#### SessionEventConsumers Interface
|
||||
|
||||
The `SessionEventConsumers` interface provides callbacks for different types of messages during a session:
|
||||
|
||||
- `onSystemMessage`: Handles system messages from the CLI (receives Session and SDKSystemMessage)
|
||||
- `onResultMessage`: Handles result messages from the CLI (receives Session and SDKResultMessage)
|
||||
- `onAssistantMessage`: Handles assistant messages (AI responses) (receives Session and SDKAssistantMessage)
|
||||
- `onPartialAssistantMessage`: Handles partial assistant messages during streaming (receives Session and SDKPartialAssistantMessage)
|
||||
- `onUserMessage`: Handles user messages (receives Session and SDKUserMessage)
|
||||
- `onOtherMessage`: Handles other types of messages (receives Session and String message)
|
||||
- `onControlResponse`: Handles control responses (receives Session and CLIControlResponse)
|
||||
- `onControlRequest`: Handles control requests (receives Session and CLIControlRequest, returns CLIControlResponse)
|
||||
- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlRequest<CLIControlPermissionRequest>, returns Behavior)
|
||||
|
||||
#### AssistantContentConsumers Interface
|
||||
|
||||
The `AssistantContentConsumers` interface handles different types of content within assistant messages:
|
||||
|
||||
- `onText`: Handles text content (receives Session and TextAssistantContent)
|
||||
- `onThinking`: Handles thinking content (receives Session and ThingkingAssistantContent)
|
||||
- `onToolUse`: Handles tool use content (receives Session and ToolUseAssistantContent)
|
||||
- `onToolResult`: Handles tool result content (receives Session and ToolResultAssistantContent)
|
||||
- `onOtherContent`: Handles other content types (receives Session and AssistantContent)
|
||||
- `onUsage`: Handles usage information (receives Session and AssistantUsage)
|
||||
- `onPermissionRequest`: Handles permission requests (receives Session and CLIControlPermissionRequest, returns Behavior)
|
||||
- `onOtherControlRequest`: Handles other control requests (receives Session and ControlRequestPayload, returns ControlResponsePayload)
|
||||
|
||||
#### Relationship Between the Interfaces
|
||||
|
||||
**Important Note on Event Hierarchy:**
|
||||
|
||||
- `SessionEventConsumers` is the **high-level** event processor that handles different message types (system, assistant, user, etc.)
|
||||
- `AssistantContentConsumers` is the **low-level** content processor that handles different types of content within assistant messages (text, tools, thinking, etc.)
|
||||
|
||||
**Processor Relationship:**
|
||||
|
||||
- `SessionEventConsumers` → `AssistantContentConsumers` (SessionEventConsumers uses AssistantContentConsumers to process content within assistant messages)
|
||||
|
||||
**Event Derivation Relationships:**
|
||||
|
||||
- `onAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent`, `onUsage`
|
||||
- `onPartialAssistantMessage` → `onText`, `onThinking`, `onToolUse`, `onToolResult`, `onOtherContent`
|
||||
- `onControlRequest` → `onPermissionRequest`, `onOtherControlRequest`
|
||||
|
||||
**Event Timeout Relationships:**
|
||||
|
||||
Each event handler method has a corresponding timeout method that allows customizing the timeout behavior for that specific event:
|
||||
|
||||
- `onSystemMessage` ↔ `onSystemMessageTimeout`
|
||||
- `onResultMessage` ↔ `onResultMessageTimeout`
|
||||
- `onAssistantMessage` ↔ `onAssistantMessageTimeout`
|
||||
- `onPartialAssistantMessage` ↔ `onPartialAssistantMessageTimeout`
|
||||
- `onUserMessage` ↔ `onUserMessageTimeout`
|
||||
- `onOtherMessage` ↔ `onOtherMessageTimeout`
|
||||
- `onControlResponse` ↔ `onControlResponseTimeout`
|
||||
- `onControlRequest` ↔ `onControlRequestTimeout`
|
||||
|
||||
For AssistantContentConsumers timeout methods:
|
||||
|
||||
- `onText` ↔ `onTextTimeout`
|
||||
- `onThinking` ↔ `onThinkingTimeout`
|
||||
- `onToolUse` ↔ `onToolUseTimeout`
|
||||
- `onToolResult` ↔ `onToolResultTimeout`
|
||||
- `onOtherContent` ↔ `onOtherContentTimeout`
|
||||
- `onPermissionRequest` ↔ `onPermissionRequestTimeout`
|
||||
- `onOtherControlRequest` ↔ `onOtherControlRequestTimeout`
|
||||
|
||||
**Default Timeout Values:**
|
||||
|
||||
- `SessionEventSimpleConsumers` default timeout: 180 seconds (Timeout.TIMEOUT_180_SECONDS)
|
||||
- `AssistantContentSimpleConsumers` default timeout: 60 seconds (Timeout.TIMEOUT_60_SECONDS)
|
||||
|
||||
**Timeout Hierarchy Requirements:**
|
||||
|
||||
For proper operation, the following timeout relationships should be maintained:
|
||||
|
||||
- `onAssistantMessageTimeout` return value should be greater than `onTextTimeout`, `onThinkingTimeout`, `onToolUseTimeout`, `onToolResultTimeout`, and `onOtherContentTimeout` return values
|
||||
- `onControlRequestTimeout` return value should be greater than `onPermissionRequestTimeout` and `onOtherControlRequestTimeout` return values
|
||||
|
||||
### Transport Options
|
||||
|
||||
The `TransportOptions` class allows configuration of how the SDK communicates with the Qwen Code CLI:
|
||||
|
||||
- `pathToQwenExecutable`: Path to the Qwen Code CLI executable
|
||||
- `cwd`: Working directory for the CLI process
|
||||
- `model`: AI model to use for the session
|
||||
- `permissionMode`: Permission mode that controls tool execution
|
||||
- `env`: Environment variables to pass to the CLI process
|
||||
- `maxSessionTurns`: Limits the number of conversation turns in a session
|
||||
- `coreTools`: List of core tools that should be available to the AI
|
||||
- `excludeTools`: List of tools to exclude from being available to the AI
|
||||
- `allowedTools`: List of tools that are pre-approved for use without additional confirmation
|
||||
- `authType`: Authentication type to use for the session
|
||||
- `includePartialMessages`: Enables receiving partial messages during streaming responses
|
||||
- `skillsEnable`: Enables or disables skills functionality for the session
|
||||
- `turnTimeout`: Timeout for a complete turn of conversation
|
||||
- `messageTimeout`: Timeout for individual messages within a turn
|
||||
- `resumeSessionId`: ID of a previous session to resume
|
||||
- `otherOptions`: Additional command-line options to pass to the CLI
|
||||
|
||||
### Session Control Features
|
||||
|
||||
- **Session creation**: Use `QwenCodeCli.newSession()` to create a new session with custom options
|
||||
- **Session management**: The `Session` class provides methods to send prompts, handle responses, and manage session state
|
||||
- **Session cleanup**: Always close sessions using `session.close()` to properly terminate the CLI process
|
||||
- **Session resumption**: Use `setResumeSessionId()` in `TransportOptions` to resume a previous session
|
||||
- **Session interruption**: Use `session.interrupt()` to interrupt a currently running prompt
|
||||
- **Dynamic model switching**: Use `session.setModel()` to change the model during a session
|
||||
- **Dynamic permission mode switching**: Use `session.setPermissionMode()` to change the permission mode during a session
|
||||
|
||||
### Thread Pool Configuration
|
||||
|
||||
The SDK uses a thread pool for managing concurrent operations with the following default configuration:
|
||||
|
||||
- **Core Pool Size**: 30 threads
|
||||
- **Maximum Pool Size**: 100 threads
|
||||
- **Keep-Alive Time**: 60 seconds
|
||||
- **Queue Capacity**: 300 tasks (using LinkedBlockingQueue)
|
||||
- **Thread Naming**: "qwen_code_cli-pool-{number}"
|
||||
- **Daemon Threads**: false
|
||||
- **Rejected Execution Handler**: CallerRunsPolicy
|
||||
|
||||
## Error Handling
|
||||
|
||||
The SDK provides specific exception types for different error scenarios:
|
||||
|
||||
- `SessionControlException`: Thrown when there's an issue with session control (creation, initialization, etc.)
|
||||
- `SessionSendPromptException`: Thrown when there's an issue sending a prompt or receiving a response
|
||||
- `SessionClosedException`: Thrown when attempting to use a closed session
|
||||
|
||||
## FAQ / Troubleshooting
|
||||
|
||||
### Q: Do I need to install the Qwen CLI separately?
|
||||
|
||||
A: No, from v0.1.1, the CLI is bundled with the SDK, so no standalone CLI installation is needed.
|
||||
|
||||
### Q: What Java versions are supported?
|
||||
|
||||
A: The SDK requires Java 1.8 or higher.
|
||||
|
||||
### Q: How do I handle long-running requests?
|
||||
|
||||
A: The SDK includes timeout utilities. You can configure timeouts using the `Timeout` class in `TransportOptions`.
|
||||
|
||||
### Q: Why are some tools not executing?
|
||||
|
||||
A: This is likely due to permission modes. Check your permission mode settings and consider using `allowedTools` to pre-approve certain tools.
|
||||
|
||||
### Q: How do I resume a previous session?
|
||||
|
||||
A: Use the `setResumeSessionId()` method in `TransportOptions` to resume a previous session.
|
||||
|
||||
### Q: Can I customize the environment for the CLI process?
|
||||
|
||||
A: Yes, use the `setEnv()` method in `TransportOptions` to pass environment variables to the CLI process.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0 - see [LICENSE](./LICENSE) for details.
|
||||
131
packages/sdk-java/checkstyle.xml
Normal file
131
packages/sdk-java/checkstyle.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
|
||||
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<module name="FileTabCharacter" />
|
||||
<module name="NewlineAtEndOfFile">
|
||||
<property name="lineSeparator" value="lf" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\r" />
|
||||
<property name="message" value="Line contains carriage return" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value=" \n" />
|
||||
<property name="message" value="Line has trailing whitespace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\n" />
|
||||
<property name="message" value="Multiple consecutive blank lines" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\Z" />
|
||||
<property name="message" value="Blank line before end of file" />
|
||||
</module>
|
||||
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\{\n\n" />
|
||||
<property name="message" value="Blank line after opening brace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="\n\n\s*\}" />
|
||||
<property name="message" value="Blank line before closing brace" />
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="->\s*\{\s+\}" />
|
||||
<property name="message" value="Whitespace inside empty lambda body" />
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="SuppressWarningsHolder" />
|
||||
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="text" />
|
||||
<property name="tokens" value="
|
||||
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_IF,
|
||||
LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, INSTANCE_INIT, STATIC_INIT" />
|
||||
</module>
|
||||
<module name="EmptyStatement" />
|
||||
<module name="EmptyForInitializerPad" />
|
||||
<module name="MethodParamPad">
|
||||
<property name="allowLineBreaks" value="true" />
|
||||
<property name="option" value="nospace" />
|
||||
</module>
|
||||
<module name="ParenPad" />
|
||||
<module name="TypecastParenPad" />
|
||||
<module name="NeedBraces" />
|
||||
<module name="LeftCurly">
|
||||
<property name="option" value="eol" />
|
||||
<property name="tokens" value="
|
||||
LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR,
|
||||
LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE" />
|
||||
</module>
|
||||
<module name="GenericWhitespace" />
|
||||
<module name="WhitespaceAfter" />
|
||||
<module name="NoWhitespaceAfter" />
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<module name="SingleSpaceSeparator" />
|
||||
<module name="Indentation">
|
||||
<property name="throwsIndent" value="8" />
|
||||
<property name="lineWrappingIndentation" value="8" />
|
||||
</module>
|
||||
|
||||
<module name="UpperEll" />
|
||||
<module name="DefaultComesLast" />
|
||||
<module name="ArrayTypeStyle" />
|
||||
<module name="ModifierOrder" />
|
||||
<module name="OneStatementPerLine" />
|
||||
<module name="StringLiteralEquality" />
|
||||
<module name="MutableException" />
|
||||
<module name="EqualsHashCode" />
|
||||
<module name="ExplicitInitialization" />
|
||||
<module name="OneTopLevelClass" />
|
||||
|
||||
<module name="MemberName" />
|
||||
<module name="PackageName" />
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="^[A-Z][0-9]?$" />
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="^[A-Z][0-9]?$" />
|
||||
</module>
|
||||
<module name="AnnotationUseStyle">
|
||||
<property name="trailingArrayComma" value="ignore" />
|
||||
</module>
|
||||
|
||||
<module name="RedundantImport" />
|
||||
<module name="UnusedImports" />
|
||||
<!-- <module name="ImportOrder">-->
|
||||
<!-- <property name="groups" value="*,javax,java" />-->
|
||||
<!-- <property name="separated" value="true" />-->
|
||||
<!-- <property name="option" value="bottom" />-->
|
||||
<!-- <property name="sortStaticImportsAlphabetically" value="true" />-->
|
||||
<!-- </module>-->
|
||||
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true" />
|
||||
<property name="allowEmptyMethods" value="true" />
|
||||
<property name="allowEmptyLambdas" value="true" />
|
||||
<property name="ignoreEnhancedForColon" value="false" />
|
||||
<property name="tokens" value="
|
||||
ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN,
|
||||
BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAND,
|
||||
LAMBDA, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
|
||||
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH,
|
||||
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE,
|
||||
LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL,
|
||||
PLUS, PLUS_ASSIGN, QUESTION, SL, SLIST, SL_ASSIGN, SR, SR_ASSIGN,
|
||||
STAR, STAR_ASSIGN, TYPE_EXTENSION_AND" />
|
||||
</module>
|
||||
|
||||
<module name="WhitespaceAfter" />
|
||||
|
||||
<module name="NoWhitespaceAfter">
|
||||
<property name="tokens" value="DOT" />
|
||||
<property name="allowLineBreaks" value="false" />
|
||||
</module>
|
||||
|
||||
<module name="MissingOverride"/>
|
||||
</module>
|
||||
</module>
|
||||
190
packages/sdk-java/pom.xml
Normal file
190
packages/sdk-java/pom.xml
Normal file
@@ -0,0 +1,190 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>qwencode-sdk</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.0.1-alpha1</version>
|
||||
<name>qwencode-sdk</name>
|
||||
<url>https://maven.apache.org</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2</name>
|
||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
<comments>A business-friendly OSS license</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>https://github.com/QwenLM/qwen-code</url>
|
||||
<connection>scm:git:https://github.com/QwenLM/qwen-code.git</connection>
|
||||
</scm>
|
||||
<properties>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<checkstyle-maven-plugin.version>3.6.0</checkstyle-maven-plugin.version>
|
||||
<jacoco-maven-plugin.version>0.8.12</jacoco-maven-plugin.version>
|
||||
<junit5.version>5.14.1</junit5.version>
|
||||
<logback-classic.version>1.3.16</logback-classic.version>
|
||||
<fastjson2.version>2.0.60</fastjson2.version>
|
||||
<maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
|
||||
<central-publishing-maven-plugin.version>9</central-publishing-maven-plugin.version>
|
||||
<maven-source-plugin.version>2</maven-source-plugin.version>
|
||||
<maven-javadoc-plugin.version>2.9.1</maven-javadoc-plugin.version>
|
||||
<maven-gpg-plugin.version>1.5</maven-gpg-plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${junit5.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback-classic.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.20.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${checkstyle-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco-maven-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.central</groupId>
|
||||
<artifactId>central-publishing-maven-plugin</artifactId>
|
||||
<version>0.${central-publishing-maven-plugin.version}.0</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<publishingServerId>central</publishingServerId>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven-source-plugin.version}.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${maven-javadoc-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven-gpg-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<organization>
|
||||
<name>Alibaba Group</name>
|
||||
<url>https://github.com/alibaba</url>
|
||||
</organization>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>skyfire</id>
|
||||
<name>skyfire</name>
|
||||
<email>gengwei.gw(at)alibaba-inc.com</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
<role>Designer</role>
|
||||
</roles>
|
||||
<timezone>+8</timezone>
|
||||
<url>https://github.com/gwinthis</url>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>central</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://central.sonatype.com/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.alibaba.qwen.code.cli;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.AssistantContentConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.AssistantContentSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.SessionEventSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
|
||||
import com.alibaba.qwen.code.cli.utils.MyConcurrentUtils;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Main entry point for interacting with the Qwen Code CLI. Provides static methods for simple queries and session management.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class QwenCodeCli {
|
||||
private static final Logger log = LoggerFactory.getLogger(QwenCodeCli.class);
|
||||
|
||||
/**
|
||||
* Sends a simple query to the Qwen Code CLI and returns a list of responses.
|
||||
*
|
||||
* @param prompt The input prompt to send to the CLI
|
||||
* @return A list of strings representing the CLI's responses
|
||||
*/
|
||||
public static List<String> simpleQuery(String prompt) {
|
||||
return simpleQuery(prompt, new TransportOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a simple query with custom transport options.
|
||||
*
|
||||
* @param prompt The input prompt to send to the CLI
|
||||
* @param transportOptions Configuration options for the transport layer
|
||||
* @return A list of strings representing the CLI's responses
|
||||
*/
|
||||
public static List<String> simpleQuery(String prompt, TransportOptions transportOptions) {
|
||||
final List<String> response = new ArrayList<>();
|
||||
MyConcurrentUtils.runAndWait(() -> simpleQuery(prompt, transportOptions, new AssistantContentSimpleConsumers() {
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
response.add(textAssistantContent.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
response.add(thingkingAssistantContent.getThinking());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseAssistantContent) {
|
||||
response.add(JSON.toJSONString(toolUseAssistantContent.getContentOfAssistant()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultAssistantContent) {
|
||||
response.add(JSON.toJSONString(toolResultAssistantContent));
|
||||
}
|
||||
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
response.add(JSON.toJSONString(other.getContentOfAssistant()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage assistantUsage) {
|
||||
log.info("received usage {} of message {}", assistantUsage.getUsage(), assistantUsage.getMessageId());
|
||||
}
|
||||
}.setDefaultPermissionOperation(Operation.allow)), Timeout.TIMEOUT_30_MINUTES);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a query with custom content consumers.
|
||||
*
|
||||
* @param prompt The input prompt to send to the CLI
|
||||
* @param transportOptions Configuration options for the transport layer
|
||||
* @param assistantContentConsumers Consumers for handling different types of assistant content
|
||||
*/
|
||||
public static void simpleQuery(String prompt, TransportOptions transportOptions, AssistantContentConsumers assistantContentConsumers) {
|
||||
Session session = newSession(transportOptions);
|
||||
try {
|
||||
session.sendPrompt(prompt, new SessionEventSimpleConsumers()
|
||||
.setAssistantContentConsumer(assistantContentConsumers));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("sendPrompt error!", e);
|
||||
} finally {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception e) {
|
||||
log.error("close session error!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new session with default transport options.
|
||||
*
|
||||
* @return A new Session instance
|
||||
*/
|
||||
public static Session newSession() {
|
||||
return newSession(new TransportOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new session with custom transport options.
|
||||
*
|
||||
* @param transportOptions Configuration options for the transport layer
|
||||
* @return A new Session instance
|
||||
*/
|
||||
public static Session newSession(TransportOptions transportOptions) {
|
||||
Transport transport;
|
||||
try {
|
||||
transport = new ProcessTransport(transportOptions);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("initialized ProcessTransport error!", e);
|
||||
}
|
||||
|
||||
Session session;
|
||||
try {
|
||||
session = new Session(transport);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("initialized Session error!", e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents content from the assistant in a Qwen Code session.
|
||||
*
|
||||
* @param <C> The type of content
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public interface AssistantContent<C> {
|
||||
/**
|
||||
* Gets the type of the assistant content.
|
||||
*
|
||||
* @return The type of the assistant content
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* Gets the actual content from the assistant.
|
||||
*
|
||||
* @return The content from the assistant
|
||||
*/
|
||||
C getContentOfAssistant();
|
||||
|
||||
/**
|
||||
* Gets the message ID associated with this content.
|
||||
*
|
||||
* @return The message ID
|
||||
*/
|
||||
String getMessageId();
|
||||
|
||||
/**
|
||||
* Represents text content from the assistant.
|
||||
*/
|
||||
interface TextAssistantContent extends AssistantContent<String> {
|
||||
/**
|
||||
* Gets the text content.
|
||||
*
|
||||
* @return The text content
|
||||
*/
|
||||
String getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents thinking content from the assistant.
|
||||
*/
|
||||
interface ThingkingAssistantContent extends AssistantContent<String> {
|
||||
/**
|
||||
* Gets the thinking content.
|
||||
*
|
||||
* @return The thinking content
|
||||
*/
|
||||
String getThinking();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents tool use content from the assistant.
|
||||
*/
|
||||
interface ToolUseAssistantContent extends AssistantContent<Map<String, Object>> {
|
||||
/**
|
||||
* Gets the tool input.
|
||||
*
|
||||
* @return The tool input
|
||||
*/
|
||||
Map<String, Object> getInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents tool result content from the assistant.
|
||||
*/
|
||||
interface ToolResultAssistantContent extends AssistantContent<String> {
|
||||
/**
|
||||
* Gets whether the tool result indicates an error.
|
||||
*
|
||||
* @return Whether the tool result indicates an error
|
||||
*/
|
||||
Boolean getIsError();
|
||||
|
||||
/**
|
||||
* Gets the tool result content.
|
||||
*
|
||||
* @return The tool result content
|
||||
*/
|
||||
String getContent();
|
||||
|
||||
/**
|
||||
* Gets the tool use ID.
|
||||
*
|
||||
* @return The tool use ID
|
||||
*/
|
||||
String getToolUseId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
/**
|
||||
* Represents usage information for an assistant message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class AssistantUsage {
|
||||
/**
|
||||
* The ID of the message.
|
||||
*/
|
||||
String messageId;
|
||||
/**
|
||||
* The usage information.
|
||||
*/
|
||||
Usage usage;
|
||||
|
||||
/**
|
||||
* Gets the message ID.
|
||||
*
|
||||
* @return The message ID
|
||||
*/
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message ID.
|
||||
*
|
||||
* @param messageId The message ID
|
||||
*/
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage information.
|
||||
*
|
||||
* @return The usage information
|
||||
*/
|
||||
public Usage getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usage information.
|
||||
*
|
||||
* @param usage The usage information
|
||||
*/
|
||||
public void setUsage(Usage usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AssistantUsage instance.
|
||||
*
|
||||
* @param messageId The message ID
|
||||
* @param usage The usage information
|
||||
*/
|
||||
public AssistantUsage(String messageId, Usage usage) {
|
||||
this.messageId = messageId;
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>toString.</p>
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* Represents a permission denial from the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class CLIPermissionDenial {
|
||||
/**
|
||||
* The name of the denied tool.
|
||||
*/
|
||||
@JSONField(name = "tool_name")
|
||||
private String toolName;
|
||||
|
||||
/**
|
||||
* The ID of the denied tool use.
|
||||
*/
|
||||
@JSONField(name = "tool_use_id")
|
||||
private String toolUseId;
|
||||
|
||||
/**
|
||||
* The input for the denied tool.
|
||||
*/
|
||||
@JSONField(name = "tool_input")
|
||||
private Object toolInput;
|
||||
|
||||
/**
|
||||
* Gets the name of the denied tool.
|
||||
*
|
||||
* @return The name of the denied tool
|
||||
*/
|
||||
public String getToolName() {
|
||||
return toolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the denied tool.
|
||||
*
|
||||
* @param toolName The name of the denied tool
|
||||
*/
|
||||
public void setToolName(String toolName) {
|
||||
this.toolName = toolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the denied tool use.
|
||||
*
|
||||
* @return The ID of the denied tool use
|
||||
*/
|
||||
public String getToolUseId() {
|
||||
return toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the denied tool use.
|
||||
*
|
||||
* @param toolUseId The ID of the denied tool use
|
||||
*/
|
||||
public void setToolUseId(String toolUseId) {
|
||||
this.toolUseId = toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input for the denied tool.
|
||||
*
|
||||
* @return The input for the denied tool
|
||||
*/
|
||||
public Object getToolInput() {
|
||||
return toolInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input for the denied tool.
|
||||
*
|
||||
* @param toolInput The input for the denied tool
|
||||
*/
|
||||
public void setToolInput(Object toolInput) {
|
||||
this.toolInput = toolInput;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* Represents the capabilities of the Qwen Code CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class Capabilities {
|
||||
/**
|
||||
* Whether the CLI can handle can_use_tool requests.
|
||||
*/
|
||||
@JSONField(name = "can_handle_can_use_tool")
|
||||
boolean canHandleCanUseTool;
|
||||
|
||||
/**
|
||||
* Whether the CLI can handle hook callbacks.
|
||||
*/
|
||||
@JSONField(name = "can_handle_hook_callback")
|
||||
boolean canHandleHookCallback;
|
||||
|
||||
/**
|
||||
* Whether the CLI can set permission mode.
|
||||
*/
|
||||
@JSONField(name = "can_set_permission_mode")
|
||||
boolean canSetPermissionMode;
|
||||
|
||||
/**
|
||||
* Whether the CLI can set the model.
|
||||
*/
|
||||
@JSONField(name = "can_set_model")
|
||||
boolean canSetModel;
|
||||
|
||||
/**
|
||||
* Whether the CLI can handle MCP messages.
|
||||
*/
|
||||
@JSONField(name = "can_handle_mcp_message")
|
||||
boolean canHandleMcpMessage;
|
||||
|
||||
/**
|
||||
* Checks if the CLI can handle can_use_tool requests.
|
||||
*
|
||||
* @return true if the CLI can handle can_use_tool requests, false otherwise
|
||||
*/
|
||||
public boolean isCanHandleCanUseTool() {
|
||||
return canHandleCanUseTool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the CLI can handle can_use_tool requests.
|
||||
*
|
||||
* @param canHandleCanUseTool Whether the CLI can handle can_use_tool requests
|
||||
*/
|
||||
public void setCanHandleCanUseTool(boolean canHandleCanUseTool) {
|
||||
this.canHandleCanUseTool = canHandleCanUseTool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CLI can handle hook callbacks.
|
||||
*
|
||||
* @return true if the CLI can handle hook callbacks, false otherwise
|
||||
*/
|
||||
public boolean isCanHandleHookCallback() {
|
||||
return canHandleHookCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the CLI can handle hook callbacks.
|
||||
*
|
||||
* @param canHandleHookCallback Whether the CLI can handle hook callbacks
|
||||
*/
|
||||
public void setCanHandleHookCallback(boolean canHandleHookCallback) {
|
||||
this.canHandleHookCallback = canHandleHookCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CLI can set permission mode.
|
||||
*
|
||||
* @return true if the CLI can set permission mode, false otherwise
|
||||
*/
|
||||
public boolean isCanSetPermissionMode() {
|
||||
return canSetPermissionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the CLI can set permission mode.
|
||||
*
|
||||
* @param canSetPermissionMode Whether the CLI can set permission mode
|
||||
*/
|
||||
public void setCanSetPermissionMode(boolean canSetPermissionMode) {
|
||||
this.canSetPermissionMode = canSetPermissionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CLI can set the model.
|
||||
*
|
||||
* @return true if the CLI can set the model, false otherwise
|
||||
*/
|
||||
public boolean isCanSetModel() {
|
||||
return canSetModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the CLI can set the model.
|
||||
*
|
||||
* @param canSetModel Whether the CLI can set the model
|
||||
*/
|
||||
public void setCanSetModel(boolean canSetModel) {
|
||||
this.canSetModel = canSetModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the CLI can handle MCP messages.
|
||||
*
|
||||
* @return true if the CLI can handle MCP messages, false otherwise
|
||||
*/
|
||||
public boolean isCanHandleMcpMessage() {
|
||||
return canHandleMcpMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the CLI can handle MCP messages.
|
||||
*
|
||||
* @param canHandleMcpMessage Whether the CLI can handle MCP messages
|
||||
*/
|
||||
public void setCanHandleMcpMessage(boolean canHandleMcpMessage) {
|
||||
this.canHandleMcpMessage = canHandleMcpMessage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* Extends the Usage class with additional usage information.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class ExtendedUsage extends Usage {
|
||||
/**
|
||||
* Server tool use information.
|
||||
*/
|
||||
@JSONField(name = "server_tool_use")
|
||||
private ServerToolUse serverToolUse;
|
||||
|
||||
/**
|
||||
* Service tier information.
|
||||
*/
|
||||
@JSONField(name = "service_tier")
|
||||
private String serviceTier;
|
||||
|
||||
/**
|
||||
* Cache creation information.
|
||||
*/
|
||||
@JSONField(name = "cache_creation")
|
||||
private CacheCreation cacheCreation;
|
||||
|
||||
/**
|
||||
* Gets the server tool use information.
|
||||
*
|
||||
* @return The server tool use information
|
||||
*/
|
||||
public ServerToolUse getServerToolUse() {
|
||||
return serverToolUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server tool use information.
|
||||
*
|
||||
* @param serverToolUse The server tool use information
|
||||
*/
|
||||
public void setServerToolUse(ServerToolUse serverToolUse) {
|
||||
this.serverToolUse = serverToolUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the service tier information.
|
||||
*
|
||||
* @return The service tier information
|
||||
*/
|
||||
public String getServiceTier() {
|
||||
return serviceTier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service tier information.
|
||||
*
|
||||
* @param serviceTier The service tier information
|
||||
*/
|
||||
public void setServiceTier(String serviceTier) {
|
||||
this.serviceTier = serviceTier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache creation information.
|
||||
*
|
||||
* @return The cache creation information
|
||||
*/
|
||||
public CacheCreation getCacheCreation() {
|
||||
return cacheCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cache creation information.
|
||||
*
|
||||
* @param cacheCreation The cache creation information
|
||||
*/
|
||||
public void setCacheCreation(CacheCreation cacheCreation) {
|
||||
this.cacheCreation = cacheCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents server tool use information.
|
||||
*/
|
||||
public static class ServerToolUse {
|
||||
/**
|
||||
* Number of web search requests.
|
||||
*/
|
||||
@JSONField(name = "web_search_requests")
|
||||
private int webSearchRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents cache creation information.
|
||||
*/
|
||||
public static class CacheCreation {
|
||||
/**
|
||||
* Number of ephemeral 1-hour input tokens.
|
||||
*/
|
||||
@JSONField(name = "ephemeral_1h_input_tokens")
|
||||
private int ephemeral1hInputTokens;
|
||||
|
||||
/**
|
||||
* Number of ephemeral 5-minute input tokens.
|
||||
*/
|
||||
@JSONField(name = "ephemeral_5m_input_tokens")
|
||||
private int ephemeral5mInputTokens;
|
||||
|
||||
/**
|
||||
* Gets the number of ephemeral 1-hour input tokens.
|
||||
*
|
||||
* @return The number of ephemeral 1-hour input tokens
|
||||
*/
|
||||
public int getEphemeral1hInputTokens() {
|
||||
return ephemeral1hInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ephemeral 1-hour input tokens.
|
||||
*
|
||||
* @param ephemeral1hInputTokens The number of ephemeral 1-hour input tokens
|
||||
*/
|
||||
public void setEphemeral1hInputTokens(int ephemeral1hInputTokens) {
|
||||
this.ephemeral1hInputTokens = ephemeral1hInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of ephemeral 5-minute input tokens.
|
||||
*
|
||||
* @return The number of ephemeral 5-minute input tokens
|
||||
*/
|
||||
public int getEphemeral5mInputTokens() {
|
||||
return ephemeral5mInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of ephemeral 5-minute input tokens.
|
||||
*
|
||||
* @param ephemeral5mInputTokens The number of ephemeral 5-minute input tokens
|
||||
*/
|
||||
public void setEphemeral5mInputTokens(int ephemeral5mInputTokens) {
|
||||
this.ephemeral5mInputTokens = ephemeral5mInputTokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
/**
|
||||
* Configuration for initializing the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class InitializeConfig {
|
||||
/**
|
||||
* Hooks configuration.
|
||||
*/
|
||||
String hooks;
|
||||
/**
|
||||
* SDK MCP servers configuration.
|
||||
*/
|
||||
String sdkMcpServers;
|
||||
/**
|
||||
* MCP servers configuration.
|
||||
*/
|
||||
String mcpServers;
|
||||
/**
|
||||
* Agents configuration.
|
||||
*/
|
||||
String agents;
|
||||
|
||||
/**
|
||||
* Gets the hooks configuration.
|
||||
*
|
||||
* @return The hooks configuration
|
||||
*/
|
||||
public String getHooks() {
|
||||
return hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hooks configuration.
|
||||
*
|
||||
* @param hooks The hooks configuration
|
||||
*/
|
||||
public void setHooks(String hooks) {
|
||||
this.hooks = hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SDK MCP servers configuration.
|
||||
*
|
||||
* @return The SDK MCP servers configuration
|
||||
*/
|
||||
public String getSdkMcpServers() {
|
||||
return sdkMcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SDK MCP servers configuration.
|
||||
*
|
||||
* @param sdkMcpServers The SDK MCP servers configuration
|
||||
*/
|
||||
public void setSdkMcpServers(String sdkMcpServers) {
|
||||
this.sdkMcpServers = sdkMcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MCP servers configuration.
|
||||
*
|
||||
* @return The MCP servers configuration
|
||||
*/
|
||||
public String getMcpServers() {
|
||||
return mcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MCP servers configuration.
|
||||
*
|
||||
* @param mcpServers The MCP servers configuration
|
||||
*/
|
||||
public void setMcpServers(String mcpServers) {
|
||||
this.mcpServers = mcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the agents configuration.
|
||||
*
|
||||
* @return The agents configuration
|
||||
*/
|
||||
public String getAgents() {
|
||||
return agents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the agents configuration.
|
||||
*
|
||||
* @param agents The agents configuration
|
||||
*/
|
||||
public void setAgents(String agents) {
|
||||
this.agents = agents;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
/**
|
||||
* Represents usage information for a specific model.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class ModelUsage {
|
||||
/**
|
||||
* Number of input tokens.
|
||||
*/
|
||||
private int inputTokens;
|
||||
/**
|
||||
* Number of output tokens.
|
||||
*/
|
||||
private int outputTokens;
|
||||
/**
|
||||
* Number of cache read input tokens.
|
||||
*/
|
||||
private int cacheReadInputTokens;
|
||||
/**
|
||||
* Number of cache creation input tokens.
|
||||
*/
|
||||
private int cacheCreationInputTokens;
|
||||
/**
|
||||
* Number of web search requests.
|
||||
*/
|
||||
private int webSearchRequests;
|
||||
/**
|
||||
* Context window size.
|
||||
*/
|
||||
private int contextWindow;
|
||||
|
||||
/**
|
||||
* Gets the number of input tokens.
|
||||
*
|
||||
* @return The number of input tokens
|
||||
*/
|
||||
public int getInputTokens() {
|
||||
return inputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of input tokens.
|
||||
*
|
||||
* @param inputTokens The number of input tokens
|
||||
*/
|
||||
public void setInputTokens(int inputTokens) {
|
||||
this.inputTokens = inputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of output tokens.
|
||||
*
|
||||
* @return The number of output tokens
|
||||
*/
|
||||
public int getOutputTokens() {
|
||||
return outputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of output tokens.
|
||||
*
|
||||
* @param outputTokens The number of output tokens
|
||||
*/
|
||||
public void setOutputTokens(int outputTokens) {
|
||||
this.outputTokens = outputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of cache read input tokens.
|
||||
*
|
||||
* @return The number of cache read input tokens
|
||||
*/
|
||||
public int getCacheReadInputTokens() {
|
||||
return cacheReadInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of cache read input tokens.
|
||||
*
|
||||
* @param cacheReadInputTokens The number of cache read input tokens
|
||||
*/
|
||||
public void setCacheReadInputTokens(int cacheReadInputTokens) {
|
||||
this.cacheReadInputTokens = cacheReadInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of cache creation input tokens.
|
||||
*
|
||||
* @return The number of cache creation input tokens
|
||||
*/
|
||||
public int getCacheCreationInputTokens() {
|
||||
return cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of cache creation input tokens.
|
||||
*
|
||||
* @param cacheCreationInputTokens The number of cache creation input tokens
|
||||
*/
|
||||
public void setCacheCreationInputTokens(int cacheCreationInputTokens) {
|
||||
this.cacheCreationInputTokens = cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of web search requests.
|
||||
*
|
||||
* @return The number of web search requests
|
||||
*/
|
||||
public int getWebSearchRequests() {
|
||||
return webSearchRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of web search requests.
|
||||
*
|
||||
* @param webSearchRequests The number of web search requests
|
||||
*/
|
||||
public void setWebSearchRequests(int webSearchRequests) {
|
||||
this.webSearchRequests = webSearchRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context window size.
|
||||
*
|
||||
* @return The context window size
|
||||
*/
|
||||
public int getContextWindow() {
|
||||
return contextWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context window size.
|
||||
*
|
||||
* @param contextWindow The context window size
|
||||
*/
|
||||
public void setContextWindow(int contextWindow) {
|
||||
this.contextWindow = contextWindow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
/**
|
||||
* Represents different permission modes for the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public enum PermissionMode {
|
||||
/**
|
||||
* Default permission mode.
|
||||
*/
|
||||
DEFAULT("default"),
|
||||
/**
|
||||
* Plan permission mode.
|
||||
*/
|
||||
PLAN("plan"),
|
||||
/**
|
||||
* Auto-edit permission mode.
|
||||
*/
|
||||
AUTO_EDIT("auto-edit"),
|
||||
/**
|
||||
* YOLO permission mode.
|
||||
*/
|
||||
YOLO("yolo");
|
||||
|
||||
private final String value;
|
||||
|
||||
PermissionMode(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string value of the permission mode.
|
||||
*
|
||||
* @return The string value of the permission mode
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permission mode from its string value.
|
||||
*
|
||||
* @param value The string value
|
||||
* @return The corresponding permission mode
|
||||
*/
|
||||
public static PermissionMode fromValue(String value) {
|
||||
for (PermissionMode mode : PermissionMode.values()) {
|
||||
if (mode.value.equals(value)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown permission mode: " + value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* Represents usage information for a message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class Usage {
|
||||
/**
|
||||
* Number of input tokens.
|
||||
*/
|
||||
@JSONField(name = "input_tokens")
|
||||
private Integer inputTokens;
|
||||
/**
|
||||
* Number of output tokens.
|
||||
*/
|
||||
@JSONField(name = "output_tokens")
|
||||
private Integer outputTokens;
|
||||
/**
|
||||
* Number of cache creation input tokens.
|
||||
*/
|
||||
@JSONField(name = "cache_creation_input_tokens")
|
||||
private Integer cacheCreationInputTokens;
|
||||
/**
|
||||
* Number of cache read input tokens.
|
||||
*/
|
||||
@JSONField(name = "cache_read_input_tokens")
|
||||
private Integer cacheReadInputTokens;
|
||||
/**
|
||||
* Total number of tokens.
|
||||
*/
|
||||
@JSONField(name = "total_tokens")
|
||||
private Integer totalTokens;
|
||||
|
||||
/**
|
||||
* Gets the number of input tokens.
|
||||
*
|
||||
* @return The number of input tokens
|
||||
*/
|
||||
public Integer getInputTokens() {
|
||||
return inputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of input tokens.
|
||||
*
|
||||
* @param inputTokens The number of input tokens
|
||||
*/
|
||||
public void setInputTokens(Integer inputTokens) {
|
||||
this.inputTokens = inputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of output tokens.
|
||||
*
|
||||
* @return The number of output tokens
|
||||
*/
|
||||
public Integer getOutputTokens() {
|
||||
return outputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of output tokens.
|
||||
*
|
||||
* @param outputTokens The number of output tokens
|
||||
*/
|
||||
public void setOutputTokens(Integer outputTokens) {
|
||||
this.outputTokens = outputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of cache creation input tokens.
|
||||
*
|
||||
* @return The number of cache creation input tokens
|
||||
*/
|
||||
public Integer getCacheCreationInputTokens() {
|
||||
return cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of cache creation input tokens.
|
||||
*
|
||||
* @param cacheCreationInputTokens The number of cache creation input tokens
|
||||
*/
|
||||
public void setCacheCreationInputTokens(Integer cacheCreationInputTokens) {
|
||||
this.cacheCreationInputTokens = cacheCreationInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of cache read input tokens.
|
||||
*
|
||||
* @return The number of cache read input tokens
|
||||
*/
|
||||
public Integer getCacheReadInputTokens() {
|
||||
return cacheReadInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of cache read input tokens.
|
||||
*
|
||||
* @param cacheReadInputTokens The number of cache read input tokens
|
||||
*/
|
||||
public void setCacheReadInputTokens(Integer cacheReadInputTokens) {
|
||||
this.cacheReadInputTokens = cacheReadInputTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total number of tokens.
|
||||
*
|
||||
* @return The total number of tokens
|
||||
*/
|
||||
public Integer getTotalTokens() {
|
||||
return totalTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of tokens.
|
||||
*
|
||||
* @param totalTokens The total number of tokens
|
||||
*/
|
||||
public void setTotalTokens(Integer totalTokens) {
|
||||
this.totalTokens = totalTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>toString.</p>
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data.behavior;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents an allow behavior that permits an operation.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "operation", typeName = "allow")
|
||||
public class Allow extends Behavior {
|
||||
/**
|
||||
* Creates a new Allow instance and sets the behavior to allow.
|
||||
*/
|
||||
public Allow() {
|
||||
super();
|
||||
this.behavior = Operation.allow;
|
||||
}
|
||||
/**
|
||||
* Updated input for the operation.
|
||||
*/
|
||||
Map<String, Object> updatedInput;
|
||||
|
||||
/**
|
||||
* Gets the updated input.
|
||||
*
|
||||
* @return The updated input
|
||||
*/
|
||||
public Map<String, Object> getUpdatedInput() {
|
||||
return updatedInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the updated input.
|
||||
*
|
||||
* @param updatedInput The updated input
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public Allow setUpdatedInput(Map<String, Object> updatedInput) {
|
||||
this.updatedInput = updatedInput;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data.behavior;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Base class for behavior objects that define how the CLI should handle requests.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "operation", typeName = "Behavior", seeAlso = {Allow.class, Deny.class})
|
||||
public class Behavior {
|
||||
/**
|
||||
* The behavior operation (allow or deny).
|
||||
*/
|
||||
Operation behavior;
|
||||
|
||||
/**
|
||||
* Gets the behavior operation.
|
||||
*
|
||||
* @return The behavior operation
|
||||
*/
|
||||
public Operation getBehavior() {
|
||||
return behavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the behavior operation.
|
||||
*
|
||||
* @param behavior The behavior operation
|
||||
*/
|
||||
public void setBehavior(Operation behavior) {
|
||||
this.behavior = behavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the type of operation.
|
||||
*/
|
||||
public enum Operation {
|
||||
/**
|
||||
* Allow the operation.
|
||||
*/
|
||||
allow,
|
||||
/**
|
||||
* Deny the operation.
|
||||
*/
|
||||
deny
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default behavior (deny with message).
|
||||
*
|
||||
* @return The default behavior
|
||||
*/
|
||||
public static Behavior defaultBehavior() {
|
||||
return denyBehavior();
|
||||
}
|
||||
|
||||
public static Behavior denyBehavior() {
|
||||
return new Deny().setMessage("Default Behavior Permission denied");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.data.behavior;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a deny behavior that rejects an operation.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "operation", typeName = "deny")
|
||||
public class Deny extends Behavior {
|
||||
/**
|
||||
* Creates a new Deny instance and sets the behavior to deny.
|
||||
*/
|
||||
public Deny() {
|
||||
super();
|
||||
this.behavior = Operation.deny;
|
||||
}
|
||||
|
||||
/**
|
||||
* The message explaining why the operation was denied.
|
||||
*/
|
||||
String message;
|
||||
|
||||
/**
|
||||
* Gets the denial message.
|
||||
*
|
||||
* @return The denial message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the denial message.
|
||||
*
|
||||
* @param message The denial message
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public Deny setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
/**
|
||||
* Represents a message in the Qwen Code protocol.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public interface Message {
|
||||
/**
|
||||
* Gets the type of the message.
|
||||
*
|
||||
* @return The type of the message
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* Gets the ID of the message.
|
||||
*
|
||||
* @return The ID of the message
|
||||
*/
|
||||
String getMessageId();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Base class for messages in the Qwen Code protocol.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(alphabetic = false, typeKey = "type", typeName = "MessageBase")
|
||||
public class MessageBase implements Message{
|
||||
/**
|
||||
* The type of the message.
|
||||
*/
|
||||
protected String type;
|
||||
|
||||
/**
|
||||
* The ID of the message.
|
||||
*/
|
||||
@JSONField(name = "message_id")
|
||||
protected String messageId;
|
||||
|
||||
/**
|
||||
* <p>toString.</p>
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the message.
|
||||
*
|
||||
* @param type The type of the message
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the message.
|
||||
*
|
||||
* @param messageId The ID of the message
|
||||
*/
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.CLIPermissionDenial;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.ExtendedUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Usage;
|
||||
|
||||
/**
|
||||
* Represents a result message from the SDK.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "result")
|
||||
public class SDKResultMessage extends MessageBase {
|
||||
/**
|
||||
* The subtype of the result.
|
||||
*/
|
||||
private String subtype; // 'error_max_turns' | 'error_during_execution'
|
||||
/**
|
||||
* The UUID of the message.
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* Whether the result represents an error.
|
||||
*/
|
||||
@JSONField(name = "is_error")
|
||||
private boolean isError = true;
|
||||
|
||||
/**
|
||||
* Duration in milliseconds.
|
||||
*/
|
||||
@JSONField(name = "duration_ms")
|
||||
private Long durationMs;
|
||||
|
||||
/**
|
||||
* API duration in milliseconds.
|
||||
*/
|
||||
@JSONField(name = "duration_api_ms")
|
||||
private Long durationApiMs;
|
||||
|
||||
/**
|
||||
* Number of turns.
|
||||
*/
|
||||
@JSONField(name = "num_turns")
|
||||
private Integer numTurns;
|
||||
/**
|
||||
* Usage information.
|
||||
*/
|
||||
private ExtendedUsage usage;
|
||||
/**
|
||||
* Model usage information.
|
||||
*/
|
||||
private Map<String, Usage> modelUsage;
|
||||
|
||||
/**
|
||||
* List of permission denials.
|
||||
*/
|
||||
@JSONField(name = "permission_denials")
|
||||
private List<CLIPermissionDenial> permissionDenials;
|
||||
/**
|
||||
* Error information.
|
||||
*/
|
||||
private Error error;
|
||||
|
||||
/**
|
||||
* Creates a new SDKResultMessage instance and sets the type to "result".
|
||||
*/
|
||||
public SDKResultMessage() {
|
||||
super();
|
||||
this.type = "result";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subtype of the result.
|
||||
*
|
||||
* @return The subtype of the result
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subtype of the result.
|
||||
*
|
||||
* @param subtype The subtype of the result
|
||||
*/
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the message.
|
||||
*
|
||||
* @return The UUID of the message
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UUID of the message.
|
||||
*
|
||||
* @param uuid The UUID of the message
|
||||
*/
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return The session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param sessionId The session ID
|
||||
*/
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the result represents an error.
|
||||
*
|
||||
* @return Whether the result represents an error
|
||||
*/
|
||||
public boolean isError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the result represents an error.
|
||||
*
|
||||
* @param error Whether the result represents an error
|
||||
*/
|
||||
public void setError(boolean error) {
|
||||
isError = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration in milliseconds.
|
||||
*
|
||||
* @return The duration in milliseconds
|
||||
*/
|
||||
public Long getDurationMs() {
|
||||
return durationMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration in milliseconds.
|
||||
*
|
||||
* @param durationMs The duration in milliseconds
|
||||
*/
|
||||
public void setDurationMs(Long durationMs) {
|
||||
this.durationMs = durationMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API duration in milliseconds.
|
||||
*
|
||||
* @return The API duration in milliseconds
|
||||
*/
|
||||
public Long getDurationApiMs() {
|
||||
return durationApiMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the API duration in milliseconds.
|
||||
*
|
||||
* @param durationApiMs The API duration in milliseconds
|
||||
*/
|
||||
public void setDurationApiMs(Long durationApiMs) {
|
||||
this.durationApiMs = durationApiMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of turns.
|
||||
*
|
||||
* @return The number of turns
|
||||
*/
|
||||
public Integer getNumTurns() {
|
||||
return numTurns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of turns.
|
||||
*
|
||||
* @param numTurns The number of turns
|
||||
*/
|
||||
public void setNumTurns(Integer numTurns) {
|
||||
this.numTurns = numTurns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage information.
|
||||
*
|
||||
* @return The usage information
|
||||
*/
|
||||
public ExtendedUsage getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usage information.
|
||||
*
|
||||
* @param usage The usage information
|
||||
*/
|
||||
public void setUsage(ExtendedUsage usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model usage information.
|
||||
*
|
||||
* @return The model usage information
|
||||
*/
|
||||
public Map<String, Usage> getModelUsage() {
|
||||
return modelUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model usage information.
|
||||
*
|
||||
* @param modelUsage The model usage information
|
||||
*/
|
||||
public void setModelUsage(Map<String, Usage> modelUsage) {
|
||||
this.modelUsage = modelUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of permission denials.
|
||||
*
|
||||
* @return The list of permission denials
|
||||
*/
|
||||
public List<CLIPermissionDenial> getPermissionDenials() {
|
||||
return permissionDenials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of permission denials.
|
||||
*
|
||||
* @param permissionDenials The list of permission denials
|
||||
*/
|
||||
public void setPermissionDenials(List<CLIPermissionDenial> permissionDenials) {
|
||||
this.permissionDenials = permissionDenials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error information.
|
||||
*
|
||||
* @return The error information
|
||||
*/
|
||||
public Error getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error information.
|
||||
*
|
||||
* @param error The error information
|
||||
*/
|
||||
public void setError(Error error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents error information.
|
||||
*/
|
||||
public static class Error {
|
||||
/**
|
||||
* Error type.
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* Error message.
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* Gets the error type.
|
||||
*
|
||||
* @return The error type
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error type.
|
||||
*
|
||||
* @param type The error type
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error message.
|
||||
*
|
||||
* @return The error message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error message.
|
||||
*
|
||||
* @param message The error message
|
||||
*/
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a system message from the SDK.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "system")
|
||||
public class SDKSystemMessage extends MessageBase {
|
||||
/**
|
||||
* The subtype of the system message.
|
||||
*/
|
||||
private String subtype;
|
||||
/**
|
||||
* The UUID of the message.
|
||||
*/
|
||||
private String uuid;
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
/**
|
||||
* Additional data.
|
||||
*/
|
||||
private Object data;
|
||||
/**
|
||||
* Current working directory.
|
||||
*/
|
||||
private String cwd;
|
||||
/**
|
||||
* List of available tools.
|
||||
*/
|
||||
private List<String> tools;
|
||||
/**
|
||||
* List of MCP servers.
|
||||
*/
|
||||
@JSONField(name = "mcp_servers")
|
||||
private List<McpServer> mcpServers;
|
||||
/**
|
||||
* Model information.
|
||||
*/
|
||||
private String model;
|
||||
/**
|
||||
* Permission mode.
|
||||
*/
|
||||
@JSONField(name = "permission_mode")
|
||||
private String permissionMode;
|
||||
/**
|
||||
* Available slash commands.
|
||||
*/
|
||||
@JSONField(name = "slash_commands")
|
||||
private List<String> slashCommands;
|
||||
/**
|
||||
* Qwen Code version.
|
||||
*/
|
||||
@JSONField(name = "qwen_code_version")
|
||||
private String qwenCodeVersion;
|
||||
/**
|
||||
* Output style.
|
||||
*/
|
||||
@JSONField(name = "output_style")
|
||||
private String outputStyle;
|
||||
/**
|
||||
* Available agents.
|
||||
*/
|
||||
private List<String> agents;
|
||||
/**
|
||||
* Available skills.
|
||||
*/
|
||||
private List<String> skills;
|
||||
/**
|
||||
* Capabilities information.
|
||||
*/
|
||||
private Map<String, Object> capabilities;
|
||||
/**
|
||||
* Compact metadata.
|
||||
*/
|
||||
@JSONField(name = "compact_metadata")
|
||||
private CompactMetadata compactMetadata;
|
||||
|
||||
/**
|
||||
* Creates a new SDKSystemMessage instance and sets the type to "system".
|
||||
*/
|
||||
public SDKSystemMessage() {
|
||||
super();
|
||||
this.type = "system";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subtype of the system message.
|
||||
*
|
||||
* @return The subtype of the system message
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subtype of the system message.
|
||||
*
|
||||
* @param subtype The subtype of the system message
|
||||
*/
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the message.
|
||||
*
|
||||
* @return The UUID of the message
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UUID of the message.
|
||||
*
|
||||
* @param uuid The UUID of the message
|
||||
*/
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return The session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param sessionId The session ID
|
||||
*/
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the additional data.
|
||||
*
|
||||
* @return The additional data
|
||||
*/
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the additional data.
|
||||
*
|
||||
* @param data The additional data
|
||||
*/
|
||||
public void setData(Object data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current working directory.
|
||||
*
|
||||
* @return The current working directory
|
||||
*/
|
||||
public String getCwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current working directory.
|
||||
*
|
||||
* @param cwd The current working directory
|
||||
*/
|
||||
public void setCwd(String cwd) {
|
||||
this.cwd = cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of available tools.
|
||||
*
|
||||
* @return The list of available tools
|
||||
*/
|
||||
public List<String> getTools() {
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of available tools.
|
||||
*
|
||||
* @param tools The list of available tools
|
||||
*/
|
||||
public void setTools(List<String> tools) {
|
||||
this.tools = tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of MCP servers.
|
||||
*
|
||||
* @return The list of MCP servers
|
||||
*/
|
||||
public List<McpServer> getMcpServers() {
|
||||
return mcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of MCP servers.
|
||||
*
|
||||
* @param mcpServers The list of MCP servers
|
||||
*/
|
||||
public void setMcpServers(List<McpServer> mcpServers) {
|
||||
this.mcpServers = mcpServers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model information.
|
||||
*
|
||||
* @return The model information
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model information.
|
||||
*
|
||||
* @param model The model information
|
||||
*/
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permission mode.
|
||||
*
|
||||
* @return The permission mode
|
||||
*/
|
||||
public String getPermissionMode() {
|
||||
return permissionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission mode.
|
||||
*
|
||||
* @param permissionMode The permission mode
|
||||
*/
|
||||
public void setPermissionMode(String permissionMode) {
|
||||
this.permissionMode = permissionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available slash commands.
|
||||
*
|
||||
* @return The available slash commands
|
||||
*/
|
||||
public List<String> getSlashCommands() {
|
||||
return slashCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the available slash commands.
|
||||
*
|
||||
* @param slashCommands The available slash commands
|
||||
*/
|
||||
public void setSlashCommands(List<String> slashCommands) {
|
||||
this.slashCommands = slashCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Qwen Code version.
|
||||
*
|
||||
* @return The Qwen Code version
|
||||
*/
|
||||
public String getQwenCodeVersion() {
|
||||
return qwenCodeVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Qwen Code version.
|
||||
*
|
||||
* @param qwenCodeVersion The Qwen Code version
|
||||
*/
|
||||
public void setQwenCodeVersion(String qwenCodeVersion) {
|
||||
this.qwenCodeVersion = qwenCodeVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output style.
|
||||
*
|
||||
* @return The output style
|
||||
*/
|
||||
public String getOutputStyle() {
|
||||
return outputStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output style.
|
||||
*
|
||||
* @param outputStyle The output style
|
||||
*/
|
||||
public void setOutputStyle(String outputStyle) {
|
||||
this.outputStyle = outputStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available agents.
|
||||
*
|
||||
* @return The available agents
|
||||
*/
|
||||
public List<String> getAgents() {
|
||||
return agents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the available agents.
|
||||
*
|
||||
* @param agents The available agents
|
||||
*/
|
||||
public void setAgents(List<String> agents) {
|
||||
this.agents = agents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the available skills.
|
||||
*
|
||||
* @return The available skills
|
||||
*/
|
||||
public List<String> getSkills() {
|
||||
return skills;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the available skills.
|
||||
*
|
||||
* @param skills The available skills
|
||||
*/
|
||||
public void setSkills(List<String> skills) {
|
||||
this.skills = skills;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the capabilities information.
|
||||
*
|
||||
* @return The capabilities information
|
||||
*/
|
||||
public Map<String, Object> getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the capabilities information.
|
||||
*
|
||||
* @param capabilities The capabilities information
|
||||
*/
|
||||
public void setCapabilities(Map<String, Object> capabilities) {
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the compact metadata.
|
||||
*
|
||||
* @return The compact metadata
|
||||
*/
|
||||
public CompactMetadata getCompactMetadata() {
|
||||
return compactMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the compact metadata.
|
||||
*
|
||||
* @param compactMetadata The compact metadata
|
||||
*/
|
||||
public void setCompactMetadata(CompactMetadata compactMetadata) {
|
||||
this.compactMetadata = compactMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents MCP server information.
|
||||
*/
|
||||
public static class McpServer {
|
||||
/**
|
||||
* Server name.
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* Server status.
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* Gets the server name.
|
||||
*
|
||||
* @return The server name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server name.
|
||||
*
|
||||
* @param name The server name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server status.
|
||||
*
|
||||
* @return The server status
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the server status.
|
||||
*
|
||||
* @param status The server status
|
||||
*/
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents compact metadata.
|
||||
*/
|
||||
public static class CompactMetadata {
|
||||
/**
|
||||
* Trigger information.
|
||||
*/
|
||||
private String trigger;
|
||||
|
||||
/**
|
||||
* Pre-tokens information.
|
||||
*/
|
||||
@JSONField(name = "pre_tokens")
|
||||
private Integer preTokens;
|
||||
|
||||
/**
|
||||
* Gets the trigger information.
|
||||
*
|
||||
* @return The trigger information
|
||||
*/
|
||||
public String getTrigger() {
|
||||
return trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trigger information.
|
||||
*
|
||||
* @param trigger The trigger information
|
||||
*/
|
||||
public void setTrigger(String trigger) {
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pre-tokens information.
|
||||
*
|
||||
* @return The pre-tokens information
|
||||
*/
|
||||
public Integer getPreTokens() {
|
||||
return preTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pre-tokens information.
|
||||
*
|
||||
* @param preTokens The pre-tokens information
|
||||
*/
|
||||
public void setPreTokens(Integer preTokens) {
|
||||
this.preTokens = preTokens;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a user message in the SDK protocol.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "user")
|
||||
public class SDKUserMessage extends MessageBase {
|
||||
/**
|
||||
* The UUID of the message.
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
/**
|
||||
* The API user message.
|
||||
*/
|
||||
private final APIUserMessage message = new APIUserMessage();
|
||||
|
||||
/**
|
||||
* The parent tool use ID.
|
||||
*/
|
||||
@JSONField(name = "parent_tool_use_id")
|
||||
private String parentToolUseId;
|
||||
/**
|
||||
* Additional options.
|
||||
*/
|
||||
private Map<String, String> options;
|
||||
|
||||
/**
|
||||
* Creates a new SDKUserMessage instance and sets the type to "user".
|
||||
*/
|
||||
public SDKUserMessage() {
|
||||
super();
|
||||
this.setType("user");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the message.
|
||||
*
|
||||
* @return The UUID of the message
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UUID of the message.
|
||||
*
|
||||
* @param uuid The UUID of the message
|
||||
*/
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return The session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param sessionId The session ID
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SDKUserMessage setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content of the message.
|
||||
*
|
||||
* @param content The content of the message
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SDKUserMessage setContent(String content) {
|
||||
message.setContent(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the message.
|
||||
*
|
||||
* @return The content of the message
|
||||
*/
|
||||
public String getContent() {
|
||||
return message.getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent tool use ID.
|
||||
*
|
||||
* @return The parent tool use ID
|
||||
*/
|
||||
public String getParentToolUseId() {
|
||||
return parentToolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent tool use ID.
|
||||
*
|
||||
* @param parentToolUseId The parent tool use ID
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SDKUserMessage setParentToolUseId(String parentToolUseId) {
|
||||
this.parentToolUseId = parentToolUseId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the additional options.
|
||||
*
|
||||
* @return The additional options
|
||||
*/
|
||||
public Map<String, String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the additional options.
|
||||
*
|
||||
* @param options The additional options
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SDKUserMessage setOptions(Map<String, String> options) {
|
||||
this.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the API user message.
|
||||
*/
|
||||
public static class APIUserMessage {
|
||||
/**
|
||||
* User role.
|
||||
*/
|
||||
private String role = "user";
|
||||
/**
|
||||
* Message content.
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* Gets the user role.
|
||||
*
|
||||
* @return The user role
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user role.
|
||||
*
|
||||
* @param role The user role
|
||||
*/
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message content.
|
||||
*
|
||||
* @return The message content
|
||||
*/
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message content.
|
||||
*
|
||||
* @param content The message content
|
||||
*/
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Usage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.block.ContentBlock;
|
||||
|
||||
/**
|
||||
* Represents an API assistant message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class APIAssistantMessage {
|
||||
/**
|
||||
* Message ID.
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* Message type.
|
||||
*/
|
||||
private String type = "message";
|
||||
/**
|
||||
* Message role.
|
||||
*/
|
||||
private String role = "assistant";
|
||||
/**
|
||||
* Message model.
|
||||
*/
|
||||
private String model;
|
||||
/**
|
||||
* Message content.
|
||||
*/
|
||||
private List<ContentBlock<?>> content;
|
||||
|
||||
/**
|
||||
* Stop reason.
|
||||
*/
|
||||
@JSONField(name = "stop_reason")
|
||||
private String stopReason;
|
||||
/**
|
||||
* Usage information.
|
||||
*/
|
||||
private Usage usage;
|
||||
|
||||
/**
|
||||
* Gets the message ID.
|
||||
*
|
||||
* @return The message ID
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message ID.
|
||||
*
|
||||
* @param id The message ID
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message type.
|
||||
*
|
||||
* @return The message type
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message type.
|
||||
*
|
||||
* @param type The message type
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message role.
|
||||
*
|
||||
* @return The message role
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message role.
|
||||
*
|
||||
* @param role The message role
|
||||
*/
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message model.
|
||||
*
|
||||
* @return The message model
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message model.
|
||||
*
|
||||
* @param model The message model
|
||||
*/
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stop reason.
|
||||
*
|
||||
* @return The stop reason
|
||||
*/
|
||||
public String getStopReason() {
|
||||
return stopReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stop reason.
|
||||
*
|
||||
* @param stopReason The stop reason
|
||||
*/
|
||||
public void setStopReason(String stopReason) {
|
||||
this.stopReason = stopReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the usage information.
|
||||
*
|
||||
* @return The usage information
|
||||
*/
|
||||
public Usage getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usage information.
|
||||
*
|
||||
* @param usage The usage information
|
||||
*/
|
||||
public void setUsage(Usage usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message content.
|
||||
*
|
||||
* @return The message content
|
||||
*/
|
||||
public List<ContentBlock<?>> getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message content.
|
||||
*
|
||||
* @param content The message content
|
||||
*/
|
||||
public void setContent(List<ContentBlock<?>> content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
|
||||
/**
|
||||
* Represents an SDK assistant message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "assistant")
|
||||
public class SDKAssistantMessage extends MessageBase {
|
||||
/**
|
||||
* The UUID of the message.
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
/**
|
||||
* The API assistant message.
|
||||
*/
|
||||
private APIAssistantMessage message;
|
||||
|
||||
/**
|
||||
* The parent tool use ID.
|
||||
*/
|
||||
@JSONField(name = "parent_tool_use_id")
|
||||
private String parentToolUseId;
|
||||
|
||||
/**
|
||||
* Creates a new SDKAssistantMessage instance and sets the type to "assistant".
|
||||
*/
|
||||
public SDKAssistantMessage() {
|
||||
super();
|
||||
this.type = "assistant";
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return this.getUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the message.
|
||||
*
|
||||
* @return The UUID of the message
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UUID of the message.
|
||||
*
|
||||
* @param uuid The UUID of the message
|
||||
*/
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return The session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param sessionId The session ID
|
||||
*/
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API assistant message.
|
||||
*
|
||||
* @return The API assistant message
|
||||
*/
|
||||
public APIAssistantMessage getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the API assistant message.
|
||||
*
|
||||
* @param message The API assistant message
|
||||
*/
|
||||
public void setMessage(APIAssistantMessage message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent tool use ID.
|
||||
*
|
||||
* @return The parent tool use ID
|
||||
*/
|
||||
public String getParentToolUseId() {
|
||||
return parentToolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent tool use ID.
|
||||
*
|
||||
* @param parentToolUseId The parent tool use ID
|
||||
*/
|
||||
public void setParentToolUseId(String parentToolUseId) {
|
||||
this.parentToolUseId = parentToolUseId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.event.StreamEvent;
|
||||
|
||||
/**
|
||||
* Represents a partial assistant message during streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "stream_event")
|
||||
public class SDKPartialAssistantMessage extends MessageBase {
|
||||
/**
|
||||
* The UUID of the message.
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* The session ID.
|
||||
*/
|
||||
@JSONField(name = "session_id")
|
||||
private String sessionId;
|
||||
/**
|
||||
* The stream event.
|
||||
*/
|
||||
private StreamEvent event;
|
||||
|
||||
/**
|
||||
* The parent tool use ID.
|
||||
*/
|
||||
@JSONField(name = "parent_tool_use_id")
|
||||
private String parentToolUseId;
|
||||
|
||||
/**
|
||||
* Creates a new SDKPartialAssistantMessage instance and sets the type to "stream_event".
|
||||
*/
|
||||
public SDKPartialAssistantMessage() {
|
||||
super();
|
||||
this.type = "stream_event";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the message.
|
||||
*
|
||||
* @return The UUID of the message
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UUID of the message.
|
||||
*
|
||||
* @param uuid The UUID of the message
|
||||
*/
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID.
|
||||
*
|
||||
* @return The session ID
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*
|
||||
* @param sessionId The session ID
|
||||
*/
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stream event.
|
||||
*
|
||||
* @return The stream event
|
||||
*/
|
||||
public StreamEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stream event.
|
||||
*
|
||||
* @param event The stream event
|
||||
*/
|
||||
public void setEvent(StreamEvent event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent tool use ID.
|
||||
*
|
||||
* @return The parent tool use ID
|
||||
*/
|
||||
public String getParentToolUseId() {
|
||||
return parentToolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent tool use ID.
|
||||
*
|
||||
* @param parentToolUseId The parent tool use ID
|
||||
*/
|
||||
public void setParentToolUseId(String parentToolUseId) {
|
||||
this.parentToolUseId = parentToolUseId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
|
||||
/**
|
||||
* Represents an annotation for a content block.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class Annotation {
|
||||
/**
|
||||
* The annotation type.
|
||||
*/
|
||||
@JSONField(name = "type")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* The annotation value.
|
||||
*/
|
||||
@JSONField(name = "value")
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* Gets the annotation type.
|
||||
*
|
||||
* @return The annotation type
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the annotation type.
|
||||
*
|
||||
* @param type The annotation type
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation value.
|
||||
*
|
||||
* @return The annotation value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the annotation value.
|
||||
*
|
||||
* @param value The annotation value
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
|
||||
/**
|
||||
* Abstract base class for content blocks in assistant messages.
|
||||
*
|
||||
* @param <C> The type of content
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "ContentBlock", seeAlso = { TextBlock.class, ToolResultBlock.class, ThinkingBlock.class, ToolUseBlock.class })
|
||||
public abstract class ContentBlock<C> implements AssistantContent<C> {
|
||||
/**
|
||||
* The type of the content block.
|
||||
*/
|
||||
protected String type;
|
||||
/**
|
||||
* List of annotations.
|
||||
*/
|
||||
protected List<Annotation> annotations;
|
||||
/**
|
||||
* The message ID.
|
||||
*/
|
||||
protected String messageId;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the content block.
|
||||
*
|
||||
* @param type The type of the content block
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of annotations.
|
||||
*
|
||||
* @return The list of annotations
|
||||
*/
|
||||
public List<Annotation> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of annotations.
|
||||
*
|
||||
* @param annotations The list of annotations
|
||||
*/
|
||||
public void setAnnotations(List<Annotation> annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message ID.
|
||||
*
|
||||
* @param messageId The message ID
|
||||
*/
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>toString.</p>
|
||||
*
|
||||
* @return a {@link java.lang.String} object.
|
||||
*/
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
|
||||
/**
|
||||
* Represents a text content block.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "text")
|
||||
public class TextBlock extends ContentBlock<String> implements TextAssistantContent {
|
||||
/**
|
||||
* The text content.
|
||||
*/
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Gets the text content.
|
||||
*
|
||||
* @return The text content
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text content.
|
||||
*
|
||||
* @param text The text content
|
||||
*/
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getContentOfAssistant() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
|
||||
/**
|
||||
* Represents a thinking content block.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "thinking")
|
||||
public class ThinkingBlock extends ContentBlock<String> implements ThingkingAssistantContent {
|
||||
/**
|
||||
* The thinking content.
|
||||
*/
|
||||
private String thinking;
|
||||
/**
|
||||
* The signature.
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* Gets the thinking content.
|
||||
*
|
||||
* @return The thinking content
|
||||
*/
|
||||
public String getThinking() {
|
||||
return thinking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thinking content.
|
||||
*
|
||||
* @param thinking The thinking content
|
||||
*/
|
||||
public void setThinking(String thinking) {
|
||||
this.thinking = thinking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the signature.
|
||||
*
|
||||
* @return The signature
|
||||
*/
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the signature.
|
||||
*
|
||||
* @param signature The signature
|
||||
*/
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getContentOfAssistant() {
|
||||
return thinking;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
|
||||
/**
|
||||
* Represents a tool result content block.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "tool_result")
|
||||
public class ToolResultBlock extends ContentBlock<String> implements ToolResultAssistantContent {
|
||||
/**
|
||||
* The tool use ID.
|
||||
*/
|
||||
@JSONField(name = "tool_use_id")
|
||||
private String toolUseId;
|
||||
|
||||
/**
|
||||
* The result content.
|
||||
*/
|
||||
@JSONField(name = "content")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* Whether the result is an error.
|
||||
*/
|
||||
@JSONField(name = "is_error")
|
||||
private Boolean isError;
|
||||
|
||||
/**
|
||||
* Gets the tool use ID.
|
||||
*
|
||||
* @return The tool use ID
|
||||
*/
|
||||
public String getToolUseId() {
|
||||
return toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tool use ID.
|
||||
*
|
||||
* @param toolUseId The tool use ID
|
||||
*/
|
||||
public void setToolUseId(String toolUseId) {
|
||||
this.toolUseId = toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result content.
|
||||
*
|
||||
* @return The result content
|
||||
*/
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result content.
|
||||
*
|
||||
* @param content The result content
|
||||
*/
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the result is an error.
|
||||
*
|
||||
* @return Whether the result is an error
|
||||
*/
|
||||
public Boolean getIsError() {
|
||||
return isError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the result is an error.
|
||||
*
|
||||
* @param isError Whether the result is an error
|
||||
*/
|
||||
public void setIsError(Boolean isError) {
|
||||
this.isError = isError;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getContentOfAssistant() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.block;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
|
||||
/**
|
||||
* Represents a tool use content block.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "tool_use")
|
||||
public class ToolUseBlock extends ContentBlock<Map<String, Object>> implements ToolUseAssistantContent {
|
||||
/**
|
||||
* The tool use ID.
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* The tool name.
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* The tool input.
|
||||
*/
|
||||
private Map<String, Object> input;
|
||||
/**
|
||||
* List of annotations.
|
||||
*/
|
||||
private List<Annotation> annotations;
|
||||
|
||||
/**
|
||||
* Creates a new ToolUseBlock instance.
|
||||
*/
|
||||
public ToolUseBlock() {}
|
||||
|
||||
/**
|
||||
* Gets the tool use ID.
|
||||
*
|
||||
* @return The tool use ID
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tool use ID.
|
||||
*
|
||||
* @param id The tool use ID
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tool name.
|
||||
*
|
||||
* @return The tool name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tool name.
|
||||
*
|
||||
* @param name The tool name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tool input.
|
||||
*
|
||||
* @return The tool input
|
||||
*/
|
||||
public Map<String, Object> getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tool input.
|
||||
*
|
||||
* @param input The tool input
|
||||
*/
|
||||
public void setInput(Map<String, Object> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of annotations.
|
||||
*
|
||||
* @return The list of annotations
|
||||
*/
|
||||
public List<Annotation> getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Sets the list of annotations.
|
||||
*/
|
||||
@Override
|
||||
public void setAnnotations(List<Annotation> annotations) {
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Gets the content of the assistant.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getContentOfAssistant() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
|
||||
/**
|
||||
* Represents a content block delta event during streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "content_block_delta")
|
||||
public class ContentBlockDeltaEvent extends StreamEvent {
|
||||
/**
|
||||
* The index of the content block.
|
||||
*/
|
||||
private int index;
|
||||
/**
|
||||
* The content block delta.
|
||||
*/
|
||||
private ContentBlockDelta<?> delta;
|
||||
|
||||
/**
|
||||
* Gets the index of the content block.
|
||||
*
|
||||
* @return The index of the content block
|
||||
*/
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the content block.
|
||||
*
|
||||
* @param index The index of the content block
|
||||
*/
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content block delta.
|
||||
*
|
||||
* @return The content block delta
|
||||
*/
|
||||
public ContentBlockDelta<?> getDelta() {
|
||||
return delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content block delta.
|
||||
*
|
||||
* @param delta The content block delta
|
||||
*/
|
||||
public void setDelta(ContentBlockDelta<?> delta) {
|
||||
this.delta = delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for content block deltas.
|
||||
*
|
||||
* @param <C> The type of content
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "ContentBlockDelta",
|
||||
seeAlso = {ContentBlockDeltaText.class, ContentBlockDeltaThinking.class, ContentBlockDeltaInputJson.class})
|
||||
public abstract static class ContentBlockDelta<C> implements AssistantContent<C> {
|
||||
/**
|
||||
* The type of the content block delta.
|
||||
*/
|
||||
protected String type;
|
||||
/**
|
||||
* The message ID.
|
||||
*/
|
||||
protected String messageId;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the content block delta.
|
||||
*
|
||||
* @param type The type of the content block delta
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message ID.
|
||||
*
|
||||
* @param messageId The message ID
|
||||
*/
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a text delta.
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "text_delta")
|
||||
public static class ContentBlockDeltaText extends ContentBlockDelta<String> implements TextAssistantContent {
|
||||
/**
|
||||
* The text content.
|
||||
*/
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* Gets the text content.
|
||||
*
|
||||
* @return The text content
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text content.
|
||||
*
|
||||
* @param text The text content
|
||||
*/
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentOfAssistant() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a thinking delta.
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "thinking_delta")
|
||||
public static class ContentBlockDeltaThinking extends ContentBlockDelta<String> implements ThingkingAssistantContent {
|
||||
/**
|
||||
* The thinking content.
|
||||
*/
|
||||
private String thinking;
|
||||
|
||||
/**
|
||||
* Gets the thinking content.
|
||||
*
|
||||
* @return The thinking content
|
||||
*/
|
||||
public String getThinking() {
|
||||
return thinking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the thinking content.
|
||||
*
|
||||
* @param thinking The thinking content
|
||||
*/
|
||||
public void setThinking(String thinking) {
|
||||
this.thinking = thinking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentOfAssistant() {
|
||||
return thinking;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an input JSON delta.
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "input_json_delta")
|
||||
public static class ContentBlockDeltaInputJson extends ContentBlockDelta<Map<String, Object>> implements ToolUseAssistantContent {
|
||||
/**
|
||||
* The partial JSON content.
|
||||
*/
|
||||
@JSONField(name = "partial_json")
|
||||
private String partialJson;
|
||||
|
||||
/**
|
||||
* Gets the partial JSON content.
|
||||
*
|
||||
* @return The partial JSON content
|
||||
*/
|
||||
public String getPartialJson() {
|
||||
return partialJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the partial JSON content.
|
||||
*
|
||||
* @param partialJson The partial JSON content
|
||||
*/
|
||||
public void setPartialJson(String partialJson) {
|
||||
this.partialJson = partialJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getContentOfAssistant() {
|
||||
return getInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getInput() {
|
||||
return JSON.parseObject(partialJson, new TypeReference<Map<String, Object>>() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.block.ContentBlock;
|
||||
|
||||
/**
|
||||
* Represents a content block start event during message streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "content_block_start")
|
||||
public class ContentBlockStartEvent extends StreamEvent{
|
||||
/**
|
||||
* The index of the content block.
|
||||
*/
|
||||
private int index;
|
||||
|
||||
/**
|
||||
* The content block that is starting.
|
||||
*/
|
||||
@JSONField(name = "content_block")
|
||||
private ContentBlock contentBlock;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a content block stop event during message streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "content_block_stop")
|
||||
public class ContentBlockStopEvent extends StreamEvent{
|
||||
/**
|
||||
* The index of the content block.
|
||||
*/
|
||||
Long index;
|
||||
|
||||
/**
|
||||
* Gets the index of the content block.
|
||||
*
|
||||
* @return The index of the content block
|
||||
*/
|
||||
public Long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the content block.
|
||||
*
|
||||
* @param index The index of the content block
|
||||
*/
|
||||
public void setIndex(Long index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a message start event during message streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeName = "message_start")
|
||||
public class MessageStartStreamEvent extends StreamEvent{
|
||||
/**
|
||||
* The message that is starting.
|
||||
*/
|
||||
private Message message;
|
||||
|
||||
/**
|
||||
* Represents the message information.
|
||||
*/
|
||||
public static class Message {
|
||||
/**
|
||||
* Message ID.
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* Message role.
|
||||
*/
|
||||
private String role;
|
||||
/**
|
||||
* Message model.
|
||||
*/
|
||||
private String model;
|
||||
|
||||
/**
|
||||
* Gets the message ID.
|
||||
*
|
||||
* @return The message ID
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message ID.
|
||||
*
|
||||
* @param id The message ID
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message role.
|
||||
*
|
||||
* @return The message role
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message role.
|
||||
*
|
||||
* @param role The message role
|
||||
*/
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message model.
|
||||
*
|
||||
* @return The message model
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message model.
|
||||
*
|
||||
* @param model The message model
|
||||
*/
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message that is starting.
|
||||
*
|
||||
* @return The message that is starting
|
||||
*/
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message that is starting.
|
||||
*
|
||||
* @param message The message that is starting
|
||||
*/
|
||||
public void setMessage(Message message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a message stop event during message streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeName = "message_stop")
|
||||
public class MessageStopStreamEvent extends StreamEvent{
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Base class for stream events during message streaming.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "StreamEvent",
|
||||
seeAlso = {MessageStartStreamEvent.class, MessageStopStreamEvent.class, ContentBlockStartEvent.class, ContentBlockStopEvent.class,
|
||||
ContentBlockDeltaEvent.class})
|
||||
public class StreamEvent {
|
||||
/**
|
||||
* The type of the stream event.
|
||||
*/
|
||||
protected String type;
|
||||
|
||||
/**
|
||||
* Gets the type of the stream event.
|
||||
*
|
||||
* @return The type of the stream event
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the stream event.
|
||||
*
|
||||
* @param type The type of the stream event
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
|
||||
/**
|
||||
* Represents a control request to the CLI.
|
||||
*
|
||||
* @param <R> The type of the request object
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "control_request")
|
||||
public class CLIControlRequest<R extends ControlRequestPayload> extends MessageBase {
|
||||
/**
|
||||
* The ID of the request.
|
||||
*/
|
||||
@JSONField(name = "request_id")
|
||||
private String requestId = UUID.randomUUID().toString();
|
||||
|
||||
/**
|
||||
* The actual request object.
|
||||
*/
|
||||
private R request;
|
||||
|
||||
/**
|
||||
* Creates a new CLIControlRequest instance and sets the type to "control_request".
|
||||
*/
|
||||
public CLIControlRequest() {
|
||||
super();
|
||||
type = "control_request";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new control request with the specified request object.
|
||||
*
|
||||
* @param request The request object
|
||||
* @param <T> The type of the request object
|
||||
* @return A new control request instance
|
||||
*/
|
||||
public static <T extends ControlRequestPayload> CLIControlRequest<T> create(T request) {
|
||||
CLIControlRequest<T> controlRequest = new CLIControlRequest<>();
|
||||
controlRequest.setRequest(request);
|
||||
return controlRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the request.
|
||||
*
|
||||
* @return The ID of the request
|
||||
*/
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the request.
|
||||
*
|
||||
* @param requestId The ID of the request
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public CLIControlRequest<R> setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual request object.
|
||||
*
|
||||
* @return The actual request object
|
||||
*/
|
||||
public R getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the actual request object.
|
||||
*
|
||||
* @param request The actual request object
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public CLIControlRequest<R> setRequest(R request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.MessageBase;
|
||||
|
||||
/**
|
||||
* Represents a control response from the CLI.
|
||||
*
|
||||
* @param <R> The type of the response object
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "type", typeName = "control_response")
|
||||
public class CLIControlResponse<R> extends MessageBase {
|
||||
/**
|
||||
* The response object.
|
||||
*/
|
||||
private Response<R> response;
|
||||
|
||||
/**
|
||||
* Creates a new CLIControlResponse instance and sets the type to "control_response".
|
||||
*/
|
||||
public CLIControlResponse() {
|
||||
super();
|
||||
this.type = "control_response";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response object.
|
||||
*
|
||||
* @return The response object
|
||||
*/
|
||||
public Response<R> getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response object.
|
||||
*
|
||||
* @param response The response object
|
||||
*/
|
||||
public void setResponse(Response<R> response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new response object.
|
||||
*
|
||||
* @return A new response object
|
||||
*/
|
||||
public Response<R> createResponse() {
|
||||
Response<R> response = new Response<>();
|
||||
this.setResponse(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the response information.
|
||||
*
|
||||
* @param <R> The type of the response object
|
||||
*/
|
||||
public static class Response<R> {
|
||||
/**
|
||||
* The ID of the request.
|
||||
*/
|
||||
@JSONField(name = "request_id")
|
||||
private String requestId;
|
||||
/**
|
||||
* The subtype of the response.
|
||||
*/
|
||||
private String subtype = "success";
|
||||
/**
|
||||
* The actual response.
|
||||
*/
|
||||
R response;
|
||||
|
||||
/**
|
||||
* Gets the ID of the request.
|
||||
*
|
||||
* @return The ID of the request
|
||||
*/
|
||||
public String getRequestId() {
|
||||
return requestId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the request.
|
||||
*
|
||||
* @param requestId The ID of the request
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public Response<R> setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subtype of the response.
|
||||
*
|
||||
* @return The subtype of the response
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subtype of the response.
|
||||
*
|
||||
* @param subtype The subtype of the response
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public Response<R> setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual response.
|
||||
*
|
||||
* @return The actual response
|
||||
*/
|
||||
public R getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the actual response.
|
||||
*
|
||||
* @param response The actual response
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public Response<R> setResponse(R response) {
|
||||
this.response = response;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.InitializeConfig;
|
||||
|
||||
/**
|
||||
* Represents a control initialize request to the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "initialize")
|
||||
public class CLIControlInitializeRequest extends ControlRequestPayload {
|
||||
public CLIControlInitializeRequest() {
|
||||
super();
|
||||
this.subtype = "initialize";
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialization configuration.
|
||||
*/
|
||||
@JSONField(unwrapped = true)
|
||||
InitializeConfig initializeConfig = new InitializeConfig();
|
||||
|
||||
/**
|
||||
* Gets the initialization configuration.
|
||||
*
|
||||
* @return The initialization configuration
|
||||
*/
|
||||
public InitializeConfig getInitializeConfig() {
|
||||
return initializeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initialization configuration.
|
||||
*
|
||||
* @param initializeConfig The initialization configuration
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public CLIControlInitializeRequest setInitializeConfig(InitializeConfig initializeConfig) {
|
||||
this.initializeConfig = initializeConfig;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
|
||||
|
||||
/**
|
||||
* Represents a control initialize response from the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "initialize")
|
||||
public class CLIControlInitializeResponse extends ControlResponsePayload {
|
||||
public CLIControlInitializeResponse() {
|
||||
super();
|
||||
this.subtype = "initialize";
|
||||
}
|
||||
|
||||
/**
|
||||
* The capabilities' information.
|
||||
*/
|
||||
Capabilities capabilities;
|
||||
|
||||
/**
|
||||
* Gets the capabilities information.
|
||||
*
|
||||
* @return The capabilities information
|
||||
*/
|
||||
public Capabilities getCapabilities() {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the capabilities information.
|
||||
*
|
||||
* @param capabilities The capabilities information
|
||||
*/
|
||||
public void setCapabilities(Capabilities capabilities) {
|
||||
this.capabilities = capabilities;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a control interrupt request to the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "interrupt")
|
||||
public class CLIControlInterruptRequest extends ControlRequestPayload {
|
||||
public CLIControlInterruptRequest() {
|
||||
super();
|
||||
setSubtype("interrupt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a control permission request to the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "can_use_tool")
|
||||
public class CLIControlPermissionRequest extends ControlRequestPayload {
|
||||
public CLIControlPermissionRequest() {
|
||||
super();
|
||||
this.subtype = "can_use_tool";
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the tool requesting permission.
|
||||
*/
|
||||
@JSONField(name = "tool_name")
|
||||
private String toolName;
|
||||
|
||||
/**
|
||||
* The ID of the tool use.
|
||||
*/
|
||||
@JSONField(name = "tool_use_id")
|
||||
private String toolUseId;
|
||||
|
||||
/**
|
||||
* The input for the tool.
|
||||
*/
|
||||
private Map<String, Object> input;
|
||||
|
||||
/**
|
||||
* List of permission suggestions.
|
||||
*/
|
||||
@JSONField(name = "permission_suggestions")
|
||||
private List<PermissionSuggestion> permissionSuggestions;
|
||||
|
||||
/**
|
||||
* The blocked path.
|
||||
*/
|
||||
@JSONField(name = "blocked_path")
|
||||
private String blockedPath;
|
||||
|
||||
/**
|
||||
* Gets the name of the tool requesting permission.
|
||||
*
|
||||
* @return The name of the tool requesting permission
|
||||
*/
|
||||
public String getToolName() {
|
||||
return toolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the tool requesting permission.
|
||||
*
|
||||
* @param toolName The name of the tool requesting permission
|
||||
*/
|
||||
public void setToolName(String toolName) {
|
||||
this.toolName = toolName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the tool use.
|
||||
*
|
||||
* @return The ID of the tool use
|
||||
*/
|
||||
public String getToolUseId() {
|
||||
return toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the tool use.
|
||||
*
|
||||
* @param toolUseId The ID of the tool use
|
||||
*/
|
||||
public void setToolUseId(String toolUseId) {
|
||||
this.toolUseId = toolUseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input for the tool.
|
||||
*
|
||||
* @return The input for the tool
|
||||
*/
|
||||
public Map<String, Object> getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input for the tool.
|
||||
*
|
||||
* @param input The input for the tool
|
||||
*/
|
||||
public void setInput(Map<String, Object> input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of permission suggestions.
|
||||
*
|
||||
* @return The list of permission suggestions
|
||||
*/
|
||||
public List<PermissionSuggestion> getPermissionSuggestions() {
|
||||
return permissionSuggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of permission suggestions.
|
||||
*
|
||||
* @param permissionSuggestions The list of permission suggestions
|
||||
*/
|
||||
public void setPermissionSuggestions(
|
||||
List<PermissionSuggestion> permissionSuggestions) {
|
||||
this.permissionSuggestions = permissionSuggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blocked path.
|
||||
*
|
||||
* @return The blocked path
|
||||
*/
|
||||
public String getBlockedPath() {
|
||||
return blockedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the blocked path.
|
||||
*
|
||||
* @param blockedPath The blocked path
|
||||
*/
|
||||
public void setBlockedPath(String blockedPath) {
|
||||
this.blockedPath = blockedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a permission suggestion.
|
||||
*/
|
||||
public static class PermissionSuggestion {
|
||||
/**
|
||||
* The type of suggestion (allow, deny, modify).
|
||||
*/
|
||||
private String type; // 'allow' | 'deny' | 'modify'
|
||||
/**
|
||||
* The label for the suggestion.
|
||||
*/
|
||||
private String label;
|
||||
/**
|
||||
* The description of the suggestion.
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* The modified input.
|
||||
*/
|
||||
private Object modifiedInput;
|
||||
|
||||
/**
|
||||
* Gets the type of suggestion.
|
||||
*
|
||||
* @return The type of suggestion
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of suggestion.
|
||||
*
|
||||
* @param type The type of suggestion
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the suggestion.
|
||||
*
|
||||
* @return The label for the suggestion
|
||||
*/
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the label for the suggestion.
|
||||
*
|
||||
* @param label The label for the suggestion
|
||||
*/
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description of the suggestion.
|
||||
*
|
||||
* @return The description of the suggestion
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description of the suggestion.
|
||||
*
|
||||
* @param description The description of the suggestion
|
||||
*/
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the modified input.
|
||||
*
|
||||
* @return The modified input
|
||||
*/
|
||||
public Object getModifiedInput() {
|
||||
return modifiedInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the modified input.
|
||||
*
|
||||
* @param modifiedInput The modified input
|
||||
*/
|
||||
public void setModifiedInput(Object modifiedInput) {
|
||||
this.modifiedInput = modifiedInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior;
|
||||
|
||||
/**
|
||||
* Represents a control permission response from the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "can_use_tool")
|
||||
public class CLIControlPermissionResponse extends ControlResponsePayload {
|
||||
public CLIControlPermissionResponse() {
|
||||
super();
|
||||
this.subtype = "can_use_tool";
|
||||
}
|
||||
|
||||
/**
|
||||
* The behavior for the permission request.
|
||||
*/
|
||||
@JSONField(unwrapped = true)
|
||||
Behavior behavior;
|
||||
|
||||
/**
|
||||
* Gets the behavior for the permission request.
|
||||
*
|
||||
* @return The behavior for the permission request
|
||||
*/
|
||||
public Behavior getBehavior() {
|
||||
return behavior;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the behavior for the permission request.
|
||||
*
|
||||
* @param behavior The behavior for the permission request
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public CLIControlPermissionResponse setBehavior(Behavior behavior) {
|
||||
this.behavior = behavior;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a control request to set the model in the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "set_model")
|
||||
public class CLIControlSetModelRequest extends ControlRequestPayload {
|
||||
public CLIControlSetModelRequest() {
|
||||
super();
|
||||
this.subtype = "set_model";
|
||||
}
|
||||
|
||||
/**
|
||||
* The model to set.
|
||||
*/
|
||||
String model;
|
||||
|
||||
/**
|
||||
* Gets the model to set.
|
||||
*
|
||||
* @return The model to set
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model to set.
|
||||
*
|
||||
* @param model The model to set
|
||||
*/
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
/**
|
||||
* Represents a control response for setting the model in the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class CLIControlSetModelResponse {
|
||||
/**
|
||||
* The subtype of the response ("set_model").
|
||||
*/
|
||||
String subtype = "set_model";
|
||||
/**
|
||||
* The model that was set.
|
||||
*/
|
||||
String model;
|
||||
|
||||
/**
|
||||
* Gets the subtype of the response.
|
||||
*
|
||||
* @return The subtype of the response
|
||||
*/
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subtype of the response.
|
||||
*
|
||||
* @param subtype The subtype of the response
|
||||
*/
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model that was set.
|
||||
*
|
||||
* @return The model that was set
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model that was set.
|
||||
*
|
||||
* @param model The model that was set
|
||||
*/
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a control request to set the permission mode in the CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "set_permission_mode")
|
||||
public class CLIControlSetPermissionModeRequest extends ControlRequestPayload {
|
||||
public CLIControlSetPermissionModeRequest() {
|
||||
super();
|
||||
setSubtype("set_permission_mode");
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission mode to set.
|
||||
*/
|
||||
String mode;
|
||||
|
||||
/**
|
||||
* Gets the permission mode to set.
|
||||
*
|
||||
* @return The permission mode to set
|
||||
*/
|
||||
public String getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission mode to set.
|
||||
*
|
||||
* @param mode The permission mode to set
|
||||
*/
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a payload request in the CLI control message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "ControlRequestPayload",
|
||||
seeAlso = {CLIControlInitializeRequest.class, CLIControlInterruptRequest.class, CLIControlPermissionRequest.class, CLIControlSetModelRequest.class, CLIControlSetPermissionModeRequest.class})
|
||||
public class ControlRequestPayload {
|
||||
/**
|
||||
* The subtype of the request.
|
||||
*/
|
||||
protected String subtype;
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.alibaba.qwen.code.cli.protocol.message.control.payload;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONType;
|
||||
|
||||
/**
|
||||
* Represents a payload request in the CLI control message.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
@JSONType(typeKey = "subtype", typeName = "ControlResponsePayload",
|
||||
seeAlso = {CLIControlInitializeResponse.class, CLIControlPermissionResponse.class})
|
||||
public class ControlResponsePayload {
|
||||
/**
|
||||
* The subtype of the request.
|
||||
*/
|
||||
protected String subtype;
|
||||
|
||||
public String getSubtype() {
|
||||
return subtype;
|
||||
}
|
||||
|
||||
public void setSubtype(String subtype) {
|
||||
this.subtype = subtype;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,594 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
export interface Annotation {
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Usage {
|
||||
input_tokens: number;
|
||||
output_tokens: number;
|
||||
cache_creation_input_tokens?: number;
|
||||
cache_read_input_tokens?: number;
|
||||
total_tokens?: number;
|
||||
}
|
||||
|
||||
export interface ExtendedUsage extends Usage {
|
||||
server_tool_use?: {
|
||||
web_search_requests: number;
|
||||
};
|
||||
service_tier?: string;
|
||||
cache_creation?: {
|
||||
ephemeral_1h_input_tokens: number;
|
||||
ephemeral_5m_input_tokens: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ModelUsage {
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
cacheReadInputTokens: number;
|
||||
cacheCreationInputTokens: number;
|
||||
webSearchRequests: number;
|
||||
contextWindow: number;
|
||||
}
|
||||
|
||||
export interface CLIPermissionDenial {
|
||||
tool_name: string;
|
||||
tool_use_id: string;
|
||||
tool_input: unknown;
|
||||
}
|
||||
|
||||
export interface TextBlock {
|
||||
type: 'text';
|
||||
text: string;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ThinkingBlock {
|
||||
type: 'thinking';
|
||||
thinking: string;
|
||||
signature?: string;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ToolUseBlock {
|
||||
type: 'tool_use';
|
||||
id: string;
|
||||
name: string;
|
||||
input: unknown;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export interface ToolResultBlock {
|
||||
type: 'tool_result';
|
||||
tool_use_id: string;
|
||||
content?: string | ContentBlock[];
|
||||
is_error?: boolean;
|
||||
annotations?: Annotation[];
|
||||
}
|
||||
|
||||
export type ContentBlock =
|
||||
| TextBlock
|
||||
| ThinkingBlock
|
||||
| ToolUseBlock
|
||||
| ToolResultBlock;
|
||||
|
||||
export interface APIUserMessage {
|
||||
role: 'user';
|
||||
content: string | ContentBlock[];
|
||||
}
|
||||
|
||||
export interface APIAssistantMessage {
|
||||
id: string;
|
||||
type: 'message';
|
||||
role: 'assistant';
|
||||
model: string;
|
||||
content: ContentBlock[];
|
||||
stop_reason?: string | null;
|
||||
usage: Usage;
|
||||
}
|
||||
|
||||
export interface SDKUserMessage {
|
||||
type: 'user';
|
||||
uuid?: string;
|
||||
session_id: string;
|
||||
message: APIUserMessage;
|
||||
parent_tool_use_id: string | null;
|
||||
options?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface SDKAssistantMessage {
|
||||
type: 'assistant';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
message: APIAssistantMessage;
|
||||
parent_tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export interface SDKSystemMessage {
|
||||
type: 'system';
|
||||
subtype: string;
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
data?: unknown;
|
||||
cwd?: string;
|
||||
tools?: string[];
|
||||
mcp_servers?: Array<{
|
||||
name: string;
|
||||
status: string;
|
||||
}>;
|
||||
model?: string;
|
||||
permission_mode?: string;
|
||||
slash_commands?: string[];
|
||||
qwen_code_version?: string;
|
||||
output_style?: string;
|
||||
agents?: string[];
|
||||
skills?: string[];
|
||||
capabilities?: Record<string, unknown>;
|
||||
compact_metadata?: {
|
||||
trigger: 'manual' | 'auto';
|
||||
pre_tokens: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SDKResultMessageSuccess {
|
||||
type: 'result';
|
||||
subtype: 'success';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
is_error: false;
|
||||
duration_ms: number;
|
||||
duration_api_ms: number;
|
||||
num_turns: number;
|
||||
result: string;
|
||||
usage: ExtendedUsage;
|
||||
modelUsage?: Record<string, ModelUsage>;
|
||||
permission_denials: CLIPermissionDenial[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface SDKResultMessageError {
|
||||
type: 'result';
|
||||
subtype: 'error_max_turns' | 'error_during_execution';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
is_error: true;
|
||||
duration_ms: number;
|
||||
duration_api_ms: number;
|
||||
num_turns: number;
|
||||
usage: ExtendedUsage;
|
||||
modelUsage?: Record<string, ModelUsage>;
|
||||
permission_denials: CLIPermissionDenial[];
|
||||
error?: {
|
||||
type?: string;
|
||||
message: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type SDKResultMessage = SDKResultMessageSuccess | SDKResultMessageError;
|
||||
|
||||
export interface MessageStartStreamEvent {
|
||||
type: 'message_start';
|
||||
message: {
|
||||
id: string;
|
||||
role: 'assistant';
|
||||
model: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ContentBlockStartEvent {
|
||||
type: 'content_block_start';
|
||||
index: number;
|
||||
content_block: ContentBlock;
|
||||
}
|
||||
|
||||
export type ContentBlockDelta =
|
||||
| {
|
||||
type: 'text_delta';
|
||||
text: string;
|
||||
}
|
||||
| {
|
||||
type: 'thinking_delta';
|
||||
thinking: string;
|
||||
}
|
||||
| {
|
||||
type: 'input_json_delta';
|
||||
partial_json: string;
|
||||
};
|
||||
|
||||
export interface ContentBlockDeltaEvent {
|
||||
type: 'content_block_delta';
|
||||
index: number;
|
||||
delta: ContentBlockDelta;
|
||||
}
|
||||
|
||||
export interface ContentBlockStopEvent {
|
||||
type: 'content_block_stop';
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface MessageStopStreamEvent {
|
||||
type: 'message_stop';
|
||||
}
|
||||
|
||||
export type StreamEvent =
|
||||
| MessageStartStreamEvent
|
||||
| ContentBlockStartEvent
|
||||
| ContentBlockDeltaEvent
|
||||
| ContentBlockStopEvent
|
||||
| MessageStopStreamEvent;
|
||||
|
||||
export interface SDKPartialAssistantMessage {
|
||||
type: 'stream_event';
|
||||
uuid: string;
|
||||
session_id: string;
|
||||
event: StreamEvent;
|
||||
parent_tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'yolo';
|
||||
|
||||
/**
|
||||
* TODO: Align with `ToolCallConfirmationDetails`
|
||||
*/
|
||||
export interface PermissionSuggestion {
|
||||
type: 'allow' | 'deny' | 'modify';
|
||||
label: string;
|
||||
description?: string;
|
||||
modifiedInput?: unknown;
|
||||
}
|
||||
|
||||
export interface HookRegistration {
|
||||
event: string;
|
||||
callback_id: string;
|
||||
}
|
||||
|
||||
export interface HookCallbackResult {
|
||||
shouldSkip?: boolean;
|
||||
shouldInterrupt?: boolean;
|
||||
suppressOutput?: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CLIControlInterruptRequest {
|
||||
subtype: 'interrupt';
|
||||
}
|
||||
|
||||
export interface CLIControlPermissionRequest {
|
||||
subtype: 'can_use_tool';
|
||||
tool_name: string;
|
||||
tool_use_id: string;
|
||||
input: unknown;
|
||||
permission_suggestions: PermissionSuggestion[] | null;
|
||||
blocked_path: string | null;
|
||||
}
|
||||
|
||||
export enum AuthProviderType {
|
||||
DYNAMIC_DISCOVERY = 'dynamic_discovery',
|
||||
GOOGLE_CREDENTIALS = 'google_credentials',
|
||||
SERVICE_ACCOUNT_IMPERSONATION = 'service_account_impersonation',
|
||||
}
|
||||
|
||||
export interface MCPServerConfig {
|
||||
command?: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
cwd?: string;
|
||||
url?: string;
|
||||
httpUrl?: string;
|
||||
headers?: Record<string, string>;
|
||||
tcp?: string;
|
||||
timeout?: number;
|
||||
trust?: boolean;
|
||||
description?: string;
|
||||
includeTools?: string[];
|
||||
excludeTools?: string[];
|
||||
extensionName?: string;
|
||||
oauth?: Record<string, unknown>;
|
||||
authProviderType?: AuthProviderType;
|
||||
targetAudience?: string;
|
||||
targetServiceAccount?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SDK MCP Server configuration
|
||||
*
|
||||
* SDK MCP servers run in the SDK process and are connected via in-memory transport.
|
||||
* Tool calls are routed through the control plane between SDK and CLI.
|
||||
*/
|
||||
export interface SDKMcpServerConfig {
|
||||
/**
|
||||
* Type identifier for SDK MCP servers
|
||||
*/
|
||||
type: 'sdk';
|
||||
/**
|
||||
* Server name for identification and routing
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The MCP Server instance created by createSdkMcpServer()
|
||||
*/
|
||||
instance: McpServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire format for SDK MCP servers sent to the CLI
|
||||
*/
|
||||
export type WireSDKMcpServerConfig = Omit<SDKMcpServerConfig, 'instance'>;
|
||||
|
||||
export interface CLIControlInitializeRequest {
|
||||
subtype: 'initialize';
|
||||
hooks?: HookRegistration[] | null;
|
||||
/**
|
||||
* SDK MCP servers config
|
||||
* These are MCP servers running in the SDK process, connected via control plane.
|
||||
* External MCP servers are configured separately in settings, not via initialization.
|
||||
*/
|
||||
sdkMcpServers?: Record<string, WireSDKMcpServerConfig>;
|
||||
/**
|
||||
* External MCP servers that should be managed by the CLI.
|
||||
*/
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
agents?: SubagentConfig[];
|
||||
}
|
||||
|
||||
export interface CLIControlSetPermissionModeRequest {
|
||||
subtype: 'set_permission_mode';
|
||||
mode: PermissionMode;
|
||||
}
|
||||
|
||||
export interface CLIHookCallbackRequest {
|
||||
subtype: 'hook_callback';
|
||||
callback_id: string;
|
||||
input: unknown;
|
||||
tool_use_id: string | null;
|
||||
}
|
||||
|
||||
export interface CLIControlMcpMessageRequest {
|
||||
subtype: 'mcp_message';
|
||||
server_name: string;
|
||||
message: {
|
||||
jsonrpc?: string;
|
||||
method: string;
|
||||
params?: Record<string, unknown>;
|
||||
id?: string | number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CLIControlSetModelRequest {
|
||||
subtype: 'set_model';
|
||||
model: string;
|
||||
}
|
||||
|
||||
export interface CLIControlMcpStatusRequest {
|
||||
subtype: 'mcp_server_status';
|
||||
}
|
||||
|
||||
export interface CLIControlSupportedCommandsRequest {
|
||||
subtype: 'supported_commands';
|
||||
}
|
||||
|
||||
export type ControlRequestPayload =
|
||||
| CLIControlInterruptRequest
|
||||
| CLIControlPermissionRequest
|
||||
| CLIControlInitializeRequest
|
||||
| CLIControlSetPermissionModeRequest
|
||||
| CLIHookCallbackRequest
|
||||
| CLIControlMcpMessageRequest
|
||||
| CLIControlSetModelRequest
|
||||
| CLIControlMcpStatusRequest
|
||||
| CLIControlSupportedCommandsRequest;
|
||||
|
||||
export interface CLIControlRequest {
|
||||
type: 'control_request';
|
||||
request_id: string;
|
||||
request: ControlRequestPayload;
|
||||
}
|
||||
|
||||
export interface PermissionApproval {
|
||||
allowed: boolean;
|
||||
reason?: string;
|
||||
modifiedInput?: unknown;
|
||||
}
|
||||
|
||||
export interface ControlResponse {
|
||||
subtype: 'success';
|
||||
request_id: string;
|
||||
response: unknown;
|
||||
}
|
||||
|
||||
export interface ControlErrorResponse {
|
||||
subtype: 'error';
|
||||
request_id: string;
|
||||
error: string | { message: string; [key: string]: unknown };
|
||||
}
|
||||
|
||||
export interface CLIControlResponse {
|
||||
type: 'control_response';
|
||||
response: ControlResponse | ControlErrorResponse;
|
||||
}
|
||||
|
||||
export interface ControlCancelRequest {
|
||||
type: 'control_cancel_request';
|
||||
request_id?: string;
|
||||
}
|
||||
|
||||
export type ControlMessage =
|
||||
| CLIControlRequest
|
||||
| CLIControlResponse
|
||||
| ControlCancelRequest;
|
||||
|
||||
/**
|
||||
* Union of all SDK message types
|
||||
*/
|
||||
export type SDKMessage =
|
||||
| SDKUserMessage
|
||||
| SDKAssistantMessage
|
||||
| SDKSystemMessage
|
||||
| SDKResultMessage
|
||||
| SDKPartialAssistantMessage;
|
||||
|
||||
export function isSDKUserMessage(msg: any): msg is SDKUserMessage {
|
||||
return (
|
||||
msg && typeof msg === 'object' && msg.type === 'user' && 'message' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKAssistantMessage(msg: any): msg is SDKAssistantMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'assistant' &&
|
||||
'uuid' in msg &&
|
||||
'message' in msg &&
|
||||
'session_id' in msg &&
|
||||
'parent_tool_use_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKSystemMessage(msg: any): msg is SDKSystemMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'system' &&
|
||||
'subtype' in msg &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKResultMessage(msg: any): msg is SDKResultMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'result' &&
|
||||
'subtype' in msg &&
|
||||
'duration_ms' in msg &&
|
||||
'is_error' in msg &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isSDKPartialAssistantMessage(
|
||||
msg: any,
|
||||
): msg is SDKPartialAssistantMessage {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'stream_event' &&
|
||||
'uuid' in msg &&
|
||||
'session_id' in msg &&
|
||||
'event' in msg &&
|
||||
'parent_tool_use_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlRequest(msg: any): msg is CLIControlRequest {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_request' &&
|
||||
'request_id' in msg &&
|
||||
'request' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlResponse(msg: any): msg is CLIControlResponse {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_response' &&
|
||||
'response' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isControlCancel(msg: any): msg is ControlCancelRequest {
|
||||
return (
|
||||
msg &&
|
||||
typeof msg === 'object' &&
|
||||
msg.type === 'control_cancel_request' &&
|
||||
'request_id' in msg
|
||||
);
|
||||
}
|
||||
|
||||
export function isTextBlock(block: any): block is TextBlock {
|
||||
return block && typeof block === 'object' && block.type === 'text';
|
||||
}
|
||||
|
||||
export function isThinkingBlock(block: any): block is ThinkingBlock {
|
||||
return block && typeof block === 'object' && block.type === 'thinking';
|
||||
}
|
||||
|
||||
export function isToolUseBlock(block: any): block is ToolUseBlock {
|
||||
return block && typeof block === 'object' && block.type === 'tool_use';
|
||||
}
|
||||
|
||||
export function isToolResultBlock(block: any): block is ToolResultBlock {
|
||||
return block && typeof block === 'object' && block.type === 'tool_result';
|
||||
}
|
||||
|
||||
export type SubagentLevel = 'session';
|
||||
|
||||
export interface ModelConfig {
|
||||
model?: string;
|
||||
temp?: number;
|
||||
top_p?: number;
|
||||
}
|
||||
|
||||
export interface RunConfig {
|
||||
max_time_minutes?: number;
|
||||
max_turns?: number;
|
||||
}
|
||||
|
||||
export interface SubagentConfig {
|
||||
name: string;
|
||||
description: string;
|
||||
tools?: string[];
|
||||
systemPrompt: string;
|
||||
level: SubagentLevel;
|
||||
filePath?: string;
|
||||
modelConfig?: Partial<ModelConfig>;
|
||||
runConfig?: Partial<RunConfig>;
|
||||
color?: string;
|
||||
readonly isBuiltin?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Control Request Types
|
||||
*
|
||||
* Centralized enum for all control request subtypes supported by the CLI.
|
||||
* This enum should be kept in sync with the controllers in:
|
||||
* - packages/cli/src/services/control/controllers/systemController.ts
|
||||
* - packages/cli/src/services/control/controllers/permissionController.ts
|
||||
* - packages/cli/src/services/control/controllers/mcpController.ts
|
||||
* - packages/cli/src/services/control/controllers/hookController.ts
|
||||
*/
|
||||
export enum ControlRequestType {
|
||||
// SystemController requests
|
||||
INITIALIZE = 'initialize',
|
||||
INTERRUPT = 'interrupt',
|
||||
SET_MODEL = 'set_model',
|
||||
SUPPORTED_COMMANDS = 'supported_commands',
|
||||
|
||||
// PermissionController requests
|
||||
CAN_USE_TOOL = 'can_use_tool',
|
||||
SET_PERMISSION_MODE = 'set_permission_mode',
|
||||
|
||||
// MCPController requests
|
||||
MCP_MESSAGE = 'mcp_message',
|
||||
MCP_SERVER_STATUS = 'mcp_server_status',
|
||||
|
||||
// HookController requests
|
||||
HOOK_CALLBACK = 'hook_callback',
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
package com.alibaba.qwen.code.cli.session;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.JSONReader.Feature;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.Capabilities;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlInitializeRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlInitializeResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlInterruptRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlSetModelRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlSetPermissionModeRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.SessionEventConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.utils.MyConcurrentUtils;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Manages a session with the Qwen Code CLI, handling communication, sending prompts, and processing responses.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class Session {
|
||||
private static final Logger log = LoggerFactory.getLogger(Session.class);
|
||||
private final Transport transport;
|
||||
private CLIControlInitializeResponse lastCliControlInitializeResponse;
|
||||
private SDKSystemMessage lastSdkSystemMessage;
|
||||
private final Timeout defaultEventTimeout = Timeout.TIMEOUT_60_SECONDS;
|
||||
|
||||
/**
|
||||
* Checks if the session is configured for streaming.
|
||||
*
|
||||
* @return true if streaming is enabled, false otherwise
|
||||
*/
|
||||
public boolean isStreaming() {
|
||||
return Optional.ofNullable(transport)
|
||||
.map(Transport::getTransportOptions)
|
||||
.map(TransportOptions::getIncludePartialMessages)
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new session with the specified transport.
|
||||
*
|
||||
* @param transport The transport layer to use for communication
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the transport is not available
|
||||
*/
|
||||
public Session(Transport transport) throws SessionControlException {
|
||||
if (transport == null || !transport.isAvailable()) {
|
||||
throw new SessionControlException("Transport is not available");
|
||||
}
|
||||
this.transport = transport;
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session by initializing communication with the CLI.
|
||||
*
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if initialization fails
|
||||
*/
|
||||
public void start() throws SessionControlException {
|
||||
try {
|
||||
if (!transport.isAvailable()) {
|
||||
transport.start();
|
||||
}
|
||||
String response = transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
|
||||
CLIControlResponse<CLIControlInitializeResponse> cliControlResponse = JSON.parseObject(response,
|
||||
new TypeReference<CLIControlResponse<CLIControlInitializeResponse>>() {});
|
||||
this.lastCliControlInitializeResponse = cliControlResponse.getResponse().getResponse();
|
||||
} catch (Exception e) {
|
||||
throw new SessionControlException("Failed to initialize the session", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the session and releases resources.
|
||||
*
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if closing fails
|
||||
*/
|
||||
public void close() throws SessionControlException {
|
||||
try {
|
||||
transport.close();
|
||||
} catch (Exception e) {
|
||||
throw new SessionControlException("Failed to close the session", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the current operation in the CLI.
|
||||
*
|
||||
* @return An optional boolean indicating success of the interrupt operation
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the operation fails
|
||||
*/
|
||||
public Optional<Boolean> interrupt() throws SessionControlException {
|
||||
checkAvailable();
|
||||
return processControlRequest(new CLIControlRequest<CLIControlInterruptRequest>().setRequest(new CLIControlInterruptRequest()).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model to be used in the session.
|
||||
*
|
||||
* @param modelName The name of the model to use
|
||||
* @return An optional boolean indicating success of the operation
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the operation fails
|
||||
*/
|
||||
public Optional<Boolean> setModel(String modelName) throws SessionControlException {
|
||||
checkAvailable();
|
||||
CLIControlSetModelRequest cliControlSetModelRequest = new CLIControlSetModelRequest();
|
||||
cliControlSetModelRequest.setModel(modelName);
|
||||
return processControlRequest(new CLIControlRequest<CLIControlSetModelRequest>().setRequest(cliControlSetModelRequest).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission mode for the session.
|
||||
*
|
||||
* @param permissionMode The permission mode to use
|
||||
* @return An optional boolean indicating success of the operation
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the operation fails
|
||||
*/
|
||||
public Optional<Boolean> setPermissionMode(PermissionMode permissionMode) throws SessionControlException {
|
||||
checkAvailable();
|
||||
CLIControlSetPermissionModeRequest cliControlSetPermissionModeRequest = new CLIControlSetPermissionModeRequest();
|
||||
cliControlSetPermissionModeRequest.setMode(permissionMode.getValue());
|
||||
return processControlRequest(
|
||||
new CLIControlRequest<CLIControlSetPermissionModeRequest>().setRequest(cliControlSetPermissionModeRequest).toString());
|
||||
}
|
||||
|
||||
private Optional<Boolean> processControlRequest(String request) throws SessionControlException {
|
||||
try {
|
||||
if (transport.isReading()) {
|
||||
transport.inputNoWaitResponse(request);
|
||||
return Optional.empty();
|
||||
} else {
|
||||
String response = transport.inputWaitForOneLine(request);
|
||||
CLIControlResponse<?> cliControlResponse = JSON.parseObject(response, new TypeReference<CLIControlResponse<?>>() {});
|
||||
return Optional.of("success".equals(cliControlResponse.getResponse().getSubtype()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SessionControlException("Failed to set model", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues the current session.
|
||||
*
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the operation fails
|
||||
*/
|
||||
public void continueSession() throws SessionControlException {
|
||||
resumeSession(getSessionId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a session with the specified ID.
|
||||
*
|
||||
* @param sessionId The ID of the session to resume
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if the operation fails
|
||||
*/
|
||||
public void resumeSession(String sessionId) throws SessionControlException {
|
||||
if (StringUtils.isNotBlank(sessionId)) {
|
||||
transport.getTransportOptions().setResumeSessionId(sessionId);
|
||||
}
|
||||
this.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a prompt to the CLI and processes the response.
|
||||
*
|
||||
* @param prompt The prompt to send to the CLI
|
||||
* @param sessionEventConsumers Consumers for handling different types of events
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException if sending the prompt fails
|
||||
* @throws com.alibaba.qwen.code.cli.session.exception.SessionControlException if a control operation fails
|
||||
*/
|
||||
public void sendPrompt(String prompt, SessionEventConsumers sessionEventConsumers) throws SessionSendPromptException, SessionControlException {
|
||||
checkAvailable();
|
||||
try {
|
||||
transport.inputWaitForMultiLine(new SDKUserMessage().setContent(prompt).toString(), (line) -> {
|
||||
JSONObject jsonObject = JSON.parseObject(line);
|
||||
String messageType = jsonObject.getString("type");
|
||||
if ("system".equals(messageType)) {
|
||||
lastSdkSystemMessage = jsonObject.to(SDKSystemMessage.class);
|
||||
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onSystemMessage(this, lastSdkSystemMessage),
|
||||
Optional.ofNullable(sessionEventConsumers.onSystemMessageTimeout(this, lastSdkSystemMessage))
|
||||
.orElse(defaultEventTimeout));
|
||||
return false;
|
||||
} else if ("assistant".equals(messageType)) {
|
||||
SDKAssistantMessage assistantMessage = jsonObject.to(SDKAssistantMessage.class);
|
||||
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onAssistantMessage(this, assistantMessage),
|
||||
Optional.ofNullable(sessionEventConsumers.onAssistantMessageTimeout(this, assistantMessage)).orElse(defaultEventTimeout));
|
||||
return false;
|
||||
} else if ("stream_event".equals(messageType)) {
|
||||
SDKPartialAssistantMessage sdkPartialAssistantMessage = jsonObject.to(SDKPartialAssistantMessage.class);
|
||||
MyConcurrentUtils.runAndWait(
|
||||
() -> sessionEventConsumers.onPartialAssistantMessage(this, sdkPartialAssistantMessage),
|
||||
Optional.ofNullable(sessionEventConsumers.onPartialAssistantMessageTimeout(this, sdkPartialAssistantMessage))
|
||||
.orElse(defaultEventTimeout));
|
||||
return false;
|
||||
} else if ("user".equals(messageType)) {
|
||||
SDKUserMessage sdkUserMessage = jsonObject.to(SDKUserMessage.class, Feature.FieldBased);
|
||||
MyConcurrentUtils.runAndWait(
|
||||
() -> sessionEventConsumers.onUserMessage(this, sdkUserMessage),
|
||||
Optional.ofNullable(sessionEventConsumers.onUserMessageTimeout(this, sdkUserMessage)).orElse(defaultEventTimeout));
|
||||
return false;
|
||||
} else if ("result".equals(messageType)) {
|
||||
SDKResultMessage sdkResultMessage = jsonObject.to(SDKResultMessage.class);
|
||||
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onResultMessage(this, sdkResultMessage),
|
||||
Optional.ofNullable(sessionEventConsumers.onResultMessageTimeout(this, sdkResultMessage)).orElse(defaultEventTimeout));
|
||||
return true;
|
||||
} else if ("control_response".equals(messageType)) {
|
||||
CLIControlResponse<? extends ControlResponsePayload> controlResponse = jsonObject.to(
|
||||
new TypeReference<CLIControlResponse<? extends ControlResponsePayload>>() {});
|
||||
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onControlResponse(this, controlResponse),
|
||||
Optional.ofNullable(sessionEventConsumers.onControlResponseTimeout(this, controlResponse)).orElse(defaultEventTimeout));
|
||||
if (!"error".equals(jsonObject.getString("subtype"))) {
|
||||
return false;
|
||||
} else {
|
||||
log.info("control_response error: {}", jsonObject.toJSONString());
|
||||
return "error".equals(jsonObject.getString("subtype"));
|
||||
}
|
||||
} else if ("control_request".equals(messageType)) {
|
||||
CLIControlResponse<? extends ControlResponsePayload> controlResponse;
|
||||
try {
|
||||
CLIControlRequest<? extends ControlRequestPayload> controlRequest = jsonObject.to(
|
||||
new TypeReference<CLIControlRequest<? extends ControlRequestPayload>>() {});
|
||||
controlResponse = MyConcurrentUtils.runAndWait(
|
||||
() -> sessionEventConsumers.onControlRequest(this, controlRequest),
|
||||
Optional.ofNullable(sessionEventConsumers.onControlRequestTimeout(this, controlRequest)).orElse(defaultEventTimeout));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process control request", e);
|
||||
controlResponse = new CLIControlResponse<>();
|
||||
}
|
||||
try {
|
||||
transport.inputNoWaitResponse(Optional.ofNullable(controlResponse).map(CLIControlResponse::toString)
|
||||
.orElse(new CLIControlResponse<ControlResponsePayload>().toString()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to send control response", e);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
log.warn("unknown message type: {}", messageType);
|
||||
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onOtherMessage(this, line),
|
||||
Optional.ofNullable(sessionEventConsumers.onOtherMessageTimeout(this, line)).orElse(defaultEventTimeout));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new SessionSendPromptException("Failed to send prompt", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current session ID.
|
||||
*
|
||||
* @return The session ID, or null if not available
|
||||
*/
|
||||
public String getSessionId() {
|
||||
return Optional.ofNullable(lastSdkSystemMessage).map(SDKSystemMessage::getSessionId).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session is available for operations.
|
||||
*
|
||||
* @return true if the session is available, false otherwise
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
return transport.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the capabilities of the CLI.
|
||||
*
|
||||
* @return A Capabilities object representing the CLI's capabilities
|
||||
*/
|
||||
public Capabilities getCapabilities() {
|
||||
return Optional.ofNullable(lastCliControlInitializeResponse).map(CLIControlInitializeResponse::getCapabilities).orElse(new Capabilities());
|
||||
}
|
||||
|
||||
private void checkAvailable() throws SessionControlException {
|
||||
if (!isAvailable()) {
|
||||
throw new SessionControlException("Session is not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.alibaba.qwen.code.cli.session.event.consumers;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlPermissionRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
/**
|
||||
* Interface for handling different types of assistant content during a session.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public interface AssistantContentConsumers {
|
||||
/**
|
||||
* Handles text content from the assistant.
|
||||
*
|
||||
* @param session The session
|
||||
* @param textAssistantContent The text content from the assistant
|
||||
*/
|
||||
void onText(Session session, TextAssistantContent textAssistantContent);
|
||||
|
||||
/**
|
||||
* Handles thinking content from the assistant.
|
||||
*
|
||||
* @param session The session
|
||||
* @param thingkingAssistantContent The thinking content from the assistant
|
||||
*/
|
||||
void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent);
|
||||
|
||||
/**
|
||||
* Handles tool use content from the assistant.
|
||||
*
|
||||
* @param session The session
|
||||
* @param toolUseAssistantContent The tool use content from the assistant
|
||||
*/
|
||||
void onToolUse(Session session, ToolUseAssistantContent toolUseAssistantContent);
|
||||
|
||||
/**
|
||||
* Handles tool result content from the assistant.
|
||||
*
|
||||
* @param session The session
|
||||
* @param toolResultAssistantContent The tool result content from the assistant
|
||||
*/
|
||||
void onToolResult(Session session, ToolResultAssistantContent toolResultAssistantContent);
|
||||
|
||||
/**
|
||||
* Handles other types of assistant content.
|
||||
*
|
||||
* @param session The session
|
||||
* @param other The other content from the assistant
|
||||
*/
|
||||
void onOtherContent(Session session, AssistantContent<?> other);
|
||||
|
||||
/**
|
||||
* Handles permission requests.
|
||||
*
|
||||
* @param session The session
|
||||
* @param permissionRequest The permission request
|
||||
* @return The behavior for the permission request
|
||||
*/
|
||||
Behavior onPermissionRequest(Session session, CLIControlPermissionRequest permissionRequest);
|
||||
|
||||
/**
|
||||
* Handles permission requests.
|
||||
*
|
||||
* @param session The session
|
||||
* @param requestPayload The control request payload
|
||||
* @return The response payload for the control request
|
||||
*/
|
||||
ControlResponsePayload onOtherControlRequest(Session session, ControlRequestPayload requestPayload);
|
||||
|
||||
/**
|
||||
* Handles usage information from the assistant.
|
||||
*
|
||||
* @param session The session
|
||||
* @param AssistantUsage The usage information from the assistant
|
||||
*/
|
||||
void onUsage(Session session, AssistantUsage AssistantUsage);
|
||||
|
||||
/**
|
||||
* Sets the default permission operation.
|
||||
*
|
||||
* @param defaultPermissionOperation The default permission operation
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
AssistantContentSimpleConsumers setDefaultPermissionOperation(Operation defaultPermissionOperation);
|
||||
|
||||
/**
|
||||
* Gets timeout for permission request handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @return The timeout for permission request handling
|
||||
*/
|
||||
Timeout onPermissionRequestTimeout(Session session, CLIControlPermissionRequest permissionRequest);
|
||||
|
||||
/**
|
||||
* Gets timeout for other control request handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param requestPayload The control request payload
|
||||
* @return The timeout for other control request handling
|
||||
*/
|
||||
Timeout onOtherControlRequestTimeout(Session session, ControlRequestPayload requestPayload);
|
||||
|
||||
/**
|
||||
* Gets timeout for text handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param textAssistantContent The text content from the assistant
|
||||
* @return The timeout for text handling
|
||||
*/
|
||||
Timeout onTextTimeout(Session session, TextAssistantContent textAssistantContent);
|
||||
|
||||
/**
|
||||
* Gets timeout for thinking handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param thingkingAssistantContent The thinking content from the assistant
|
||||
* @return The timeout for thinking handling
|
||||
*/
|
||||
Timeout onThinkingTimeout(Session session, ThingkingAssistantContent thingkingAssistantContent);
|
||||
|
||||
/**
|
||||
* Gets timeout for tool use handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param toolUseAssistantContent The tool use content from the assistant
|
||||
* @return The timeout for tool use handling
|
||||
*/
|
||||
Timeout onToolUseTimeout(Session session, ToolUseAssistantContent toolUseAssistantContent);
|
||||
|
||||
/**
|
||||
* Gets timeout for tool result handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param toolResultAssistantContent The tool result content from the assistant
|
||||
* @return The timeout for tool result handling
|
||||
*/
|
||||
Timeout onToolResultTimeout(Session session, ToolResultAssistantContent toolResultAssistantContent);
|
||||
|
||||
/**
|
||||
* Gets timeout for other content handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param other The other content from the assistant
|
||||
* @return The timeout for other content handling
|
||||
*/
|
||||
Timeout onOtherContentTimeout(Session session, AssistantContent<?> other);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.alibaba.qwen.code.cli.session.event.consumers;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Allow;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Deny;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlPermissionRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple implementation of AssistantContentConsumers that provides empty implementations for all methods.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class AssistantContentSimpleConsumers implements AssistantContentConsumers {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
log.debug("Received textAssistantContent {}", textAssistantContent.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
log.debug("Received thingkingAssistantContent {}", thingkingAssistantContent.getThinking());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseAssistantContent) {
|
||||
log.debug("Received toolUseAssistantContent {}", toolUseAssistantContent.getInput());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultAssistantContent) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received toolResultAssistantContent {}", toolResultAssistantContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Received other content {}", other);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Behavior onPermissionRequest(Session session, CLIControlPermissionRequest permissionRequest) {
|
||||
if (Operation.deny.equals(this.defaultPermissionOperation)) {
|
||||
log.info("use defaultPermissionOperation Permission denied.");
|
||||
return new Deny().setMessage("Permission denied.");
|
||||
} else {
|
||||
log.info("use defaultPermissionOperation Permission allowed.");
|
||||
return new Allow().setUpdatedInput(permissionRequest.getInput());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlResponsePayload onOtherControlRequest(Session session, ControlRequestPayload requestPayload) {
|
||||
throw new RuntimeException("need override onOtherControlRequest");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage AssistantUsage) {
|
||||
log.info("received usage {} of message {}", AssistantUsage.getUsage(), AssistantUsage.getMessageId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onPermissionRequestTimeout(Session session, CLIControlPermissionRequest permissionRequest) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onOtherControlRequestTimeout(Session session, ControlRequestPayload requestPayload) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onTextTimeout(Session session, TextAssistantContent textAssistantContent) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onThinkingTimeout(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onToolUseTimeout(Session session, ToolUseAssistantContent toolUseAssistantContent) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onToolResultTimeout(Session session, ToolResultAssistantContent toolResultAssistantContent) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onOtherContentTimeout(Session session, AssistantContent<?> other) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public AssistantContentSimpleConsumers setDefaultPermissionOperation(Operation defaultPermissionOperation) {
|
||||
this.defaultPermissionOperation = defaultPermissionOperation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param defaultPermissionOperation The default permission operation.
|
||||
* @param defaultEventTimeout The default event timeout.
|
||||
*/
|
||||
public AssistantContentSimpleConsumers(Operation defaultPermissionOperation, Timeout defaultEventTimeout) {
|
||||
this.defaultPermissionOperation = defaultPermissionOperation;
|
||||
this.defaultEventTimeout = defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AssistantContentSimpleConsumers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The default permission operation.
|
||||
*/
|
||||
private Operation defaultPermissionOperation = Operation.deny;
|
||||
|
||||
/**
|
||||
* The default event timeout.
|
||||
*/
|
||||
protected Timeout defaultEventTimeout = Timeout.TIMEOUT_60_SECONDS;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AssistantContentSimpleConsumers.class);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.alibaba.qwen.code.cli.session.event.consumers;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
/**
|
||||
* Interface for handling different types of events during a session.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public interface SessionEventConsumers {
|
||||
/**
|
||||
* Handles system messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param systemMessage The system message
|
||||
*/
|
||||
void onSystemMessage(Session session, SDKSystemMessage systemMessage);
|
||||
|
||||
/**
|
||||
* Handles result messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param resultMessage The result message
|
||||
*/
|
||||
void onResultMessage(Session session, SDKResultMessage resultMessage);
|
||||
|
||||
/**
|
||||
* Handles assistant messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param assistantMessage The assistant message
|
||||
*/
|
||||
void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage);
|
||||
|
||||
/**
|
||||
* Handles partial assistant messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param partialAssistantMessage The partial assistant message
|
||||
*/
|
||||
void onPartialAssistantMessage(Session session, SDKPartialAssistantMessage partialAssistantMessage);
|
||||
|
||||
/**
|
||||
* Handles user messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param userMessage The user message
|
||||
*/
|
||||
void onUserMessage(Session session, SDKUserMessage userMessage);
|
||||
|
||||
/**
|
||||
* Handles other types of messages.
|
||||
*
|
||||
* @param session The session
|
||||
* @param message The message
|
||||
*/
|
||||
void onOtherMessage(Session session, String message);
|
||||
|
||||
/**
|
||||
* Handles control responses.
|
||||
*
|
||||
* @param session The session
|
||||
* @param cliControlResponse The control response
|
||||
*/
|
||||
void onControlResponse(Session session, CLIControlResponse<?> cliControlResponse);
|
||||
|
||||
/**
|
||||
* Handles control requests.
|
||||
*
|
||||
* @param session The session
|
||||
* @param cliControlRequest The control request
|
||||
* @return The control response
|
||||
*/
|
||||
CLIControlResponse<? extends ControlResponsePayload> onControlRequest(Session session, CLIControlRequest<? extends ControlRequestPayload> cliControlRequest);
|
||||
|
||||
/**
|
||||
* Gets timeout for system message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param systemMessage The system message
|
||||
* @return The timeout for system message handling
|
||||
*/
|
||||
Timeout onSystemMessageTimeout(Session session, SDKSystemMessage systemMessage);
|
||||
|
||||
/**
|
||||
* Gets timeout for result message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param resultMessage The result message
|
||||
* @return The timeout for result message handling
|
||||
*/
|
||||
Timeout onResultMessageTimeout(Session session, SDKResultMessage resultMessage);
|
||||
|
||||
/**
|
||||
* Gets timeout for assistant message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param assistantMessage The assistant message
|
||||
* @return The timeout for assistant message handling
|
||||
*/
|
||||
Timeout onAssistantMessageTimeout(Session session, SDKAssistantMessage assistantMessage);
|
||||
|
||||
/**
|
||||
* Gets timeout for partial assistant message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param partialAssistantMessage The partial assistant message
|
||||
* @return The timeout for partial assistant message handling
|
||||
*/
|
||||
Timeout onPartialAssistantMessageTimeout(Session session, SDKPartialAssistantMessage partialAssistantMessage);
|
||||
|
||||
/**
|
||||
* Gets timeout for user message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param userMessage The user message
|
||||
* @return The timeout for user message handling
|
||||
*/
|
||||
Timeout onUserMessageTimeout(Session session, SDKUserMessage userMessage);
|
||||
|
||||
/**
|
||||
* Gets timeout for other message handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param message The message
|
||||
* @return The timeout for other message handling
|
||||
*/
|
||||
Timeout onOtherMessageTimeout(Session session, String message);
|
||||
|
||||
/**
|
||||
* Gets timeout for control response handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param cliControlResponse The control response
|
||||
* @return The timeout for control response handling
|
||||
*/
|
||||
Timeout onControlResponseTimeout(Session session, CLIControlResponse<?> cliControlResponse);
|
||||
|
||||
/**
|
||||
* Gets timeout for control request handling.
|
||||
*
|
||||
* @param session The session
|
||||
* @param cliControlRequest The control request
|
||||
* @return The timeout for control request handling
|
||||
*/
|
||||
Timeout onControlRequestTimeout(Session session, CLIControlRequest<?> cliControlRequest);
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
package com.alibaba.qwen.code.cli.session.event.consumers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Allow;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.block.ContentBlock;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.event.ContentBlockDeltaEvent;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.event.StreamEvent;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlPermissionRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlPermissionResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.utils.MyConcurrentUtils;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Simple implementation of SessionEventConsumers that provides basic implementations for all methods.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onSystemMessage(Session session, SDKSystemMessage systemMessage) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onResultMessage(Session session, SDKResultMessage resultMessage) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
||||
List<ContentBlock<?>> contentBlocks = assistantMessage.getMessage().getContent();
|
||||
if (assistantContentConsumers == null || contentBlocks == null || contentBlocks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
assistantContentConsumers.onUsage(session,
|
||||
new AssistantUsage(assistantMessage.getMessage().getId(), assistantMessage.getMessage().getUsage()));
|
||||
|
||||
if (!session.isStreaming()) {
|
||||
contentBlocks.forEach(contentBlock -> consumeAssistantContent(session, contentBlock));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onPartialAssistantMessage(Session session, SDKPartialAssistantMessage partialAssistantMessage) {
|
||||
StreamEvent event = partialAssistantMessage.getEvent();
|
||||
if (!(event instanceof ContentBlockDeltaEvent)) {
|
||||
log.debug("received partialAssistantMessage and is not instance of ContentBlockDeltaEvent, will ignore process. the message is {}",
|
||||
partialAssistantMessage);
|
||||
return;
|
||||
}
|
||||
ContentBlockDeltaEvent contentBlockDeltaEvent = (ContentBlockDeltaEvent) event;
|
||||
contentBlockDeltaEvent.getDelta().setMessageId(partialAssistantMessage.getMessageId());
|
||||
consumeAssistantContent(session, contentBlockDeltaEvent.getDelta());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>consumeAssistantContent.</p>
|
||||
*
|
||||
* @param session a {@link com.alibaba.qwen.code.cli.session.Session} object.
|
||||
* @param assistantContent a {@link com.alibaba.qwen.code.cli.protocol.data.AssistantContent} object.
|
||||
*/
|
||||
protected void consumeAssistantContent(Session session, AssistantContent<?> assistantContent) {
|
||||
if (assistantContent instanceof TextAssistantContent) {
|
||||
MyConcurrentUtils.runAndWait(() -> assistantContentConsumers.onText(session, (TextAssistantContent) assistantContent),
|
||||
Optional.ofNullable(assistantContentConsumers.onTextTimeout(session, (TextAssistantContent) assistantContent))
|
||||
.orElse(defaultEventTimeout));
|
||||
} else if (assistantContent instanceof ThingkingAssistantContent) {
|
||||
MyConcurrentUtils.runAndWait(() -> assistantContentConsumers.onThinking(session, (ThingkingAssistantContent) assistantContent),
|
||||
Optional.ofNullable(assistantContentConsumers.onThinkingTimeout(session, (ThingkingAssistantContent) assistantContent))
|
||||
.orElse(defaultEventTimeout));
|
||||
} else if (assistantContent instanceof ToolUseAssistantContent) {
|
||||
MyConcurrentUtils.runAndWait(() -> assistantContentConsumers.onToolUse(session, (ToolUseAssistantContent) assistantContent),
|
||||
Optional.ofNullable(assistantContentConsumers.onToolUseTimeout(session, (ToolUseAssistantContent) assistantContent))
|
||||
.orElse(defaultEventTimeout));
|
||||
} else if (assistantContent instanceof ToolResultAssistantContent) {
|
||||
MyConcurrentUtils.runAndWait(() -> assistantContentConsumers.onToolResult(session, (ToolResultAssistantContent) assistantContent),
|
||||
Optional.ofNullable(assistantContentConsumers.onToolResultTimeout(session, (ToolResultAssistantContent) assistantContent))
|
||||
.orElse(defaultEventTimeout));
|
||||
} else {
|
||||
MyConcurrentUtils.runAndWait(() -> assistantContentConsumers.onOtherContent(session, assistantContent),
|
||||
Optional.ofNullable(assistantContentConsumers.onOtherContentTimeout(session, assistantContent)).orElse(defaultEventTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onUserMessage(Session session, SDKUserMessage userMessage) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onOtherMessage(Session session, String message) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onControlResponse(Session session, CLIControlResponse<?> cliControlResponse) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public CLIControlResponse<? extends ControlResponsePayload> onControlRequest(Session session, CLIControlRequest<?> cliControlRequest) {
|
||||
if (assistantContentConsumers == null) {
|
||||
throw new RuntimeException("please set assistantContentConsumers or override onControlRequest of ");
|
||||
}
|
||||
ControlRequestPayload payload = cliControlRequest.getRequest();
|
||||
if (payload instanceof CLIControlPermissionRequest) {
|
||||
CLIControlPermissionRequest permissionRequest = (CLIControlPermissionRequest) payload;
|
||||
return supplyPermissionControlResponse(session, permissionRequest, cliControlRequest.getRequestId());
|
||||
} else {
|
||||
ControlRequestPayload request = cliControlRequest.getRequest();
|
||||
return supplyOtherControlResponse(session, request, cliControlRequest.getRequestId());
|
||||
}
|
||||
}
|
||||
|
||||
private CLIControlResponse<CLIControlPermissionResponse> supplyPermissionControlResponse(Session session,
|
||||
CLIControlPermissionRequest permissionRequest, String requestId) {
|
||||
Behavior behavior;
|
||||
try {
|
||||
behavior = Optional.ofNullable(
|
||||
MyConcurrentUtils.runAndWait(() -> this.assistantContentConsumers.onPermissionRequest(session, permissionRequest),
|
||||
Optional.ofNullable(assistantContentConsumers.onPermissionRequestTimeout(session, permissionRequest))
|
||||
.orElse(defaultEventTimeout)))
|
||||
.map(b -> {
|
||||
if (b instanceof Allow) {
|
||||
Allow allow = (Allow) b;
|
||||
if (allow.getUpdatedInput() == null) {
|
||||
allow.setUpdatedInput(permissionRequest.getInput());
|
||||
}
|
||||
}
|
||||
return b;
|
||||
})
|
||||
.orElse(Behavior.defaultBehavior());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process permission response", e);
|
||||
behavior = Behavior.defaultBehavior();
|
||||
}
|
||||
|
||||
CLIControlResponse<CLIControlPermissionResponse> permissionResponse = new CLIControlResponse<>();
|
||||
permissionResponse.createResponse().setResponse(new CLIControlPermissionResponse().setBehavior(behavior)).setRequestId(requestId);
|
||||
return permissionResponse;
|
||||
}
|
||||
|
||||
private CLIControlResponse<ControlResponsePayload> supplyOtherControlResponse(Session session, ControlRequestPayload requestPayload,
|
||||
String requestId) {
|
||||
ControlResponsePayload controlResponsePayload;
|
||||
try {
|
||||
controlResponsePayload = Optional.ofNullable(
|
||||
MyConcurrentUtils.runAndWait(() -> this.assistantContentConsumers.onOtherControlRequest(session, requestPayload),
|
||||
ObjectUtils.getIfNull(assistantContentConsumers.onOtherControlRequestTimeout(session, requestPayload),
|
||||
defaultEventTimeout)))
|
||||
.orElse(new ControlResponsePayload());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to process permission response", e);
|
||||
controlResponsePayload = new ControlResponsePayload();
|
||||
}
|
||||
|
||||
CLIControlResponse<ControlResponsePayload> cliControlResponse = new CLIControlResponse<>();
|
||||
cliControlResponse.createResponse().setResponse(controlResponsePayload).setRequestId(requestId);
|
||||
return cliControlResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onSystemMessageTimeout(Session session, SDKSystemMessage systemMessage) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onResultMessageTimeout(Session session, SDKResultMessage resultMessage) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onAssistantMessageTimeout(Session session, SDKAssistantMessage assistantMessage) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onPartialAssistantMessageTimeout(Session session, SDKPartialAssistantMessage partialAssistantMessage) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onUserMessageTimeout(Session session, SDKUserMessage userMessage) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onOtherMessageTimeout(Session session, String message) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onControlResponseTimeout(Session session, CLIControlResponse<?> cliControlResponse) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Timeout onControlRequestTimeout(Session session, CLIControlRequest<?> cliControlRequest) {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default event timeout.
|
||||
*
|
||||
* @return The default event timeout
|
||||
*/
|
||||
protected Timeout getDefaultEventTimeout() {
|
||||
return defaultEventTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default event timeout.
|
||||
*
|
||||
* @param defaultEventTimeout The default event timeout
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SessionEventSimpleConsumers setDefaultEventTimeout(Timeout defaultEventTimeout) {
|
||||
this.defaultEventTimeout = defaultEventTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SessionEventSimpleConsumers instance with default values.
|
||||
*/
|
||||
public SessionEventSimpleConsumers() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SessionEventSimpleConsumers instance with the specified parameters.
|
||||
*
|
||||
* @param defaultEventTimeout The default event timeout
|
||||
* @param assistantContentConsumers The assistant content consumers
|
||||
*/
|
||||
public SessionEventSimpleConsumers(Timeout defaultEventTimeout, AssistantContentConsumers assistantContentConsumers) {
|
||||
Validate.notNull(defaultEventTimeout, "defaultEventTimeout can't be null");
|
||||
Validate.notNull(assistantContentConsumers, "assistantContentConsumers can't be null");
|
||||
this.defaultEventTimeout = defaultEventTimeout;
|
||||
this.assistantContentConsumers = assistantContentConsumers;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default event timeout.
|
||||
*/
|
||||
protected Timeout defaultEventTimeout = Timeout.TIMEOUT_180_SECONDS;
|
||||
/**
|
||||
* The assistant content consumers.
|
||||
*/
|
||||
protected AssistantContentConsumers assistantContentConsumers = new AssistantContentSimpleConsumers();
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionEventSimpleConsumers.class);
|
||||
|
||||
/**
|
||||
* Sets the assistant content consumers.
|
||||
*
|
||||
* @param assistantContentConsumers The assistant content consumers
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public SessionEventSimpleConsumers setAssistantContentConsumer(AssistantContentConsumers assistantContentConsumers) {
|
||||
Validate.notNull(assistantContentConsumers, "assistantContentConsumers can't be null");
|
||||
this.assistantContentConsumers = assistantContentConsumers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the assistant content consumers.
|
||||
*
|
||||
* @return The assistant content consumers
|
||||
*/
|
||||
public AssistantContentConsumers getAssistantContentConsumers() {
|
||||
return assistantContentConsumers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.alibaba.qwen.code.cli.session.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a session control operation fails.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class SessionControlException extends Exception {
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*/
|
||||
public SessionControlException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a message.
|
||||
*
|
||||
* @param message The exception message
|
||||
*/
|
||||
public SessionControlException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a message and cause.
|
||||
*
|
||||
* @param message The exception message
|
||||
* @param cause The exception cause
|
||||
*/
|
||||
public SessionControlException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a cause.
|
||||
*
|
||||
* @param cause The exception cause
|
||||
*/
|
||||
public SessionControlException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with all parameters.
|
||||
*
|
||||
* @param message The exception message
|
||||
* @param cause The exception cause
|
||||
* @param enableSuppression Whether suppression is enabled
|
||||
* @param writableStackTrace Whether the stack trace is writable
|
||||
*/
|
||||
public SessionControlException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.alibaba.qwen.code.cli.session.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when sending a prompt in a session fails.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class SessionSendPromptException extends Exception {
|
||||
/**
|
||||
* Creates a new exception.
|
||||
*/
|
||||
public SessionSendPromptException() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a message.
|
||||
*
|
||||
* @param message The exception message
|
||||
*/
|
||||
public SessionSendPromptException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a message and cause.
|
||||
*
|
||||
* @param message The exception message
|
||||
* @param cause The exception cause
|
||||
*/
|
||||
public SessionSendPromptException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with a cause.
|
||||
*
|
||||
* @param cause The exception cause
|
||||
*/
|
||||
public SessionSendPromptException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception with all parameters.
|
||||
*
|
||||
* @param message The exception message
|
||||
* @param cause The exception cause
|
||||
* @param enableSuppression Whether suppression is enabled
|
||||
* @param writableStackTrace Whether the stack trace is writable
|
||||
*/
|
||||
public SessionSendPromptException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Defines the contract for communication with the Qwen Code CLI.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public interface Transport {
|
||||
/**
|
||||
* Gets the transport options used by this transport.
|
||||
*
|
||||
* @return The transport options
|
||||
*/
|
||||
TransportOptions getTransportOptions();
|
||||
|
||||
/**
|
||||
* Checks if the transport is currently reading.
|
||||
*
|
||||
* @return true if reading, false otherwise
|
||||
*/
|
||||
boolean isReading();
|
||||
|
||||
/**
|
||||
* Starts the transport.
|
||||
*
|
||||
* @throws java.io.IOException if starting fails
|
||||
*/
|
||||
void start() throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the transport and releases resources.
|
||||
*
|
||||
* @throws java.io.IOException if closing fails
|
||||
*/
|
||||
void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Checks if the transport is available for communication.
|
||||
*
|
||||
* @return true if available, false otherwise
|
||||
*/
|
||||
boolean isAvailable();
|
||||
|
||||
/**
|
||||
* Sends a message and waits for a single-line response.
|
||||
*
|
||||
* @param message The message to send
|
||||
* @return The response message
|
||||
* @throws java.io.IOException if an I/O error occurs
|
||||
* @throws java.util.concurrent.ExecutionException if an execution error occurs
|
||||
* @throws java.lang.InterruptedException if the operation is interrupted
|
||||
* @throws java.util.concurrent.TimeoutException if the operation times out
|
||||
*/
|
||||
String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException;
|
||||
|
||||
/**
|
||||
* Sends a message and waits for a multi-line response.
|
||||
*
|
||||
* @param message The message to send
|
||||
* @param callBackFunction A function to process each line of the response
|
||||
* @throws java.io.IOException if an I/O error occurs
|
||||
*/
|
||||
void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException;
|
||||
|
||||
/**
|
||||
* Sends a message without waiting for a response.
|
||||
*
|
||||
* @param message The message to send
|
||||
* @throws java.io.IOException if an I/O error occurs
|
||||
*/
|
||||
void inputNoWaitResponse(String message) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
/**
|
||||
* Configuration options for the transport layer.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class TransportOptions implements Cloneable {
|
||||
/**
|
||||
* Path to the Qwen executable.
|
||||
*/
|
||||
private String pathToQwenExecutable;
|
||||
/**
|
||||
* Current working directory for the CLI process.
|
||||
*/
|
||||
private String cwd;
|
||||
/**
|
||||
* Model to use for the session.
|
||||
*/
|
||||
private String model;
|
||||
/**
|
||||
* Permission mode for the session.
|
||||
*/
|
||||
private PermissionMode permissionMode;
|
||||
/**
|
||||
* Environment variables to pass to the CLI process.
|
||||
*/
|
||||
private Map<String, String> env;
|
||||
/**
|
||||
* Maximum number of turns in a session.
|
||||
*/
|
||||
private Integer maxSessionTurns;
|
||||
/**
|
||||
* List of core tools to enable.
|
||||
*/
|
||||
private List<String> coreTools;
|
||||
/**
|
||||
* List of tools to exclude.
|
||||
*/
|
||||
private List<String> excludeTools;
|
||||
/**
|
||||
* List of tools that are allowed.
|
||||
*/
|
||||
private List<String> allowedTools;
|
||||
/**
|
||||
* Authentication type to use.
|
||||
*/
|
||||
private String authType;
|
||||
/**
|
||||
* Whether to include partial messages in responses.
|
||||
*/
|
||||
private Boolean includePartialMessages;
|
||||
/**
|
||||
* Whether to enable skills.
|
||||
*/
|
||||
private Boolean skillsEnable;
|
||||
/**
|
||||
* Timeout for individual turns.
|
||||
*/
|
||||
private Timeout turnTimeout;
|
||||
/**
|
||||
* Timeout for messages.
|
||||
*/
|
||||
private Timeout messageTimeout;
|
||||
/**
|
||||
* Session ID to resume.
|
||||
*/
|
||||
private String resumeSessionId;
|
||||
/**
|
||||
* Additional options to pass to the CLI.
|
||||
*/
|
||||
private List<String> otherOptions;
|
||||
|
||||
/**
|
||||
* Gets the path to the Qwen executable.
|
||||
*
|
||||
* @return The path to the Qwen executable
|
||||
*/
|
||||
public String getPathToQwenExecutable() {
|
||||
return pathToQwenExecutable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the Qwen executable.
|
||||
*
|
||||
* @param pathToQwenExecutable The path to the Qwen executable
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setPathToQwenExecutable(String pathToQwenExecutable) {
|
||||
this.pathToQwenExecutable = pathToQwenExecutable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current working directory.
|
||||
*
|
||||
* @return The current working directory
|
||||
*/
|
||||
public String getCwd() {
|
||||
return cwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current working directory.
|
||||
*
|
||||
* @param cwd The current working directory
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setCwd(String cwd) {
|
||||
this.cwd = cwd;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model to use.
|
||||
*
|
||||
* @return The model name
|
||||
*/
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model to use.
|
||||
*
|
||||
* @param model The model name
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setModel(String model) {
|
||||
this.model = model;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permission mode.
|
||||
*
|
||||
* @return The permission mode
|
||||
*/
|
||||
public PermissionMode getPermissionMode() {
|
||||
return permissionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the permission mode.
|
||||
*
|
||||
* @param permissionMode The permission mode
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setPermissionMode(PermissionMode permissionMode) {
|
||||
this.permissionMode = permissionMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment variables.
|
||||
*
|
||||
* @return A map of environment variables
|
||||
*/
|
||||
public Map<String, String> getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the environment variables.
|
||||
*
|
||||
* @param env A map of environment variables
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of session turns.
|
||||
*
|
||||
* @return The maximum number of session turns
|
||||
*/
|
||||
public Integer getMaxSessionTurns() {
|
||||
return maxSessionTurns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of session turns.
|
||||
*
|
||||
* @param maxSessionTurns The maximum number of session turns
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setMaxSessionTurns(Integer maxSessionTurns) {
|
||||
this.maxSessionTurns = maxSessionTurns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of core tools.
|
||||
*
|
||||
* @return The list of core tools
|
||||
*/
|
||||
public List<String> getCoreTools() {
|
||||
return coreTools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of core tools.
|
||||
*
|
||||
* @param coreTools The list of core tools
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setCoreTools(List<String> coreTools) {
|
||||
this.coreTools = coreTools;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of excluded tools.
|
||||
*
|
||||
* @return The list of excluded tools
|
||||
*/
|
||||
public List<String> getExcludeTools() {
|
||||
return excludeTools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of excluded tools.
|
||||
*
|
||||
* @param excludeTools The list of excluded tools
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setExcludeTools(List<String> excludeTools) {
|
||||
this.excludeTools = excludeTools;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of allowed tools.
|
||||
*
|
||||
* @return The list of allowed tools
|
||||
*/
|
||||
public List<String> getAllowedTools() {
|
||||
return allowedTools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of allowed tools.
|
||||
*
|
||||
* @param allowedTools The list of allowed tools
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setAllowedTools(List<String> allowedTools) {
|
||||
this.allowedTools = allowedTools;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the authentication type.
|
||||
*
|
||||
* @return The authentication type
|
||||
*/
|
||||
public String getAuthType() {
|
||||
return authType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication type.
|
||||
*
|
||||
* @param authType The authentication type
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setAuthType(String authType) {
|
||||
this.authType = authType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether to include partial messages.
|
||||
*
|
||||
* @return Whether to include partial messages
|
||||
*/
|
||||
public Boolean getIncludePartialMessages() {
|
||||
return includePartialMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to include partial messages.
|
||||
*
|
||||
* @param includePartialMessages Whether to include partial messages
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setIncludePartialMessages(Boolean includePartialMessages) {
|
||||
this.includePartialMessages = includePartialMessages;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether skills are enabled.
|
||||
*
|
||||
* @return Whether skills are enabled
|
||||
*/
|
||||
public Boolean getSkillsEnable() {
|
||||
return skillsEnable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether skills are enabled.
|
||||
*
|
||||
* @param skillsEnable Whether skills are enabled
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setSkillsEnable(Boolean skillsEnable) {
|
||||
this.skillsEnable = skillsEnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the turn timeout.
|
||||
*
|
||||
* @return The turn timeout
|
||||
*/
|
||||
public Timeout getTurnTimeout() {
|
||||
return turnTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the turn timeout.
|
||||
*
|
||||
* @param turnTimeout The turn timeout
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setTurnTimeout(Timeout turnTimeout) {
|
||||
this.turnTimeout = turnTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message timeout.
|
||||
*
|
||||
* @return The message timeout
|
||||
*/
|
||||
public Timeout getMessageTimeout() {
|
||||
return messageTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message timeout.
|
||||
*
|
||||
* @param messageTimeout The message timeout
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setMessageTimeout(Timeout messageTimeout) {
|
||||
this.messageTimeout = messageTimeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the session ID to resume.
|
||||
*
|
||||
* @return The session ID to resume
|
||||
*/
|
||||
public String getResumeSessionId() {
|
||||
return resumeSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID to resume.
|
||||
*
|
||||
* @param resumeSessionId The session ID to resume
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setResumeSessionId(String resumeSessionId) {
|
||||
this.resumeSessionId = resumeSessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets additional options.
|
||||
*
|
||||
* @return Additional options
|
||||
*/
|
||||
public List<String> getOtherOptions() {
|
||||
return otherOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets additional options.
|
||||
*
|
||||
* @param otherOptions Additional options
|
||||
* @return This instance for method chaining
|
||||
*/
|
||||
public TransportOptions setOtherOptions(List<String> otherOptions) {
|
||||
this.otherOptions = otherOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TransportOptions clone() {
|
||||
try {
|
||||
return (TransportOptions) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.utils.MyConcurrentUtils;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.exception.ContextedRuntimeException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation of the Transport interface that communicates with the Qwen CLI via a process.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class ProcessTransport implements Transport {
|
||||
private static final Logger log = LoggerFactory.getLogger(ProcessTransport.class);
|
||||
private final TransportOptions transportOptions;
|
||||
protected Timeout turnTimeout;
|
||||
protected Timeout messageTimeout;
|
||||
|
||||
protected Process process;
|
||||
protected BufferedWriter processInput;
|
||||
protected BufferedReader processOutput;
|
||||
protected BufferedReader processError;
|
||||
protected final Consumer<String> errorHandler;
|
||||
|
||||
private final AtomicBoolean reading = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Constructs a new ProcessTransport with default options.
|
||||
*
|
||||
* @throws java.io.IOException if starting the process fails
|
||||
*/
|
||||
public ProcessTransport() throws IOException {
|
||||
this(new TransportOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ProcessTransport with the specified options.
|
||||
*
|
||||
* @param transportOptions The transport options to use
|
||||
* @throws java.io.IOException if starting the process fails
|
||||
*/
|
||||
public ProcessTransport(TransportOptions transportOptions) throws IOException {
|
||||
this(transportOptions, (line) -> log.error("process error: {}", line));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new ProcessTransport with the specified options and error handler.
|
||||
*
|
||||
* @param transportOptions The transport options to use
|
||||
* @param errorHandler The error handler to use
|
||||
* @throws java.io.IOException if starting the process fails
|
||||
*/
|
||||
public ProcessTransport(TransportOptions transportOptions, Consumer<String> errorHandler) throws IOException {
|
||||
this.transportOptions = transportOptions;
|
||||
this.errorHandler = errorHandler;
|
||||
start();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TransportOptions getTransportOptions() {
|
||||
return transportOptions;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isReading() {
|
||||
return reading.get();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void start() throws IOException {
|
||||
TransportOptionsAdapter transportOptionsAdapter = new TransportOptionsAdapter(transportOptions);
|
||||
this.turnTimeout = transportOptionsAdapter.getHandledTransportOptions().getTurnTimeout();
|
||||
this.messageTimeout = transportOptionsAdapter.getHandledTransportOptions().getMessageTimeout();
|
||||
|
||||
String[] commandArgs = transportOptionsAdapter.buildCommandArgs();
|
||||
log.debug("trans to command args: {}", transportOptionsAdapter);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(commandArgs)
|
||||
.redirectOutput(Redirect.PIPE)
|
||||
.redirectInput(Redirect.PIPE)
|
||||
.redirectError(Redirect.PIPE)
|
||||
.redirectErrorStream(false)
|
||||
.directory(new File(transportOptionsAdapter.getCwd()));
|
||||
|
||||
process = processBuilder.start();
|
||||
processInput = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
|
||||
processOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
processError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
startErrorReading();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (processInput != null) {
|
||||
processInput.close();
|
||||
}
|
||||
if (processOutput != null) {
|
||||
processOutput.close();
|
||||
}
|
||||
if (processError != null) {
|
||||
processError.close();
|
||||
}
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return process != null && process.isAlive();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
return inputWaitForOneLine(message, turnTimeout);
|
||||
}
|
||||
|
||||
private String inputWaitForOneLine(String message, Timeout timeOut)
|
||||
throws IOException, TimeoutException, InterruptedException, ExecutionException {
|
||||
inputNoWaitResponse(message);
|
||||
try {
|
||||
reading.set(true);
|
||||
String line = MyConcurrentUtils.runAndWait(() -> {
|
||||
try {
|
||||
return processOutput.readLine();
|
||||
} catch (IOException e) {
|
||||
throw new ContextedRuntimeException("read line error", e)
|
||||
.addContextValue("message", message);
|
||||
}
|
||||
}, timeOut);
|
||||
log.info("inputWaitForOneLine result: {}", line);
|
||||
return line;
|
||||
} finally {
|
||||
reading.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException {
|
||||
inputWaitForMultiLine(message, callBackFunction, turnTimeout);
|
||||
}
|
||||
|
||||
private void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction, Timeout timeOut) throws IOException {
|
||||
log.debug("input message for multiLine: {}", message);
|
||||
inputNoWaitResponse(message);
|
||||
MyConcurrentUtils.runAndWait(() -> iterateOutput(callBackFunction), timeOut);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void inputNoWaitResponse(String message) throws IOException {
|
||||
log.debug("input message to process: {}", message);
|
||||
processInput.write(message);
|
||||
processInput.newLine();
|
||||
processInput.flush();
|
||||
}
|
||||
|
||||
private void startErrorReading() {
|
||||
MyConcurrentUtils.asyncRun(() -> {
|
||||
try {
|
||||
for (;;) {
|
||||
final String line = processError.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
if (errorHandler != null) {
|
||||
try {
|
||||
MyConcurrentUtils.runAndWait(() -> errorHandler.accept(line), messageTimeout);
|
||||
} catch (Exception e) {
|
||||
log.warn("error handler error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Failed read error {}, caused by {}", e.getMessage(), e.getCause(), e);
|
||||
}
|
||||
}, (e, t) -> log.warn("read error {}", t.getMessage(), t));
|
||||
}
|
||||
|
||||
private void iterateOutput(Function<String, Boolean> callBackFunction) {
|
||||
try {
|
||||
reading.set(true);
|
||||
MyConcurrentUtils.runAndWait(() -> {
|
||||
try {
|
||||
for (String line = processOutput.readLine(); line != null; line = processOutput.readLine()) {
|
||||
log.debug("read a message from process {}", line);
|
||||
if (callBackFunction.apply(line)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("read process output error", e);
|
||||
}
|
||||
}, messageTimeout);
|
||||
} finally {
|
||||
reading.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Adapter that converts TransportOptions to command-line arguments for the CLI process.
|
||||
*/
|
||||
class TransportOptionsAdapter {
|
||||
/**
|
||||
* The adapted transport options.
|
||||
*/
|
||||
TransportOptions transportOptions;
|
||||
/**
|
||||
* Default timeout for turns.
|
||||
*/
|
||||
private static final Timeout DEFAULT_TURN_TIMEOUT = new Timeout(1000 * 60 * 30L, TimeUnit.MILLISECONDS);
|
||||
/**
|
||||
* Default timeout for messages.
|
||||
*/
|
||||
private static final Timeout DEFAULT_MESSAGE_TIMEOUT = new Timeout(1000 * 60 * 3L, TimeUnit.MILLISECONDS);
|
||||
|
||||
/**
|
||||
* Constructs a new adapter with the specified options.
|
||||
*
|
||||
* @param userTransportOptions The user's transport options
|
||||
*/
|
||||
TransportOptionsAdapter(TransportOptions userTransportOptions) {
|
||||
transportOptions = addDefaultTransportOptions(userTransportOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the processed transport options.
|
||||
*
|
||||
* @return The processed transport options
|
||||
*/
|
||||
TransportOptions getHandledTransportOptions() {
|
||||
return transportOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current working directory.
|
||||
*
|
||||
* @return The current working directory
|
||||
*/
|
||||
String getCwd() {
|
||||
return transportOptions.getCwd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds command-line arguments from the transport options.
|
||||
*
|
||||
* @return An array of command-line arguments
|
||||
*/
|
||||
String[] buildCommandArgs() {
|
||||
List<String> args = new ArrayList<>(
|
||||
Arrays.asList(transportOptions.getPathToQwenExecutable(), "--input-format", "stream-json", "--output-format",
|
||||
"stream-json", "--channel=SDK"));
|
||||
|
||||
if (StringUtils.isNotBlank(transportOptions.getModel())) {
|
||||
args.add("--model");
|
||||
args.add(transportOptions.getModel());
|
||||
}
|
||||
|
||||
if (transportOptions.getPermissionMode() != null) {
|
||||
args.add("--approval-mode");
|
||||
args.add(transportOptions.getPermissionMode().getValue());
|
||||
}
|
||||
|
||||
if (transportOptions.getMaxSessionTurns() != null) {
|
||||
args.add("--max-session-turns");
|
||||
args.add(transportOptions.getMaxSessionTurns().toString());
|
||||
}
|
||||
|
||||
if (transportOptions.getCoreTools() != null && !transportOptions.getCoreTools().isEmpty()) {
|
||||
args.add("--core-tools");
|
||||
args.add(String.join(",", transportOptions.getCoreTools()));
|
||||
}
|
||||
|
||||
if (transportOptions.getExcludeTools() != null && !transportOptions.getExcludeTools().isEmpty()) {
|
||||
args.add("--exclude-tools");
|
||||
args.add(String.join(",", transportOptions.getExcludeTools()));
|
||||
}
|
||||
|
||||
if (transportOptions.getAllowedTools() != null && !transportOptions.getAllowedTools().isEmpty()) {
|
||||
args.add("--allowed-tools");
|
||||
args.add(String.join(",", transportOptions.getAllowedTools()));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(transportOptions.getAuthType())) {
|
||||
args.add("--auth-type");
|
||||
args.add(transportOptions.getAuthType());
|
||||
}
|
||||
|
||||
if (transportOptions.getIncludePartialMessages() != null && transportOptions.getIncludePartialMessages()) {
|
||||
args.add("--include-partial-messages");
|
||||
}
|
||||
|
||||
if (transportOptions.getSkillsEnable() != null && transportOptions.getSkillsEnable()) {
|
||||
args.add("--experimental-skills");
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(transportOptions.getResumeSessionId())) {
|
||||
args.add("--resume");
|
||||
args.add(transportOptions.getResumeSessionId());
|
||||
}
|
||||
|
||||
if (transportOptions.getOtherOptions() != null) {
|
||||
args.addAll(transportOptions.getOtherOptions());
|
||||
}
|
||||
return args.toArray(new String[] {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default values to the user's transport options.
|
||||
*
|
||||
* @param userTransportOptions The user's transport options
|
||||
* @return The options with defaults added
|
||||
*/
|
||||
private TransportOptions addDefaultTransportOptions(TransportOptions userTransportOptions) {
|
||||
TransportOptions transportOptions = Optional.ofNullable(userTransportOptions)
|
||||
.map(TransportOptions::clone)
|
||||
.orElse(new TransportOptions());
|
||||
|
||||
if (StringUtils.isBlank(transportOptions.getPathToQwenExecutable())) {
|
||||
transportOptions.setPathToQwenExecutable("qwen");
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(transportOptions.getCwd())) {
|
||||
transportOptions.setCwd(new File("").getAbsolutePath());
|
||||
}
|
||||
|
||||
Map<String, String> env = new HashMap<>(System.getenv());
|
||||
Optional.ofNullable(transportOptions.getEnv()).ifPresent(env::putAll);
|
||||
transportOptions.setEnv(env);
|
||||
|
||||
if (transportOptions.getTurnTimeout() == null) {
|
||||
transportOptions.setTurnTimeout(DEFAULT_TURN_TIMEOUT);
|
||||
}
|
||||
|
||||
if (transportOptions.getMessageTimeout() == null) {
|
||||
transportOptions.setMessageTimeout(DEFAULT_MESSAGE_TIMEOUT);
|
||||
}
|
||||
return transportOptions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.alibaba.qwen.code.cli.utils;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Utility class for concurrent operations.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class MyConcurrentUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(MyConcurrentUtils.class);
|
||||
|
||||
/**
|
||||
* Runs a task and waits for it to complete with a timeout.
|
||||
*
|
||||
* @param runnable The task to run
|
||||
* @param timeOut The timeout for the operation
|
||||
*/
|
||||
public static void runAndWait(Runnable runnable, Timeout timeOut) {
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, ThreadPoolConfig.getExecutor());
|
||||
try {
|
||||
future.get(timeOut.getValue(), timeOut.getUnit());
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("task interrupted", e);
|
||||
future.cancel(true);
|
||||
} catch (TimeoutException e) {
|
||||
log.warn("Operation timed out", e);
|
||||
future.cancel(true);
|
||||
} catch (Exception e) {
|
||||
future.cancel(true);
|
||||
log.warn("Operation error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task that returns a value and waits for it to complete with a timeout.
|
||||
*
|
||||
* @param supplier The task to run
|
||||
* @param timeOut The timeout for the operation
|
||||
* @param <T> The type of the result
|
||||
* @return The result of the task
|
||||
* @throws java.util.concurrent.ExecutionException if an execution error occurs
|
||||
* @throws java.lang.InterruptedException if the operation is interrupted
|
||||
* @throws java.util.concurrent.TimeoutException if the operation times out
|
||||
*/
|
||||
public static <T> T runAndWait(Supplier<T> supplier, Timeout timeOut)
|
||||
throws ExecutionException, InterruptedException, TimeoutException {
|
||||
CompletableFuture<T> future = CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, ThreadPoolConfig.getExecutor());
|
||||
|
||||
try {
|
||||
return future.get(timeOut.getValue(), timeOut.getUnit());
|
||||
} catch (TimeoutException | InterruptedException | ExecutionException e) {
|
||||
future.cancel(true);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a task asynchronously with an error callback.
|
||||
*
|
||||
* @param runnable The task to run
|
||||
* @param errorCallback The error callback
|
||||
*/
|
||||
public static void asyncRun(Runnable runnable, BiConsumer<Void, Throwable> errorCallback) {
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
log.warn("async task error", e);
|
||||
}
|
||||
}, ThreadPoolConfig.getExecutor());
|
||||
future.whenComplete(errorCallback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.alibaba.qwen.code.cli.utils;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Configuration for the thread pool used by the SDK.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class ThreadPoolConfig {
|
||||
private static final ThreadPoolExecutor defaultExecutor = new ThreadPoolExecutor(
|
||||
30, 100, 60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(300),
|
||||
new ThreadFactory() {
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r, "qwen_code_cli-pool-" + threadNumber.getAndIncrement());
|
||||
t.setDaemon(false);
|
||||
return t;
|
||||
}
|
||||
},
|
||||
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
|
||||
);
|
||||
|
||||
private static Supplier<ThreadPoolExecutor> executorSupplier;
|
||||
|
||||
/**
|
||||
* Sets the supplier for the executor.
|
||||
*
|
||||
* @param executorSupplier The supplier for the executor
|
||||
*/
|
||||
public static void setExecutorSupplier(Supplier<ThreadPoolExecutor> executorSupplier) {
|
||||
ThreadPoolConfig.executorSupplier = executorSupplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default executor.
|
||||
*
|
||||
* @return The default executor
|
||||
*/
|
||||
public static ThreadPoolExecutor getDefaultExecutor() {
|
||||
return defaultExecutor;
|
||||
}
|
||||
|
||||
static ExecutorService getExecutor() {
|
||||
return Optional.ofNullable(executorSupplier).map(s -> {
|
||||
try {
|
||||
return s.get();
|
||||
} catch (Exception e) {
|
||||
return defaultExecutor;
|
||||
}
|
||||
}).orElse(defaultExecutor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.alibaba.qwen.code.cli.utils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
/**
|
||||
* Represents a timeout value with a time unit.
|
||||
*
|
||||
* @author skyfire
|
||||
* @version $Id: 0.0.1
|
||||
*/
|
||||
public class Timeout {
|
||||
/**
|
||||
* The timeout value.
|
||||
*/
|
||||
private final Long value;
|
||||
/**
|
||||
* The time unit.
|
||||
*/
|
||||
private final TimeUnit unit;
|
||||
|
||||
/**
|
||||
* Creates a new Timeout instance.
|
||||
*
|
||||
* @param value The timeout value
|
||||
* @param unit The time unit
|
||||
*/
|
||||
public Timeout(Long value, TimeUnit unit) {
|
||||
Validate.notNull(value, "value can not be null");
|
||||
Validate.notNull(unit, "unit can not be null");
|
||||
this.value = value;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeout value.
|
||||
*
|
||||
* @return The timeout value
|
||||
*/
|
||||
public Long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time unit.
|
||||
*
|
||||
* @return The time unit
|
||||
*/
|
||||
public TimeUnit getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* A timeout of 60 seconds.
|
||||
*/
|
||||
public static final Timeout TIMEOUT_60_SECONDS = new Timeout(60L, TimeUnit.SECONDS);
|
||||
|
||||
/**
|
||||
* A timeout of 180 seconds.
|
||||
*/
|
||||
public static final Timeout TIMEOUT_180_SECONDS = new Timeout(180L, TimeUnit.SECONDS);
|
||||
|
||||
/**
|
||||
* A timeout of 30 minutes.
|
||||
*/
|
||||
public static final Timeout TIMEOUT_30_MINUTES = new Timeout(60L, TimeUnit.MINUTES);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.alibaba.qwen.code.cli;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class QwenCodeCliTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(QwenCodeCliTest.class);
|
||||
@Test
|
||||
void simpleQuery() {
|
||||
List<String> result = QwenCodeCli.simpleQuery("hello world");
|
||||
log.info("simpleQuery result: {}", result);
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleQueryWithModel() {
|
||||
List<String> result = QwenCodeCli.simpleQuery("hello world", new TransportOptions().setModel("qwen-plus"));
|
||||
log.info("simpleQueryWithModel result: {}", result);
|
||||
assertNotNull(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.alibaba.qwen.code.cli.example;
|
||||
|
||||
import com.alibaba.qwen.code.cli.QwenCodeCli;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.AssistantContentSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class QuickStartExample {
|
||||
private static final Logger logger = LoggerFactory.getLogger(QuickStartExample.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
logger.info("runSimpleExample started.{}", StringUtils.repeat("=", 150));
|
||||
runSimpleExample();
|
||||
|
||||
logger.info("runTransportOptionsExample started. {}", StringUtils.repeat("=", 150));
|
||||
runTransportOptionsExample();
|
||||
|
||||
logger.info("runStreamingExample started. {}", StringUtils.repeat("=", 150));
|
||||
runStreamingExample();
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple example showing basic query usage
|
||||
*/
|
||||
public static void runSimpleExample() {
|
||||
List<String> result = QwenCodeCli.simpleQuery("hello world");
|
||||
result.forEach(logger::info);
|
||||
}
|
||||
|
||||
/**
|
||||
* TransportOptions example showing comprehensive transport options configuration
|
||||
*/
|
||||
public static void runTransportOptionsExample() {
|
||||
TransportOptions options = new TransportOptions()
|
||||
.setModel("qwen3-coder-flash")
|
||||
.setPermissionMode(PermissionMode.AUTO_EDIT)
|
||||
.setCwd("./")
|
||||
.setEnv(new HashMap<String, String>() {{put("CUSTOM_VAR", "value");}})
|
||||
.setIncludePartialMessages(true)
|
||||
.setTurnTimeout(new Timeout(120L, TimeUnit.SECONDS))
|
||||
.setMessageTimeout(new Timeout(90L, TimeUnit.SECONDS))
|
||||
.setAllowedTools(Arrays.asList("read_file", "write_file", "list_directory"));
|
||||
|
||||
List<String> result = QwenCodeCli.simpleQuery("who are you, what are your capabilities?", options);
|
||||
result.forEach(logger::info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streaming example showing simple query usage
|
||||
*/
|
||||
public static void runStreamingExample() {
|
||||
QwenCodeCli.simpleQuery("who are you, what are your capabilities?",
|
||||
new TransportOptions().setMessageTimeout(new Timeout(10L, TimeUnit.SECONDS)), new AssistantContentSimpleConsumers() {
|
||||
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
logger.info("Text content received: {}", textAssistantContent.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
logger.info("Thinking content received: {}", thingkingAssistantContent.getThinking());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseContent) {
|
||||
logger.info("Tool use content received: {} with arguments: {}",
|
||||
toolUseContent, toolUseContent.getInput());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultContent) {
|
||||
logger.info("Tool result content received: {}", toolResultContent.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
logger.info("Other content received: {}", other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage assistantUsage) {
|
||||
logger.info("Usage information received: Input tokens: {}, Output tokens: {}",
|
||||
assistantUsage.getUsage().getInputTokens(), assistantUsage.getUsage().getOutputTokens());
|
||||
}
|
||||
}.setDefaultPermissionOperation(Operation.allow));
|
||||
logger.info("Streaming example completed.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.alibaba.qwen.code.cli.example;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.qwen.code.cli.QwenCodeCli;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlRequestPayload;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.ControlResponsePayload;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.AssistantContentSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.SessionEventSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.block.TextBlock;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class SessionExample {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SessionExample.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
Session session = QwenCodeCli.newSession();
|
||||
try {
|
||||
logger.info("runPermissionModeExample started {}", StringUtils.repeat("=", 150));
|
||||
runPermissionModeExample(session);
|
||||
|
||||
logger.info("runSetModelExample started {}", StringUtils.repeat("=", 150));
|
||||
runSetModelExample(session);
|
||||
|
||||
logger.info("runSetPermissionModeExample started {}", StringUtils.repeat("=", 150));
|
||||
runSetPermissionModeExample(session);
|
||||
|
||||
logger.info("runInterruptExample started {}", StringUtils.repeat("=", 150));
|
||||
runInterruptExample(session);
|
||||
|
||||
logger.info("runSetModelExample started {}", StringUtils.repeat("=", 150));
|
||||
runSetModelExample(session);
|
||||
|
||||
logger.info("runPromptUseLowLevelEventExample started {}", StringUtils.repeat("=", 150));
|
||||
runPromptUseLowLevelEventExample(session);
|
||||
|
||||
logger.info("runPromptUseHighLevelEventExample started {}", StringUtils.repeat("=", 150));
|
||||
runPromptUseHighLevelEventExample(session);
|
||||
|
||||
System.exit(0);
|
||||
} finally {
|
||||
try {
|
||||
session.close();
|
||||
} catch (SessionControlException e) {
|
||||
logger.error("Error closing session", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to set different permission modes
|
||||
*/
|
||||
public static void runPermissionModeExample(Session session) {
|
||||
try {
|
||||
logger.info(session.setPermissionMode(PermissionMode.PLAN).map(s -> s ? "Permission mode set to PLAN" : "Permission mode set error")
|
||||
.orElse("Permission mode set unknown"));
|
||||
} catch (SessionControlException e) {
|
||||
logger.error("Error setting permission mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to interrupt a running prompt
|
||||
*/
|
||||
public static void runInterruptExample(Session session) {
|
||||
try {
|
||||
session.sendPrompt("Analyze this large codebase...", new SessionEventSimpleConsumers() {
|
||||
@Override
|
||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
||||
String message = assistantMessage.getMessage().getContent().stream()
|
||||
.findFirst()
|
||||
.filter(content -> content instanceof TextBlock)
|
||||
.map(content -> ((TextBlock) content).getText())
|
||||
.orElse("");
|
||||
logger.info("Received: {}", message);
|
||||
|
||||
// Interrupt the session after receiving the first message
|
||||
try {
|
||||
Optional<Boolean> interruptResult = session.interrupt();
|
||||
logger.info("{}", interruptResult.map(s -> s ? "Interrupt successful" : "Interrupt error")
|
||||
.orElse("Interrupt unknown"));
|
||||
} catch (SessionControlException e) {
|
||||
logger.error("Interrupt error: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while sending the prompt", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to dynamically change the AI model during a session
|
||||
*/
|
||||
public static void runSetModelExample(Session session) {
|
||||
try {
|
||||
// Switch to a specific model
|
||||
Optional<Boolean> modelChangeResult = session.setModel("qwen3-coder-flash");
|
||||
logger.info("{}", modelChangeResult.map(s -> s ? "setModel success" : "setModel error")
|
||||
.orElse("setModel unknown"));
|
||||
|
||||
// Use the model for a prompt
|
||||
session.sendPrompt("hello world", new SessionEventSimpleConsumers());
|
||||
|
||||
// Switch to another model
|
||||
Optional<Boolean> modelChangeResult2 = session.setModel("qwen3-coder-plus");
|
||||
logger.info("{}", modelChangeResult2.map(s -> s ? "setModel success" : "setModel error")
|
||||
.orElse("setModel unknown"));
|
||||
|
||||
// Use the new model for another prompt
|
||||
session.sendPrompt("list files in the current directory", new SessionEventSimpleConsumers());
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while changing model or sending prompt", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to dynamically change permission mode during a session
|
||||
*/
|
||||
public static void runSetPermissionModeExample(Session session) {
|
||||
try {
|
||||
// Switch to a permissive mode
|
||||
Optional<Boolean> permissionChangeResult = session.setPermissionMode(PermissionMode.YOLO);
|
||||
logger.info("{}", permissionChangeResult.map(s -> s ? "setPermissionMode success" : "setPermissionMode error")
|
||||
.orElse("setPermissionMode unknown"));
|
||||
|
||||
// Use the session with the new permission mode
|
||||
session.sendPrompt("in the dir src/test/temp/, create file empty file test.touch", new SessionEventSimpleConsumers());
|
||||
|
||||
// Switch to another permission mode
|
||||
Optional<Boolean> permissionChangeResult2 = session.setPermissionMode(PermissionMode.PLAN);
|
||||
logger.info("{}", permissionChangeResult2.map(s -> s ? "setPermissionMode success" : "setPermissionMode error")
|
||||
.orElse("setPermissionMode unknown"));
|
||||
|
||||
// Use the session with the new permission mode
|
||||
session.sendPrompt("rename test.touch to test_rename.touch", new SessionEventSimpleConsumers());
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while changing permission mode or sending prompt", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runPromptUseLowLevelEventExample(Session session) {
|
||||
try {
|
||||
session.setPermissionMode(PermissionMode.YOLO);
|
||||
session.sendPrompt("devlop Fibonacci function by python", new SessionEventSimpleConsumers() {
|
||||
@Override
|
||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
||||
logger.info("Received assistantMessage {}", JSON.toJSONString(assistantMessage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartialAssistantMessage(Session session, SDKPartialAssistantMessage partialAssistantMessage) {
|
||||
logger.info("Received partialAssistantMessage {}", JSON.toJSONString(partialAssistantMessage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserMessage(Session session, SDKUserMessage userMessage) {
|
||||
logger.info("Received userMessage {}", JSON.toJSONString(userMessage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherMessage(Session session, String message) {
|
||||
logger.info("Received otherMessage {}", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlResponse(Session session, CLIControlResponse<?> cliControlResponse) {
|
||||
logger.info("Received controlResponse {}", JSON.toJSONString(cliControlResponse));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CLIControlResponse<? extends ControlResponsePayload> onControlRequest(Session session, CLIControlRequest<?> cliControlRequest) {
|
||||
logger.info("Received controlRequest {}", JSON.toJSONString(cliControlRequest));
|
||||
return new CLIControlResponse<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResultMessage(Session session, SDKResultMessage resultMessage) {
|
||||
logger.info("Received resultMessage {}", JSON.toJSONString(resultMessage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSystemMessage(Session session, SDKSystemMessage systemMessage) {
|
||||
logger.info("Received systemMessage {}", JSON.toJSONString(systemMessage));
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while sending prompt", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runPromptUseHighLevelEventExample(Session session) {
|
||||
try {
|
||||
session.sendPrompt("devlop Fibonacci function by python", new SessionEventSimpleConsumers().setAssistantContentConsumer(new AssistantContentSimpleConsumers(){
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
logger.info("Received textAssistantContent {}", textAssistantContent.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
logger.info("Received thingkingAssistantContent {}", thingkingAssistantContent.getThinking());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseAssistantContent) {
|
||||
logger.info("Received toolUseAssistantContent {}", toolUseAssistantContent.getInput());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultAssistantContent) {
|
||||
logger.info("Received toolResultAssistantContent {}", toolResultAssistantContent.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
logger.info("Received other {}", other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage assistantUsage) {
|
||||
logger.info("Received usage {}", assistantUsage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControlResponsePayload onOtherControlRequest(Session session, ControlRequestPayload requestPayload) {
|
||||
logger.info("Received otherControlRequest {}", requestPayload);
|
||||
return new ControlResponsePayload();
|
||||
}
|
||||
}.setDefaultPermissionOperation(Operation.allow)));
|
||||
} catch (Exception e) {
|
||||
logger.error("An error occurred while sending prompt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.alibaba.qwen.code.cli.example;
|
||||
|
||||
import com.alibaba.qwen.code.cli.QwenCodeCli;
|
||||
import com.alibaba.qwen.code.cli.session.Session;
|
||||
import com.alibaba.qwen.code.cli.utils.ThreadPoolConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ThreadPoolConfigurationExample {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfigurationExample.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
runModifyDefaultExample();
|
||||
runCustomSupplierExample();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to set a custom thread pool supplier
|
||||
*/
|
||||
public static void runCustomSupplierExample() {
|
||||
// Set a custom thread pool supplier
|
||||
ThreadPoolConfig.setExecutorSupplier(() -> (ThreadPoolExecutor) Executors.newFixedThreadPool(20));
|
||||
logger.info("Custom thread pool supplier set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Example showing how to modify properties of the default thread pool
|
||||
*/
|
||||
public static void runModifyDefaultExample() {
|
||||
// Get the default executor and modify its properties
|
||||
ThreadPoolExecutor executor = ThreadPoolConfig.getDefaultExecutor();
|
||||
|
||||
// Modify the core pool size
|
||||
executor.setCorePoolSize(15);
|
||||
|
||||
// Modify the maximum pool size
|
||||
executor.setMaximumPoolSize(40);
|
||||
|
||||
// Modify the keep-alive time
|
||||
executor.setKeepAliveTime(120, TimeUnit.SECONDS);
|
||||
|
||||
logger.info("Default thread pool properties modified");
|
||||
|
||||
// The SDK will now use the modified executor for all operations
|
||||
Session session = QwenCodeCli.newSession();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.alibaba.qwen.code.cli.session;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.qwen.code.cli.QwenCodeCli;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantUsage;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.TextAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ThingkingAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolResultAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent.ToolUseAssistantContent;
|
||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Behavior.Operation;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKResultMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.AssistantContentSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.SessionEventConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.event.consumers.SessionEventSimpleConsumers;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class SessionTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SessionTest.class);
|
||||
|
||||
@Test
|
||||
void partialSendPromptSuccessfully() throws SessionControlException, SessionSendPromptException {
|
||||
Session session = QwenCodeCli.newSession(new TransportOptions().setIncludePartialMessages(true));
|
||||
session.sendPrompt("in the dir src/test/temp/, create file empty file test.touch", new SessionEventSimpleConsumers() {
|
||||
}.setAssistantContentConsumer(new AssistantContentSimpleConsumers() {
|
||||
@Override
|
||||
public void onText(Session session, TextAssistantContent textAssistantContent) {
|
||||
log.info("receive textAssistantContent {}", textAssistantContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThinking(Session session, ThingkingAssistantContent thingkingAssistantContent) {
|
||||
log.info("receive thingkingAssistantContent {}", thingkingAssistantContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolUse(Session session, ToolUseAssistantContent toolUseAssistantContent) {
|
||||
log.info("receive toolUseAssistantContent {}", toolUseAssistantContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToolResult(Session session, ToolResultAssistantContent toolResultAssistantContent) {
|
||||
log.info("receive toolResultAssistantContent {}", toolResultAssistantContent);
|
||||
}
|
||||
|
||||
public void onOtherContent(Session session, AssistantContent<?> other) {
|
||||
log.info("receive otherContent {}", other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUsage(Session session, AssistantUsage assistantUsage) {
|
||||
log.info("receive assistantUsage {}", assistantUsage);
|
||||
}
|
||||
}.setDefaultPermissionOperation(Operation.allow)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void setPermissionModeSuccessfully() throws SessionControlException, SessionSendPromptException {
|
||||
Session session = QwenCodeCli.newSession(new TransportOptions());
|
||||
|
||||
log.info(session.setPermissionMode(PermissionMode.YOLO).map(s -> s ? "setPermissionMode 1 success" : "setPermissionMode 1 error")
|
||||
.orElse("setPermissionMode 1 unknown"));
|
||||
session.sendPrompt("in the dir src/test/temp/, create file empty file test.touch", new SessionEventSimpleConsumers());
|
||||
|
||||
log.info(session.setPermissionMode(PermissionMode.PLAN).map(s -> s ? "setPermissionMode 2 success" : "setPermissionMode 2 error")
|
||||
.orElse("setPermissionMode 2 unknown"));
|
||||
session.sendPrompt("rename test.touch to test_rename.touch", new SessionEventSimpleConsumers());
|
||||
|
||||
log.info(session.setPermissionMode(PermissionMode.AUTO_EDIT).map(s -> s ? "setPermissionMode 3 success" : "setPermissionMode 3 error")
|
||||
.orElse("setPermissionMode 3 unknown"));
|
||||
session.sendPrompt("rename test.touch to test_rename.touch", new SessionEventSimpleConsumers());
|
||||
|
||||
session.sendPrompt("rename test.touch to test_rename.touch again user will allow",
|
||||
new SessionEventSimpleConsumers().setAssistantContentConsumer(new AssistantContentSimpleConsumers().setDefaultPermissionOperation(Operation.allow)));
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendPromptAndSetModelSuccessfully() throws SessionControlException, SessionSendPromptException {
|
||||
Session session = QwenCodeCli.newSession(new TransportOptions());
|
||||
|
||||
log.info(session.setModel("qwen3-coder-flash").map(s -> s ? "setModel 1 success" : "setModel 1 error").orElse("setModel 1 unknown"));
|
||||
writeSplitLine("setModel 1 end");
|
||||
|
||||
session.sendPrompt("hello world", new SessionEventSimpleConsumers());
|
||||
writeSplitLine("prompt 1 end");
|
||||
|
||||
log.info(session.setModel("qwen3-coder-plus").map(s -> s ? "setModel 2 success" : "setModel 2 error").orElse("setModel 2 unknown"));
|
||||
writeSplitLine("setModel 1 end");
|
||||
|
||||
session.sendPrompt("Check how many files are in the current directory", new SessionEventSimpleConsumers());
|
||||
writeSplitLine("prompt 2 end");
|
||||
|
||||
log.info(session.setModel("qwen3-max").map(s -> s ? "setModel 3 success" : "setModel 3 error").orElse("setModel 3 unknown"));
|
||||
writeSplitLine("setModel 1 end");
|
||||
|
||||
session.sendPrompt("Check how many xml files are in the current directory", new SessionEventSimpleConsumers());
|
||||
writeSplitLine("prompt 3 end");
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendPromptAndInterruptContinueSuccessfully() throws SessionControlException, SessionSendPromptException {
|
||||
Session session = QwenCodeCli.newSession();
|
||||
|
||||
SessionEventConsumers sessionEventConsumers = new SessionEventSimpleConsumers() {
|
||||
|
||||
@Override
|
||||
public void onSystemMessage(Session session, SDKSystemMessage systemMessage) {
|
||||
log.info("systemMessage: {}", systemMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResultMessage(Session session, SDKResultMessage resultMessage) {
|
||||
log.info("resultMessage: {}", resultMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
||||
log.info("assistantMessage: {}", assistantMessage);
|
||||
try {
|
||||
session.interrupt();
|
||||
} catch (SessionControlException e) {
|
||||
log.error("interrupt error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlResponse(Session session, CLIControlResponse<?> cliControlResponse) {
|
||||
log.info("cliControlResponse: {}", cliControlResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOtherMessage(Session session, String message) {
|
||||
log.info("otherMessage: {}", message);
|
||||
}
|
||||
}.setDefaultEventTimeout(new Timeout(90L, TimeUnit.SECONDS));
|
||||
|
||||
session.sendPrompt("Check how many files are in the current directory", sessionEventConsumers);
|
||||
writeSplitLine("prompt 1 end");
|
||||
|
||||
session.continueSession();
|
||||
session.sendPrompt("hello world", sessionEventConsumers);
|
||||
writeSplitLine("prompt 2 end");
|
||||
|
||||
session.continueSession();
|
||||
session.sendPrompt("当前目录有多少个java文件", sessionEventConsumers);
|
||||
writeSplitLine("prompt 3 end");
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
public void writeSplitLine(String line) {
|
||||
log.info("{} {}", line, StringUtils.repeat("=", 300));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJSON() {
|
||||
String json
|
||||
= "{\"type\":\"assistant\",\"uuid\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\","
|
||||
+ "\"session_id\":\"166badc0-e6d3-4978-ae47-4ccd51c468ef\",\"message\":{\"content\":[{\"text\":\"Hello! How can I help you with the"
|
||||
+ " Qwen Code SDK for Java today?\",\"type\":\"text\"}],\"id\":\"ed8374fe-a4eb-4fc0-9780-9bd2fd831cda\","
|
||||
+ "\"model\":\"qwen3-coder-plus\",\"role\":\"assistant\",\"type\":\"message\",\"usage\":{\"cache_read_input_tokens\":12766,"
|
||||
+ "\"input_tokens\":12770,\"output_tokens\":17,\"total_tokens\":12787}}}";
|
||||
SDKAssistantMessage assistantMessage = JSON.parseObject(json, SDKAssistantMessage.class);
|
||||
log.info("the assistantMessage: {}", assistantMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alibaba.qwen.code.cli.transport;
|
||||
|
||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class PermissionModeTest {
|
||||
|
||||
@Test
|
||||
public void shouldBeReturnQwenPermissionModeValue() {
|
||||
assertEquals("default", PermissionMode.DEFAULT.getValue());
|
||||
assertEquals("plan", PermissionMode.PLAN.getValue());
|
||||
assertEquals("auto-edit", PermissionMode.AUTO_EDIT.getValue());
|
||||
assertEquals("yolo", PermissionMode.YOLO.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.alibaba.qwen.code.cli.transport.process;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.TypeReference;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlInitializeRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.payload.CLIControlInitializeResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlResponse;
|
||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class ProcessTransportTest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProcessTransportTest.class);
|
||||
|
||||
@Test
|
||||
void shouldStartAndCloseSuccessfully() throws IOException {
|
||||
TransportOptions transportOptions = new TransportOptions();
|
||||
Transport transport = new ProcessTransport(transportOptions);
|
||||
transport.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInputWaitForOneLineSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
TransportOptions transportOptions = new TransportOptions();
|
||||
Transport transport = new ProcessTransport(transportOptions);
|
||||
|
||||
String message = "{\"type\": \"control_request\", \"request_id\": \"1\", \"request\": {\"subtype\": \"initialize\"} }";
|
||||
System.out.println(transport.inputWaitForOneLine(message));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldInitializeSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
Transport transport = new ProcessTransport();
|
||||
|
||||
String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
|
||||
String responseMsg = transport.inputWaitForOneLine(message);
|
||||
logger.info("responseMsg: {}", responseMsg);
|
||||
CLIControlResponse<CLIControlInitializeResponse> response = JSON.parseObject(responseMsg,
|
||||
new TypeReference<CLIControlResponse<CLIControlInitializeResponse>>() {});
|
||||
logger.info("response: {}", response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSdkMessageSuccessfully() throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||
Transport transport = new ProcessTransport();
|
||||
String message = CLIControlRequest.create(new CLIControlInitializeRequest()).toString();
|
||||
transport.inputWaitForOneLine(message);
|
||||
|
||||
String sessionId = "session-" + UUID.randomUUID().toString();
|
||||
String userMessage = new SDKUserMessage().setSessionId(sessionId).setContent("hello world").toString();
|
||||
transport.inputWaitForMultiLine(userMessage, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage2 = new SDKUserMessage().setSessionId(sessionId).setContent("请使用中文").toString();
|
||||
transport.inputWaitForMultiLine(userMessage2, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage3 = new SDKUserMessage().setSessionId(sessionId).setContent("当前工作区有多少个文件").toString();
|
||||
transport.inputWaitForMultiLine(userMessage3, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
String userMessage4 = new SDKUserMessage().setSessionId("session-sec" + UUID.randomUUID()).setContent("有多少个xml文件").toString();
|
||||
transport.inputWaitForMultiLine(userMessage4, line -> {
|
||||
return "result".equals(JSON.parseObject(line).getString("type"));
|
||||
});
|
||||
|
||||
transport.inputWaitForOneLine(CLIControlRequest.create(new CLIControlInitializeRequest()).toString());
|
||||
transport.inputWaitForMultiLine(new SDKUserMessage().setContent("您好").toString(),
|
||||
line -> "result".equals(JSON.parseObject(line).getString("type")));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user