mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-04-28 11:41:04 +00:00
for partial message processing and event timeout processing
This commit is contained in:
parent
ac7ba95d65
commit
e4caa7a856
30 changed files with 934 additions and 254 deletions
|
|
@ -25,6 +25,7 @@
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<checkstyle-maven-plugin.version>3.6.0</checkstyle-maven-plugin.version>
|
<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>
|
<junit5.version>5.14.1</junit5.version>
|
||||||
<logback-classic.version>1.3.16</logback-classic.version>
|
<logback-classic.version>1.3.16</logback-classic.version>
|
||||||
<fastjson2.version>2.0.60</fastjson2.version>
|
<fastjson2.version>2.0.60</fastjson2.version>
|
||||||
|
|
@ -81,6 +82,25 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</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>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package com.alibaba.qwen.code.cli;
|
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
|
||||||
|
|
||||||
public class Options extends TransportOptions {
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
package com.alibaba.qwen.code.cli;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.Message;
|
|
||||||
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.session.Session;
|
|
||||||
import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
|
|
||||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
|
||||||
import com.alibaba.qwen.code.cli.transport.process.ProcessTransport;
|
|
||||||
|
|
||||||
public class QwenCli {
|
|
||||||
public static List<Message> query(String prompt) {
|
|
||||||
Transport transport;
|
|
||||||
try {
|
|
||||||
transport = new ProcessTransport();
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<Message> response = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
session.sendPrompt(prompt, new SessionEventSimpleConsumers() {
|
|
||||||
@Override
|
|
||||||
public void onSystemMessage(Session session, SDKSystemMessage systemMessage) {
|
|
||||||
response.add(systemMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
|
||||||
response.add(assistantMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("sendPrompt error!", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
session.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("close Session error!", e);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.alibaba.qwen.code.cli;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
public class QwenCodeCli {
|
||||||
|
public static List<String> simpleQuery(String prompt) {
|
||||||
|
final List<String> response = new ArrayList<>();
|
||||||
|
MyConcurrentUtils.runAndWait(() -> simpleQuery(prompt, response::add), Timeout.TIMEOUT_30_MINUTES);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void simpleQuery(String prompt, Consumer<String> messageConsumer) {
|
||||||
|
Session session = newSessionWithProcessTransport(new TransportOptions());
|
||||||
|
try {
|
||||||
|
session.sendPrompt(prompt, new SessionEventSimpleConsumers() {
|
||||||
|
@Override
|
||||||
|
public void onAssistantMessageIncludePartial(Session session, List<AssistantContent> assistantContents, AssistantMessageOutputType assistantMessageOutputType) {
|
||||||
|
messageConsumer.accept(assistantContents.stream()
|
||||||
|
.map(AssistantContent::getContent)
|
||||||
|
.map(content -> {
|
||||||
|
if (content instanceof String) {
|
||||||
|
return (String) content;
|
||||||
|
} else {
|
||||||
|
return JSON.toJSONString(content);
|
||||||
|
}
|
||||||
|
}).collect(Collectors.joining()));
|
||||||
|
}
|
||||||
|
}.setDefaultPermissionOperation(Operation.allow));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("sendPrompt error!", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
session.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("close Session error!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Session newSessionWithProcessTransport(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,6 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.data;
|
||||||
|
|
||||||
|
public interface AssistantContent {
|
||||||
|
String getType();
|
||||||
|
Object getContent();
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,11 @@ public class SDKAssistantMessage extends MessageBase {
|
||||||
@JSONField(name = "parent_tool_use_id")
|
@JSONField(name = "parent_tool_use_id")
|
||||||
private String parentToolUseId;
|
private String parentToolUseId;
|
||||||
|
|
||||||
|
public SDKAssistantMessage() {
|
||||||
|
super();
|
||||||
|
this.type = "assistant";
|
||||||
|
}
|
||||||
|
|
||||||
public String getUuid() {
|
public String getUuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "stream_event")
|
||||||
|
public class SDKPartialAssistantMessage extends MessageBase {
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@JSONField(name = "session_id")
|
||||||
|
private String sessionId;
|
||||||
|
private StreamEvent event;
|
||||||
|
|
||||||
|
@JSONField(name = "parent_tool_use_id")
|
||||||
|
private String parentToolUseId;
|
||||||
|
|
||||||
|
public SDKPartialAssistantMessage() {
|
||||||
|
super();
|
||||||
|
this.type = "stream_event";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionId(String sessionId) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEvent(StreamEvent event) {
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParentToolUseId() {
|
||||||
|
return parentToolUseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentToolUseId(String parentToolUseId) {
|
||||||
|
this.parentToolUseId = parentToolUseId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,9 +4,10 @@ import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.annotation.JSONType;
|
import com.alibaba.fastjson2.annotation.JSONType;
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||||
|
|
||||||
@JSONType(typeKey = "type", typeName = "ContentBlock", seeAlso = { TextBlock.class, ToolResultBlock.class, ThinkingBlock.class, ToolUseBlock.class })
|
@JSONType(typeKey = "type", typeName = "ContentBlock", seeAlso = { TextBlock.class, ToolResultBlock.class, ThinkingBlock.class, ToolUseBlock.class })
|
||||||
public class ContentBlock {
|
public abstract class ContentBlock implements AssistantContent {
|
||||||
protected String type;
|
protected String type;
|
||||||
protected List<Annotation> annotations;
|
protected List<Annotation> annotations;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,9 @@ public class TextBlock extends ContentBlock {
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,4 +22,9 @@ public class ThinkingBlock extends ContentBlock{
|
||||||
public void setSignature(String signature) {
|
public void setSignature(String signature) {
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return thinking;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,4 +46,9 @@ public class ToolUseBlock extends ContentBlock {
|
||||||
public void setAnnotations(List<Annotation> annotations) {
|
public void setAnnotations(List<Annotation> annotations) {
|
||||||
this.annotations = annotations;
|
this.annotations = annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
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.data.AssistantContent;
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "content_block_delta")
|
||||||
|
public class ContentBlockDeltaEvent extends StreamEvent {
|
||||||
|
private int index;
|
||||||
|
private ContentBlockDelta delta;
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentBlockDelta getDelta() {
|
||||||
|
return delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDelta(ContentBlockDelta delta) {
|
||||||
|
this.delta = delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "ContentBlockDelta",
|
||||||
|
seeAlso = {ContentBlockDeltaText.class, ContentBlockDeltaThinking.class, ContentBlockDeltaInputJson.class})
|
||||||
|
public abstract static class ContentBlockDelta implements AssistantContent {
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "text_delta")
|
||||||
|
public static class ContentBlockDeltaText extends ContentBlockDelta {
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "thinking_delta")
|
||||||
|
public static class ContentBlockDeltaThinking extends ContentBlockDelta {
|
||||||
|
private String thinking;
|
||||||
|
|
||||||
|
public String getThinking() {
|
||||||
|
return thinking;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThinking(String thinking) {
|
||||||
|
this.thinking = thinking;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return thinking;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "input_json_delta")
|
||||||
|
public static class ContentBlockDeltaInputJson extends ContentBlockDelta {
|
||||||
|
@JSONField(name = "partial_json")
|
||||||
|
private String partialJson;
|
||||||
|
|
||||||
|
public String getPartialJson() {
|
||||||
|
return partialJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartialJson(String partialJson) {
|
||||||
|
this.partialJson = partialJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getContent() {
|
||||||
|
return partialJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "content_block_start")
|
||||||
|
public class ContentBlockStartEvent extends StreamEvent{
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
@JSONField(name = "content_block")
|
||||||
|
private ContentBlock contentBlock;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.annotation.JSONType;
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "content_block_stop")
|
||||||
|
public class ContentBlockStopEvent extends StreamEvent{
|
||||||
|
Long index;
|
||||||
|
|
||||||
|
public Long getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(Long index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.annotation.JSONType;
|
||||||
|
|
||||||
|
@JSONType(typeName = "message_start")
|
||||||
|
public class MessageStartStreamEvent extends StreamEvent{
|
||||||
|
private Message message;
|
||||||
|
|
||||||
|
public static class Message {
|
||||||
|
private String id;
|
||||||
|
private String role;
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(String role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(String model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(Message message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.annotation.JSONType;
|
||||||
|
|
||||||
|
@JSONType(typeName = "message_stop")
|
||||||
|
public class MessageStopStreamEvent extends StreamEvent{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.message.assistant.event;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.annotation.JSONType;
|
||||||
|
|
||||||
|
@JSONType(typeKey = "type", typeName = "StreamEvent",
|
||||||
|
seeAlso = {MessageStartStreamEvent.class, MessageStopStreamEvent.class, ContentBlockStartEvent.class, ContentBlockStopEvent.class,
|
||||||
|
ContentBlockDeltaEvent.class})
|
||||||
|
public class StreamEvent {
|
||||||
|
protected String type;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.alibaba.qwen.code.cli.protocol.message.control;
|
||||||
|
|
||||||
|
public class CLIControlSetModelResponse {
|
||||||
|
String subtype = "set_model";
|
||||||
|
String model;
|
||||||
|
|
||||||
|
public String getSubtype() {
|
||||||
|
return subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubtype(String subtype) {
|
||||||
|
this.subtype = subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(String model) {
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,8 @@ package com.alibaba.qwen.code.cli.session;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
|
@ -15,6 +17,7 @@ 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.SDKSystemMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
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.SDKAssistantMessage;
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
|
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeRequest;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
|
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInitializeResponse;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInterruptRequest;
|
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlInterruptRequest;
|
||||||
|
|
@ -28,16 +31,19 @@ import com.alibaba.qwen.code.cli.session.event.SessionEventConsumers;
|
||||||
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
||||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.MyConcurrentUtils;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Session {
|
public class Session {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Session.class);
|
||||||
private final Transport transport;
|
private final Transport transport;
|
||||||
private CLIControlInitializeResponse lastCliControlInitializeResponse;
|
private CLIControlInitializeResponse lastCliControlInitializeResponse;
|
||||||
private SDKSystemMessage lastSdkSystemMessage;
|
private SDKSystemMessage lastSdkSystemMessage;
|
||||||
private static final Logger log = LoggerFactory.getLogger(Session.class);
|
private final Timeout defaultEventTimeout = Timeout.TIMEOUT_60_SECONDS;
|
||||||
|
|
||||||
public Session(Transport transport) throws SessionControlException {
|
public Session(Transport transport) throws SessionControlException {
|
||||||
if (transport == null || !transport.isAvailable()) {
|
if (transport == null || !transport.isAvailable()) {
|
||||||
|
|
@ -61,43 +67,44 @@ public class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interrupt() throws SessionControlException {
|
public void close() throws SessionControlException {
|
||||||
if (!isAvailable()) {
|
|
||||||
throw new SessionControlException("Session is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
transport.inputNoWaitResponse(
|
transport.close();
|
||||||
new CLIControlRequest<CLIControlInterruptRequest>().setRequest(new CLIControlInterruptRequest()).toString());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SessionControlException("Failed to interrupt the session", e);
|
throw new SessionControlException("Failed to close the session", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModel(String modelName) throws SessionControlException {
|
public Optional<Boolean> interrupt() throws SessionControlException {
|
||||||
if (!isAvailable()) {
|
checkAvailable();
|
||||||
throw new SessionControlException("Session is not available");
|
return processControlRequest(new CLIControlRequest<CLIControlInterruptRequest>().setRequest(new CLIControlInterruptRequest()).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> setModel(String modelName) throws SessionControlException {
|
||||||
|
checkAvailable();
|
||||||
CLIControlSetModelRequest cliControlSetModelRequest = new CLIControlSetModelRequest();
|
CLIControlSetModelRequest cliControlSetModelRequest = new CLIControlSetModelRequest();
|
||||||
cliControlSetModelRequest.setModel(modelName);
|
cliControlSetModelRequest.setModel(modelName);
|
||||||
try {
|
return processControlRequest(new CLIControlRequest<CLIControlSetModelRequest>().setRequest(cliControlSetModelRequest).toString());
|
||||||
transport.inputNoWaitResponse(new CLIControlRequest<CLIControlSetModelRequest>().setRequest(cliControlSetModelRequest).toString());
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SessionControlException("Failed to set model", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPermissionMode(PermissionMode permissionMode) throws SessionControlException {
|
public Optional<Boolean> setPermissionMode(PermissionMode permissionMode) throws SessionControlException {
|
||||||
if (!isAvailable()) {
|
checkAvailable();
|
||||||
throw new SessionControlException("Session is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIControlSetPermissionModeRequest cliControlSetPermissionModeRequest = new CLIControlSetPermissionModeRequest();
|
CLIControlSetPermissionModeRequest cliControlSetPermissionModeRequest = new CLIControlSetPermissionModeRequest();
|
||||||
cliControlSetPermissionModeRequest.setMode(permissionMode.getValue());
|
cliControlSetPermissionModeRequest.setMode(permissionMode.getValue());
|
||||||
|
return processControlRequest(
|
||||||
|
new CLIControlRequest<CLIControlSetPermissionModeRequest>().setRequest(cliControlSetPermissionModeRequest).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Boolean> processControlRequest(String request) throws SessionControlException {
|
||||||
try {
|
try {
|
||||||
transport.inputNoWaitResponse(
|
if (transport.isReading()) {
|
||||||
new CLIControlRequest<CLIControlSetPermissionModeRequest>().setRequest(cliControlSetPermissionModeRequest).toString());
|
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) {
|
} catch (Exception e) {
|
||||||
throw new SessionControlException("Failed to set model", e);
|
throw new SessionControlException("Failed to set model", e);
|
||||||
}
|
}
|
||||||
|
|
@ -108,61 +115,43 @@ public class Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resumeSession(String sessionId) throws SessionControlException {
|
public void resumeSession(String sessionId) throws SessionControlException {
|
||||||
if (!isAvailable()) {
|
|
||||||
throw new SessionControlException("Session is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(sessionId)) {
|
if (StringUtils.isNotBlank(sessionId)) {
|
||||||
transport.getTransportOptions().setResumeSessionId(sessionId);
|
transport.getTransportOptions().setResumeSessionId(sessionId);
|
||||||
}
|
}
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSessionId() {
|
public void sendPrompt(String prompt, SessionEventConsumers sessionEventConsumers) throws SessionSendPromptException, SessionControlException {
|
||||||
return Optional.ofNullable(lastSdkSystemMessage).map(SDKSystemMessage::getSessionId).orElse(null);
|
checkAvailable();
|
||||||
}
|
|
||||||
|
|
||||||
public void close() throws SessionControlException {
|
|
||||||
try {
|
|
||||||
transport.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SessionControlException("Failed to close the session", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return transport.isAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Capabilities getCapabilities() {
|
|
||||||
return Optional.ofNullable(lastCliControlInitializeResponse).map(CLIControlInitializeResponse::getCapabilities).orElse(new Capabilities());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendPrompt(String prompt, SessionEventConsumers sessionEventConsumers) throws SessionSendPromptException {
|
|
||||||
if (!transport.isAvailable()) {
|
|
||||||
throw new SessionSendPromptException("Session is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
transport.inputWaitForMultiLine(new SDKUserMessage().setContent(prompt).toString(), (line) -> {
|
transport.inputWaitForMultiLine(new SDKUserMessage().setContent(prompt).toString(), (line) -> {
|
||||||
log.debug("read a message from agent {}", line);
|
|
||||||
JSONObject jsonObject = JSON.parseObject(line);
|
JSONObject jsonObject = JSON.parseObject(line);
|
||||||
String messageType = jsonObject.getString("type");
|
String messageType = jsonObject.getString("type");
|
||||||
if ("system".equals(messageType)) {
|
if ("system".equals(messageType)) {
|
||||||
lastSdkSystemMessage = jsonObject.to(SDKSystemMessage.class);
|
lastSdkSystemMessage = jsonObject.to(SDKSystemMessage.class);
|
||||||
sessionEventConsumers.onSystemMessage(this, lastSdkSystemMessage);
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onSystemMessage(this, lastSdkSystemMessage),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onSystemMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
return false;
|
return false;
|
||||||
} else if ("assistant".equals(messageType)) {
|
} else if ("assistant".equals(messageType)) {
|
||||||
sessionEventConsumers.onAssistantMessage(this, jsonObject.to(SDKAssistantMessage.class));
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onAssistantMessage(this, jsonObject.to(SDKAssistantMessage.class)),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onAssistantMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
|
return false;
|
||||||
|
} else if ("stream_event".equals(messageType)) {
|
||||||
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onPartialAssistantMessage(this, jsonObject.to(SDKPartialAssistantMessage.class)),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onPartialAssistantMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
return false;
|
return false;
|
||||||
} else if ("user".equals(messageType)) {
|
} else if ("user".equals(messageType)) {
|
||||||
sessionEventConsumers.onUserMessage(this, jsonObject.to(SDKUserMessage.class, Feature.FieldBased));
|
MyConcurrentUtils.runAndWait(
|
||||||
|
() -> sessionEventConsumers.onUserMessage(this, jsonObject.to(SDKUserMessage.class, Feature.FieldBased)),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onUserMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
return false;
|
return false;
|
||||||
} else if ("result".equals(messageType)) {
|
} else if ("result".equals(messageType)) {
|
||||||
sessionEventConsumers.onResultMessage(this, jsonObject.to(SDKResultMessage.class));
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onResultMessage(this, jsonObject.to(SDKResultMessage.class)),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onResultMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
return true;
|
return true;
|
||||||
} else if ("control_response".equals(messageType)) {
|
} else if ("control_response".equals(messageType)) {
|
||||||
sessionEventConsumers.onControlResponse(this, jsonObject.to(CLIControlResponse.class));
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onControlResponse(this, jsonObject.to(CLIControlResponse.class)),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onControlResponseTimeout(this)).orElse(defaultEventTimeout));
|
||||||
if (!"error".equals(jsonObject.getString("subtype"))) {
|
if (!"error".equals(jsonObject.getString("subtype"))) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -170,10 +159,11 @@ public class Session {
|
||||||
return "error".equals(jsonObject.getString("subtype"));
|
return "error".equals(jsonObject.getString("subtype"));
|
||||||
}
|
}
|
||||||
} else if ("control_request".equals(messageType)) {
|
} else if ("control_request".equals(messageType)) {
|
||||||
return processControlRequest(jsonObject, sessionEventConsumers);
|
return processControlRequestInThePrompting(jsonObject, sessionEventConsumers);
|
||||||
} else {
|
} else {
|
||||||
log.warn("unknown message type: {}", messageType);
|
log.warn("unknown message type: {}", messageType);
|
||||||
sessionEventConsumers.onOtherMessage(this, line);
|
MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onOtherMessage(this, line),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onOtherMessageTimeout(this)).orElse(defaultEventTimeout));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -182,7 +172,7 @@ public class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processControlRequest(JSONObject jsonObject, SessionEventConsumers sessionEventConsumers) {
|
private boolean processControlRequestInThePrompting(JSONObject jsonObject, SessionEventConsumers sessionEventConsumers) {
|
||||||
String subType = Optional.of(jsonObject)
|
String subType = Optional.of(jsonObject)
|
||||||
.map(cr -> cr.getJSONObject("request"))
|
.map(cr -> cr.getJSONObject("request"))
|
||||||
.map(r -> r.getString("subtype"))
|
.map(r -> r.getString("subtype"))
|
||||||
|
|
@ -190,13 +180,21 @@ public class Session {
|
||||||
if ("can_use_tool".equals(subType)) {
|
if ("can_use_tool".equals(subType)) {
|
||||||
try {
|
try {
|
||||||
return processPermissionResponse(jsonObject, sessionEventConsumers);
|
return processPermissionResponse(jsonObject, sessionEventConsumers);
|
||||||
} catch (IOException e) {
|
} catch (IOException | ExecutionException | InterruptedException | TimeoutException e) {
|
||||||
log.error("Failed to process permission response", e);
|
log.error("Failed to process permission response", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CLIControlResponse<?> cliControlResponse = sessionEventConsumers.onControlRequest(this,
|
CLIControlResponse<?> cliControlResponse;
|
||||||
jsonObject.to(new TypeReference<CLIControlRequest<?>>() {}));
|
try {
|
||||||
|
cliControlResponse = MyConcurrentUtils.runAndWait(
|
||||||
|
() -> sessionEventConsumers.onControlRequest(this, jsonObject.to(new TypeReference<CLIControlRequest<?>>() {})),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onControlRequestTimeout(this)).orElse(defaultEventTimeout));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to process control request", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (cliControlResponse != null) {
|
if (cliControlResponse != null) {
|
||||||
try {
|
try {
|
||||||
transport.inputNoWaitResponse(cliControlResponse.toString());
|
transport.inputNoWaitResponse(cliControlResponse.toString());
|
||||||
|
|
@ -209,9 +207,13 @@ public class Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean processPermissionResponse(JSONObject jsonObject, SessionEventConsumers sessionEventConsumers) throws IOException {
|
private boolean processPermissionResponse(JSONObject jsonObject, SessionEventConsumers sessionEventConsumers)
|
||||||
CLIControlRequest<CLIControlPermissionRequest> permissionRequest = jsonObject.to(new TypeReference<CLIControlRequest<CLIControlPermissionRequest>>() {});
|
throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||||
Behavior behavior = Optional.ofNullable(sessionEventConsumers.onPermissionRequest(this, permissionRequest))
|
CLIControlRequest<CLIControlPermissionRequest> permissionRequest = jsonObject.to(
|
||||||
|
new TypeReference<CLIControlRequest<CLIControlPermissionRequest>>() {});
|
||||||
|
|
||||||
|
Behavior behavior = Optional.ofNullable(MyConcurrentUtils.runAndWait(() -> sessionEventConsumers.onPermissionRequest(this, permissionRequest),
|
||||||
|
Optional.ofNullable(sessionEventConsumers.onPermissionRequestTimeout(this)).orElse(defaultEventTimeout)))
|
||||||
.map(b -> {
|
.map(b -> {
|
||||||
if (b instanceof Allow) {
|
if (b instanceof Allow) {
|
||||||
Allow allow = (Allow) b;
|
Allow allow = (Allow) b;
|
||||||
|
|
@ -223,11 +225,30 @@ public class Session {
|
||||||
})
|
})
|
||||||
.orElse(Behavior.defaultBehavior());
|
.orElse(Behavior.defaultBehavior());
|
||||||
CLIControlResponse<CLIControlPermissionResponse> permissionResponse = new CLIControlResponse<>();
|
CLIControlResponse<CLIControlPermissionResponse> permissionResponse = new CLIControlResponse<>();
|
||||||
permissionResponse.createResponse().setResponse(new CLIControlPermissionResponse().setBehavior(behavior)).setRequestId(permissionRequest.getRequestId());
|
permissionResponse.createResponse().setResponse(new CLIControlPermissionResponse().setBehavior(behavior)).setRequestId(
|
||||||
|
permissionRequest.getRequestId());
|
||||||
String permissionMessage = permissionResponse.toString();
|
String permissionMessage = permissionResponse.toString();
|
||||||
log.debug("send permission message to agent: {}", permissionMessage);
|
log.debug("send permission message to agent: {}", permissionMessage);
|
||||||
transport.inputNoWaitResponse(permissionMessage);
|
transport.inputNoWaitResponse(permissionMessage);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return Optional.ofNullable(lastSdkSystemMessage).map(SDKSystemMessage::getSessionId).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return transport.isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,12 @@ 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.SDKSystemMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
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.SDKAssistantMessage;
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlPermissionRequest;
|
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlPermissionRequest;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
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.CLIControlResponse;
|
||||||
import com.alibaba.qwen.code.cli.session.Session;
|
import com.alibaba.qwen.code.cli.session.Session;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||||
|
|
||||||
public interface SessionEventConsumers {
|
public interface SessionEventConsumers {
|
||||||
void onSystemMessage(Session session, SDKSystemMessage systemMessage);
|
void onSystemMessage(Session session, SDKSystemMessage systemMessage);
|
||||||
|
|
@ -17,6 +19,8 @@ public interface SessionEventConsumers {
|
||||||
|
|
||||||
void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage);
|
void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage);
|
||||||
|
|
||||||
|
void onPartialAssistantMessage(Session session, SDKPartialAssistantMessage partialAssistantMessage);
|
||||||
|
|
||||||
void onUserMessage(Session session, SDKUserMessage userMessage);
|
void onUserMessage(Session session, SDKUserMessage userMessage);
|
||||||
|
|
||||||
void onOtherMessage(Session session, String message);
|
void onOtherMessage(Session session, String message);
|
||||||
|
|
@ -26,4 +30,22 @@ public interface SessionEventConsumers {
|
||||||
CLIControlResponse<?> onControlRequest(Session session, CLIControlRequest<?> cliControlRequest);
|
CLIControlResponse<?> onControlRequest(Session session, CLIControlRequest<?> cliControlRequest);
|
||||||
|
|
||||||
Behavior onPermissionRequest(Session session, CLIControlRequest<CLIControlPermissionRequest> permissionRequest);
|
Behavior onPermissionRequest(Session session, CLIControlRequest<CLIControlPermissionRequest> permissionRequest);
|
||||||
|
|
||||||
|
Timeout onSystemMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onResultMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onAssistantMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onPartialAssistantMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onUserMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onOtherMessageTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onControlResponseTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onControlRequestTimeout(Session session);
|
||||||
|
|
||||||
|
Timeout onPermissionRequestTimeout(Session session);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,28 @@
|
||||||
package com.alibaba.qwen.code.cli.session.event;
|
package com.alibaba.qwen.code.cli.session.event;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.data.AssistantContent;
|
||||||
|
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;
|
||||||
|
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.SDKResultMessage;
|
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.SDKSystemMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.SDKUserMessage;
|
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.SDKAssistantMessage;
|
||||||
|
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKPartialAssistantMessage;
|
||||||
|
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.CLIControlPermissionRequest;
|
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlPermissionRequest;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.control.CLIControlRequest;
|
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.CLIControlResponse;
|
||||||
import com.alibaba.qwen.code.cli.session.Session;
|
import com.alibaba.qwen.code.cli.session.Session;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||||
|
|
||||||
public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -21,6 +35,22 @@ public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
public void onAssistantMessage(Session session, SDKAssistantMessage assistantMessage) {
|
||||||
|
onAssistantMessageIncludePartial(session, Optional.ofNullable(assistantMessage.getMessage().getContent())
|
||||||
|
.map(cbs -> cbs.stream().map(cb -> (AssistantContent) cb).collect(Collectors.toList()))
|
||||||
|
.orElse(new ArrayList<>()), AssistantMessageOutputType.entire);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPartialAssistantMessage(Session session, SDKPartialAssistantMessage partialAssistantMessage) {
|
||||||
|
StreamEvent event = partialAssistantMessage.getEvent();
|
||||||
|
if (!(event instanceof ContentBlockDeltaEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onAssistantMessageIncludePartial(session, Collections.singletonList(((ContentBlockDeltaEvent) event).getDelta()), AssistantMessageOutputType.partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAssistantMessageIncludePartial(Session session, List<AssistantContent> assistantContents,
|
||||||
|
AssistantMessageOutputType assistantMessageOutputType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -42,6 +72,89 @@ public class SessionEventSimpleConsumers implements SessionEventConsumers {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Behavior onPermissionRequest(Session session, CLIControlRequest<CLIControlPermissionRequest> permissionRequest) {
|
public Behavior onPermissionRequest(Session session, CLIControlRequest<CLIControlPermissionRequest> permissionRequest) {
|
||||||
return Behavior.defaultBehavior();
|
if (Operation.deny.equals(this.defaultPermissionOperation)) {
|
||||||
|
return new Deny().setMessage("Permission denied.");
|
||||||
|
} else {
|
||||||
|
return new Allow().setUpdatedInput(permissionRequest.getRequest().getInput());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onSystemMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onResultMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onAssistantMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onPartialAssistantMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onUserMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onOtherMessageTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onControlResponseTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onControlRequestTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Timeout onPermissionRequestTimeout(Session session) {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeout getDefaultEventTimeout() {
|
||||||
|
return defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionEventSimpleConsumers setDefaultEventTimeout(Timeout defaultEventTimeout) {
|
||||||
|
this.defaultEventTimeout = defaultEventTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operation getDefaultPermissionOperation() {
|
||||||
|
return defaultPermissionOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionEventSimpleConsumers setDefaultPermissionOperation(Operation defaultPermissionOperation) {
|
||||||
|
this.defaultPermissionOperation = defaultPermissionOperation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionEventSimpleConsumers() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionEventSimpleConsumers(Operation defaultPermissionOperation, Timeout defaultEventTimeout) {
|
||||||
|
this.defaultPermissionOperation = defaultPermissionOperation;
|
||||||
|
this.defaultEventTimeout = defaultEventTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Operation defaultPermissionOperation = Operation.deny;
|
||||||
|
protected Timeout defaultEventTimeout = Timeout.TIMEOUT_60_SECONDS;
|
||||||
|
|
||||||
|
public enum AssistantMessageOutputType {
|
||||||
|
entire,
|
||||||
|
partial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import java.util.function.Function;
|
||||||
public interface Transport {
|
public interface Transport {
|
||||||
TransportOptions getTransportOptions();
|
TransportOptions getTransportOptions();
|
||||||
|
|
||||||
|
boolean isReading();
|
||||||
|
|
||||||
void start() throws IOException;
|
void start() throws IOException;
|
||||||
|
|
||||||
void close() throws IOException;
|
void close() throws IOException;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
import com.alibaba.qwen.code.cli.protocol.data.PermissionMode;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||||
|
|
||||||
public class TransportOptions implements Cloneable {
|
public class TransportOptions implements Cloneable {
|
||||||
private String pathToQwenExecutable;
|
private String pathToQwenExecutable;
|
||||||
|
|
@ -17,120 +18,154 @@ public class TransportOptions implements Cloneable {
|
||||||
private List<String> allowedTools;
|
private List<String> allowedTools;
|
||||||
private String authType;
|
private String authType;
|
||||||
private Boolean includePartialMessages;
|
private Boolean includePartialMessages;
|
||||||
private Long turnTimeoutMs;
|
private Boolean skillsEnable;
|
||||||
private Long messageTimeoutMs;
|
private Timeout turnTimeout;
|
||||||
|
private Timeout messageTimeout;
|
||||||
private String resumeSessionId;
|
private String resumeSessionId;
|
||||||
|
private List<String> otherOptions;
|
||||||
|
|
||||||
public String getPathToQwenExecutable() {
|
public String getPathToQwenExecutable() {
|
||||||
return pathToQwenExecutable;
|
return pathToQwenExecutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPathToQwenExecutable(String pathToQwenExecutable) {
|
public TransportOptions setPathToQwenExecutable(String pathToQwenExecutable) {
|
||||||
this.pathToQwenExecutable = pathToQwenExecutable;
|
this.pathToQwenExecutable = pathToQwenExecutable;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCwd() {
|
public String getCwd() {
|
||||||
return cwd;
|
return cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCwd(String cwd) {
|
public TransportOptions setCwd(String cwd) {
|
||||||
this.cwd = cwd;
|
this.cwd = cwd;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getModel() {
|
public String getModel() {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModel(String model) {
|
public TransportOptions setModel(String model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PermissionMode getPermissionMode() {
|
public PermissionMode getPermissionMode() {
|
||||||
return permissionMode;
|
return permissionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPermissionMode(PermissionMode permissionMode) {
|
public TransportOptions setPermissionMode(PermissionMode permissionMode) {
|
||||||
this.permissionMode = permissionMode;
|
this.permissionMode = permissionMode;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getEnv() {
|
public Map<String, String> getEnv() {
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnv(Map<String, String> env) {
|
public TransportOptions setEnv(Map<String, String> env) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getMaxSessionTurns() {
|
public Integer getMaxSessionTurns() {
|
||||||
return maxSessionTurns;
|
return maxSessionTurns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxSessionTurns(Integer maxSessionTurns) {
|
public TransportOptions setMaxSessionTurns(Integer maxSessionTurns) {
|
||||||
this.maxSessionTurns = maxSessionTurns;
|
this.maxSessionTurns = maxSessionTurns;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getCoreTools() {
|
public List<String> getCoreTools() {
|
||||||
return coreTools;
|
return coreTools;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCoreTools(List<String> coreTools) {
|
public TransportOptions setCoreTools(List<String> coreTools) {
|
||||||
this.coreTools = coreTools;
|
this.coreTools = coreTools;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getExcludeTools() {
|
public List<String> getExcludeTools() {
|
||||||
return excludeTools;
|
return excludeTools;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExcludeTools(List<String> excludeTools) {
|
public TransportOptions setExcludeTools(List<String> excludeTools) {
|
||||||
this.excludeTools = excludeTools;
|
this.excludeTools = excludeTools;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAllowedTools() {
|
public List<String> getAllowedTools() {
|
||||||
return allowedTools;
|
return allowedTools;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllowedTools(List<String> allowedTools) {
|
public TransportOptions setAllowedTools(List<String> allowedTools) {
|
||||||
this.allowedTools = allowedTools;
|
this.allowedTools = allowedTools;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthType() {
|
public String getAuthType() {
|
||||||
return authType;
|
return authType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuthType(String authType) {
|
public TransportOptions setAuthType(String authType) {
|
||||||
this.authType = authType;
|
this.authType = authType;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getIncludePartialMessages() {
|
public Boolean getIncludePartialMessages() {
|
||||||
return includePartialMessages;
|
return includePartialMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIncludePartialMessages(Boolean includePartialMessages) {
|
public TransportOptions setIncludePartialMessages(Boolean includePartialMessages) {
|
||||||
this.includePartialMessages = includePartialMessages;
|
this.includePartialMessages = includePartialMessages;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getTurnTimeoutMs() {
|
public Boolean getSkillsEnable() {
|
||||||
return turnTimeoutMs;
|
return skillsEnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTurnTimeoutMs(Long turnTimeoutMs) {
|
public TransportOptions setSkillsEnable(Boolean skillsEnable) {
|
||||||
this.turnTimeoutMs = turnTimeoutMs;
|
this.skillsEnable = skillsEnable;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getMessageTimeoutMs() {
|
public Timeout getTurnTimeout() {
|
||||||
return messageTimeoutMs;
|
return turnTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessageTimeoutMs(Long messageTimeoutMs) {
|
public TransportOptions setTurnTimeout(Timeout turnTimeout) {
|
||||||
this.messageTimeoutMs = messageTimeoutMs;
|
this.turnTimeout = turnTimeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeout getMessageTimeout() {
|
||||||
|
return messageTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportOptions setMessageTimeout(Timeout messageTimeout) {
|
||||||
|
this.messageTimeout = messageTimeout;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getResumeSessionId() {
|
public String getResumeSessionId() {
|
||||||
return resumeSessionId;
|
return resumeSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResumeSessionId(String resumeSessionId) {
|
public TransportOptions setResumeSessionId(String resumeSessionId) {
|
||||||
this.resumeSessionId = resumeSessionId;
|
this.resumeSessionId = resumeSessionId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOtherOptions() {
|
||||||
|
return otherOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransportOptions setOtherOptions(List<String> otherOptions) {
|
||||||
|
this.otherOptions = otherOptions;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package com.alibaba.qwen.code.cli.transport.process;
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
import com.alibaba.qwen.code.cli.transport.Transport;
|
||||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
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.apache.commons.lang3.exception.ContextedRuntimeException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
@ -14,29 +16,37 @@ import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.lang.ProcessBuilder.Redirect;
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ProcessTransport implements Transport {
|
public class ProcessTransport implements Transport {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ProcessTransport.class);
|
private static final Logger log = LoggerFactory.getLogger(ProcessTransport.class);
|
||||||
private final TransportOptions transportOptions;
|
private final TransportOptions transportOptions;
|
||||||
protected Long turnTimeoutMs;
|
protected Timeout turnTimeout;
|
||||||
protected Long messageTimeoutMs;
|
protected Timeout messageTimeout;
|
||||||
|
|
||||||
protected Process process;
|
protected Process process;
|
||||||
protected BufferedWriter processInput;
|
protected BufferedWriter processInput;
|
||||||
protected BufferedReader processOutput;
|
protected BufferedReader processOutput;
|
||||||
protected BufferedReader processError;
|
protected BufferedReader processError;
|
||||||
|
protected final Consumer<String> errorHandler;
|
||||||
|
|
||||||
|
private final AtomicBoolean reading = new AtomicBoolean(false);
|
||||||
|
|
||||||
public ProcessTransport() throws IOException {
|
public ProcessTransport() throws IOException {
|
||||||
this(new TransportOptions());
|
this(new TransportOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProcessTransport(TransportOptions transportOptions) throws IOException {
|
public ProcessTransport(TransportOptions transportOptions) throws IOException {
|
||||||
|
this(transportOptions, (line) -> log.error("process error: {}", line));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessTransport(TransportOptions transportOptions, Consumer<String> errorHandler) throws IOException {
|
||||||
this.transportOptions = transportOptions;
|
this.transportOptions = transportOptions;
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,11 +55,16 @@ public class ProcessTransport implements Transport {
|
||||||
return transportOptions;
|
return transportOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReading() {
|
||||||
|
return reading.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws IOException {
|
public void start() throws IOException {
|
||||||
TransportOptionsAdapter transportOptionsAdapter = new TransportOptionsAdapter(transportOptions);
|
TransportOptionsAdapter transportOptionsAdapter = new TransportOptionsAdapter(transportOptions);
|
||||||
this.turnTimeoutMs = transportOptionsAdapter.getHandledTransportOptions().getTurnTimeoutMs();
|
this.turnTimeout = transportOptionsAdapter.getHandledTransportOptions().getTurnTimeout();
|
||||||
this.messageTimeoutMs = transportOptionsAdapter.getHandledTransportOptions().getMessageTimeoutMs();
|
this.messageTimeout = transportOptionsAdapter.getHandledTransportOptions().getMessageTimeout();
|
||||||
|
|
||||||
String[] commandArgs = transportOptionsAdapter.buildCommandArgs();
|
String[] commandArgs = transportOptionsAdapter.buildCommandArgs();
|
||||||
log.debug("trans to command args: {}", transportOptionsAdapter);
|
log.debug("trans to command args: {}", transportOptionsAdapter);
|
||||||
|
|
@ -91,113 +106,87 @@ public class ProcessTransport implements Transport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
public String inputWaitForOneLine(String message) throws IOException, ExecutionException, InterruptedException, TimeoutException {
|
||||||
return inputWaitForOneLine(message, turnTimeoutMs);
|
return inputWaitForOneLine(message, turnTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String inputWaitForOneLine(String message, long timeOutInMs)
|
private String inputWaitForOneLine(String message, Timeout timeOut)
|
||||||
throws IOException, TimeoutException, InterruptedException, ExecutionException {
|
throws IOException, TimeoutException, InterruptedException, ExecutionException {
|
||||||
inputNoWaitResponse(message);
|
inputNoWaitResponse(message);
|
||||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
|
|
||||||
try {
|
|
||||||
return processOutput.readLine();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ContextedRuntimeException("read line error", e)
|
|
||||||
.addContextValue("message", message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String line = future.get(timeOutInMs, TimeUnit.MILLISECONDS);
|
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);
|
log.info("inputWaitForOneLine result: {}", line);
|
||||||
return line;
|
return line;
|
||||||
} catch (TimeoutException e) {
|
} finally {
|
||||||
future.cancel(true);
|
reading.set(false);
|
||||||
log.warn("read message timeout {}, canceled readOneLine task", timeOutInMs, e);
|
|
||||||
throw e;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("interrupted task, canceled task", e);
|
|
||||||
throw e;
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("the readOneLine task execute error", e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException {
|
public void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction) throws IOException {
|
||||||
inputWaitForMultiLine(message, callBackFunction, turnTimeoutMs);
|
inputWaitForMultiLine(message, callBackFunction, turnTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction, long timeOutInMs) throws IOException {
|
private void inputWaitForMultiLine(String message, Function<String, Boolean> callBackFunction, Timeout timeOut) throws IOException {
|
||||||
log.debug("input message for multiLine: {}", message);
|
log.debug("input message for multiLine: {}", message);
|
||||||
inputNoWaitResponse(message);
|
inputNoWaitResponse(message);
|
||||||
|
MyConcurrentUtils.runAndWait(() -> iterateOutput(callBackFunction), timeOut);
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> iterateOutput(callBackFunction));
|
|
||||||
try {
|
|
||||||
future.get(timeOutInMs, TimeUnit.MILLISECONDS);
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("read message timeout {}, canceled readMultiMessages task", timeOutInMs, e);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("interrupted task, canceled task", e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("the readMultiMessages task execute error", e);
|
|
||||||
} catch (Exception e) {
|
|
||||||
future.cancel(true);
|
|
||||||
log.warn("other error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inputNoWaitResponse(String message) throws IOException {
|
public void inputNoWaitResponse(String message) throws IOException {
|
||||||
log.debug("input message to agent: {}", message);
|
log.debug("input message to process: {}", message);
|
||||||
processInput.write(message);
|
processInput.write(message);
|
||||||
processInput.newLine();
|
processInput.newLine();
|
||||||
processInput.flush();
|
processInput.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startErrorReading() {
|
private void startErrorReading() {
|
||||||
CompletableFuture.runAsync(() -> {
|
MyConcurrentUtils.asyncRun(() -> {
|
||||||
try {
|
try {
|
||||||
String line;
|
for (;;) {
|
||||||
while ((line = processError.readLine()) != null) {
|
final String line = processError.readLine();
|
||||||
System.err.println("错误: " + line);
|
if (line == null) {
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("错误: " + e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void iterateOutput(Function<String, Boolean> callBackFunction) {
|
|
||||||
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
|
|
||||||
try {
|
|
||||||
for (String line = processOutput.readLine(); line != null; line = processOutput.readLine()) {
|
|
||||||
log.debug("read a message from agent {}", line);
|
|
||||||
if (callBackFunction.apply(line)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (errorHandler != null) {
|
||||||
|
try {
|
||||||
|
MyConcurrentUtils.runAndWait(() -> errorHandler.accept(line), messageTimeout);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("error handler error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("read process output error", 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 {
|
try {
|
||||||
future.get(messageTimeoutMs, TimeUnit.MILLISECONDS);
|
reading.set(true);
|
||||||
} catch (InterruptedException e) {
|
MyConcurrentUtils.runAndWait(() -> {
|
||||||
log.warn("read message task interrupted", e);
|
try {
|
||||||
future.cancel(true);
|
for (String line = processOutput.readLine(); line != null; line = processOutput.readLine()) {
|
||||||
} catch (TimeoutException e) {
|
log.debug("read a message from process {}", line);
|
||||||
log.warn("Operation timed out", e);
|
if (callBackFunction.apply(line)) {
|
||||||
future.cancel(true);
|
break;
|
||||||
} catch (Exception e) {
|
}
|
||||||
future.cancel(true);
|
}
|
||||||
log.warn("Operation error", e);
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("read process output error", e);
|
||||||
|
}
|
||||||
|
}, messageTimeout);
|
||||||
|
} finally {
|
||||||
|
reading.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.alibaba.qwen.code.cli.transport.process;
|
package com.alibaba.qwen.code.cli.transport.process;
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
import com.alibaba.qwen.code.cli.transport.TransportOptions;
|
||||||
|
import com.alibaba.qwen.code.cli.utils.Timeout;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
|
@ -11,11 +12,12 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
class TransportOptionsAdapter {
|
class TransportOptionsAdapter {
|
||||||
TransportOptions transportOptions;
|
TransportOptions transportOptions;
|
||||||
private static final Long DEFAULT_TURN_TIMEOUT_MS = 1000 * 60 * 30L;
|
private static final Timeout DEFAULT_TURN_TIMEOUT = new Timeout(1000 * 60 * 30L, TimeUnit.MILLISECONDS);
|
||||||
private static final Long DEFAULT_MESSAGE_TIMEOUT_MS = 1000 * 60 * 3L;
|
private static final Timeout DEFAULT_MESSAGE_TIMEOUT = new Timeout(1000 * 60 * 3L, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
TransportOptionsAdapter(TransportOptions userTransportOptions) {
|
TransportOptionsAdapter(TransportOptions userTransportOptions) {
|
||||||
transportOptions = addDefaultTransportOptions(userTransportOptions);
|
transportOptions = addDefaultTransportOptions(userTransportOptions);
|
||||||
|
|
@ -73,10 +75,18 @@ class TransportOptionsAdapter {
|
||||||
args.add("--include-partial-messages");
|
args.add("--include-partial-messages");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transportOptions.getSkillsEnable() != null && transportOptions.getSkillsEnable()) {
|
||||||
|
args.add("--experimental-skills");
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(transportOptions.getResumeSessionId())) {
|
if (StringUtils.isNotBlank(transportOptions.getResumeSessionId())) {
|
||||||
args.add("--resume");
|
args.add("--resume");
|
||||||
args.add(transportOptions.getResumeSessionId());
|
args.add(transportOptions.getResumeSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (transportOptions.getOtherOptions() != null) {
|
||||||
|
args.addAll(transportOptions.getOtherOptions());
|
||||||
|
}
|
||||||
return args.toArray(new String[] {});
|
return args.toArray(new String[] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,12 +107,12 @@ class TransportOptionsAdapter {
|
||||||
Optional.ofNullable(transportOptions.getEnv()).ifPresent(env::putAll);
|
Optional.ofNullable(transportOptions.getEnv()).ifPresent(env::putAll);
|
||||||
transportOptions.setEnv(env);
|
transportOptions.setEnv(env);
|
||||||
|
|
||||||
if (transportOptions.getTurnTimeoutMs() == null) {
|
if (transportOptions.getTurnTimeout() == null) {
|
||||||
transportOptions.setTurnTimeoutMs(DEFAULT_TURN_TIMEOUT_MS);
|
transportOptions.setTurnTimeout(DEFAULT_TURN_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transportOptions.getMessageTimeoutMs() == null) {
|
if (transportOptions.getMessageTimeout() == null) {
|
||||||
transportOptions.setMessageTimeoutMs(DEFAULT_MESSAGE_TIMEOUT_MS);
|
transportOptions.setMessageTimeout(DEFAULT_MESSAGE_TIMEOUT);
|
||||||
}
|
}
|
||||||
return transportOptions;
|
return transportOptions;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class MyConcurrentUtils {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MyConcurrentUtils.class);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,43 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class ThreadPoolConfig {
|
||||||
|
private static final ThreadPoolExecutor defaultExecutor = new ThreadPoolExecutor(
|
||||||
|
10, 30, 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;
|
||||||
|
public static void setExecutorSupplier(Supplier<ThreadPoolExecutor> executorSupplier) {
|
||||||
|
ThreadPoolConfig.executorSupplier = executorSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExecutorService getExecutor() {
|
||||||
|
return Optional.ofNullable(executorSupplier).map(s -> {
|
||||||
|
try {
|
||||||
|
return s.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return defaultExecutor;
|
||||||
|
}
|
||||||
|
}).orElse(defaultExecutor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.alibaba.qwen.code.cli.utils;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
public class Timeout {
|
||||||
|
private final Long value;
|
||||||
|
private final TimeUnit 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeUnit getUnit() {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Timeout TIMEOUT_60_SECONDS = new Timeout(60L, TimeUnit.SECONDS);
|
||||||
|
public static final Timeout TIMEOUT_30_MINUTES = new Timeout(60L, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
@ -2,21 +2,18 @@ package com.alibaba.qwen.code.cli;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.Message;
|
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.assistant.SDKAssistantMessage;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
class QwenCliTest {
|
class QwenCodeCliTest {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(QwenCliTest.class);
|
private static final Logger log = LoggerFactory.getLogger(QwenCodeCliTest.class);
|
||||||
@Test
|
@Test
|
||||||
void query() {
|
void simpleQuery() {
|
||||||
List<Message> result = QwenCli.query("hello world");
|
List<String> result = QwenCodeCli.simpleQuery("hello world");
|
||||||
log.info("result: {}", result);
|
log.info("result: {}", result);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package com.alibaba.qwen.code.cli.session;
|
package com.alibaba.qwen.code.cli.session;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
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.PermissionMode;
|
||||||
import com.alibaba.qwen.code.cli.protocol.data.behavior.Allow;
|
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;
|
||||||
|
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.SDKResultMessage;
|
||||||
import com.alibaba.qwen.code.cli.protocol.message.SDKSystemMessage;
|
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.assistant.SDKAssistantMessage;
|
||||||
|
|
@ -17,6 +20,7 @@ import com.alibaba.qwen.code.cli.session.event.SessionEventSimpleConsumers;
|
||||||
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
import com.alibaba.qwen.code.cli.session.exception.SessionControlException;
|
||||||
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
import com.alibaba.qwen.code.cli.session.exception.SessionSendPromptException;
|
||||||
import com.alibaba.qwen.code.cli.transport.Transport;
|
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.transport.process.ProcessTransport;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
@ -28,18 +32,34 @@ class SessionTest {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SessionTest.class);
|
private static final Logger log = LoggerFactory.getLogger(SessionTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void partialSendPromptSuccessfully() throws IOException, SessionControlException, SessionSendPromptException {
|
||||||
|
Transport transport = new ProcessTransport(new TransportOptions().setIncludePartialMessages(true));
|
||||||
|
Session session = new Session(transport);
|
||||||
|
session.sendPrompt("in the dir src/test/temp/, create file empty file test.touch", new SessionEventSimpleConsumers() {
|
||||||
|
@Override
|
||||||
|
public void onAssistantMessageIncludePartial(Session session, List<AssistantContent> assistantContents,
|
||||||
|
AssistantMessageOutputType assistantMessageOutputType) {
|
||||||
|
log.info("onAssistantMessageIncludePartial: {}", JSON.toJSONString(assistantContents));
|
||||||
|
}
|
||||||
|
}.setDefaultPermissionOperation(Operation.allow));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setPermissionModeSuccessfully() throws IOException, SessionControlException, SessionSendPromptException {
|
void setPermissionModeSuccessfully() throws IOException, SessionControlException, SessionSendPromptException {
|
||||||
Transport transport = new ProcessTransport();
|
Transport transport = new ProcessTransport();
|
||||||
Session session = new Session(transport);
|
Session session = new Session(transport);
|
||||||
|
|
||||||
session.setPermissionMode(PermissionMode.YOLO);
|
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());
|
session.sendPrompt("in the dir src/test/temp/, create file empty file test.touch", new SessionEventSimpleConsumers());
|
||||||
|
|
||||||
session.setPermissionMode(PermissionMode.PLAN);
|
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());
|
session.sendPrompt("rename test.touch to test_rename.touch", new SessionEventSimpleConsumers());
|
||||||
|
|
||||||
session.setPermissionMode(PermissionMode.AUTO_EDIT);
|
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", new SessionEventSimpleConsumers());
|
||||||
|
|
||||||
session.sendPrompt("rename test.touch to test_rename.touch again user will allow", new SessionEventSimpleConsumers() {
|
session.sendPrompt("rename test.touch to test_rename.touch again user will allow", new SessionEventSimpleConsumers() {
|
||||||
|
|
@ -57,19 +77,19 @@ class SessionTest {
|
||||||
Transport transport = new ProcessTransport();
|
Transport transport = new ProcessTransport();
|
||||||
Session session = new Session(transport);
|
Session session = new Session(transport);
|
||||||
|
|
||||||
session.setModel("qwen3-coder-flash");
|
log.info(session.setModel("qwen3-coder-flash").map(s -> s ? "setModel 1 success" : "setModel 1 error").orElse("setModel 1 unknown"));
|
||||||
writeSplitLine("setModel 1 end");
|
writeSplitLine("setModel 1 end");
|
||||||
|
|
||||||
session.sendPrompt("hello world", new SessionEventSimpleConsumers());
|
session.sendPrompt("hello world", new SessionEventSimpleConsumers());
|
||||||
writeSplitLine("prompt 1 end");
|
writeSplitLine("prompt 1 end");
|
||||||
|
|
||||||
session.setModel("qwen3-coder-plus");
|
log.info(session.setModel("qwen3-coder-plus").map(s -> s ? "setModel 2 success" : "setModel 2 error").orElse("setModel 2 unknown"));
|
||||||
writeSplitLine("setModel 1 end");
|
writeSplitLine("setModel 1 end");
|
||||||
|
|
||||||
session.sendPrompt("查看下当前目录有多少个文件", new SessionEventSimpleConsumers());
|
session.sendPrompt("查看下当前目录有多少个文件", new SessionEventSimpleConsumers());
|
||||||
writeSplitLine("prompt 2 end");
|
writeSplitLine("prompt 2 end");
|
||||||
|
|
||||||
session.setModel("qwen3-max");
|
log.info(session.setModel("qwen3-max").map(s -> s ? "setModel 3 success" : "setModel 3 error").orElse("setModel 3 unknown"));
|
||||||
writeSplitLine("setModel 1 end");
|
writeSplitLine("setModel 1 end");
|
||||||
|
|
||||||
session.sendPrompt("查看下当前目录有多少个xml文件", new SessionEventSimpleConsumers());
|
session.sendPrompt("查看下当前目录有多少个xml文件", new SessionEventSimpleConsumers());
|
||||||
|
|
@ -129,12 +149,17 @@ class SessionTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeSplitLine(String line) {
|
public void writeSplitLine(String line) {
|
||||||
log.info("{} {}",line, StringUtils.repeat("=", 300));
|
log.info("{} {}", line, StringUtils.repeat("=", 300));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testJSON() {
|
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}}}";
|
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);
|
SDKAssistantMessage assistantMessage = JSON.parseObject(json, SDKAssistantMessage.class);
|
||||||
log.info("the assistantMessage: {}", assistantMessage);
|
log.info("the assistantMessage: {}", assistantMessage);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue